[华为云在线课程][MyBatis入门][一][介绍和简单用法][学习笔记]
1.MyBatis简单介绍
MyBatis官网地址:https://mybatis.org/mybatis-3/zh/index.html
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。
MyBatis支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。让开发者只关注SQL本身而不需要去编写注册驱动、创建connection、创建statement、手动设置参数、结果集检索等繁琐的代码。
MyBatis通过XML或注解的方式将要执行的各种statement(statement、preparedStatement、CallableStatement)进行配置,并通过Java对象和statement中的SQL进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL并将结果映射成Java对象并返回。MyBatis将Java方法与SQL语句关联,并没有将Java对象与数据库表关联。
1.1.JDBC的缺点及MyBatis解决方法
- 数据连接的创建、释放频繁造成系统资源浪费从而影响了性能,如果使用数据库连接池就可以解决这个问题。当然JDBC同样能够使用数据源。
解决方法:在SQLMapConfig.xml中配置数据连接池,使用数据库连接池管理数据库连接。 - SQL语句在写代码中不容易维护,事件需求中SQL变化的可能性很大,SQL变动需要改变Java代码。
解决方法:将SQL语句配置在mapper.xml文件中与Java代码分离。 - 向SQL语句传递参数麻烦,因为SQL语句的where条件不一定,可能多可能少,占位符需要和参数一一对应。
解决方法:MyBatis自动将Java对象映射到SQL语句。 - 对结果集解析麻烦,SQL变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成POJO对象解析比较方便。
解决方法:MyBatis自动将SQL执行结果映射到Java对象。
1.2.MyBatis架构介绍
- MyBatis配置:mybatis-config.xml,此文件作为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息。另一个mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在mybatis-config.xml中加载。
- 通过MyBatis环境等配置信息构造SqlSessionFactory即会话工厂。
- 由会话工厂创建SqlSession即会话,操作数据库需要通过SqlSession进行。
- MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器,一个是缓存执行器。
- Mapped Statement也是MyBatis一个底层封装对象,包装了MyBatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped Statement的id。
- Mapped Statement对sql执行输入参数进行定义,包括Map、List类型,基本数据类型,POJO类型,Executor通过Mapped Statement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是JDBC编程中对preparedStatement设置参数。
- Mapped Statement对sql执行输出结果进行定义,包括Map、List类型,基本数据类型,POJO类型,Executor通过Mapped Statement在执行sql后将输出结果映射至Java对象中,输出结果映射过程相当于JDBC编程中对结果的解析处理过程。
2.MyBatis基本用法
2.1.HelloWorld
首先准备一个数据库:
create database `mybatis01` default character set utf8mb4;
use `mybatis01`;
drop table if exists `mybatis01`;
create table `user`
(
`id` int(11) not null auto_increment,
`username` varchar(255),
`address` varchar(255),
primary key (`id`)
) engine = InnoDB
auto_increment = 8
default charset = utf8mb4;
insert into `user`(id, username, address)
values (1, 'aa', 'beijing'),
(2, 'bb', 'shanghai'),
(3, 'cc', 'guangzhou'),
(4, 'dd', 'shenzhen');
完成数据库创建后,创建一个Maven工程,添加MyBatis的相关依赖:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
</dependencies>
然后定义一个实体类User,
package org.example.mybatis01.model;
public class User {
private Integer id;
private String username;
private String address;
public User() {
}
public User(Integer id, String username, String address) {
this.id = id;
this.username = username;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}
接下来创建一个Mapper文件,用来在MyBatis中定义SQL的XML配置文件,首先取一个namespace,相当于一个分隔符,因为在项目中会存在多个Mapper,每个都会定义对应的增删改查方法,为了避免冲突和方便管理,每个Mapper都有自己的namespace,而且这个namespace不可重复。
现在在Mapper中定义一个简单的查询方法,根据id查询用户:
<!-- UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/schema/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mybatis01.mapper">
<select id="getUserById" resultType="org.example.mybatis01.model.User">
select * from user where id=#{id};
</select>
</mapper>
首先定义一个select,id表示查询方法的唯一标识符,resultType定义了返回值的类型。在select节点中,定义查询SQL,#{id},表示这个位置用来接收外部传进来的参数。
然后创建MyBatis配置文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis01?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
在这个配置文件中,只需要配置environments和mapper即可。
- environment就是MyBatis连接的数据库环境信息,它放在一个environments节点中,意味着environments中可以有多个environment,通常开发、测试、生产三个环境都单独配置一个environment,每个environment都有一个id,然后在environments中通过default属性,指定需要的environment。每个environment中定义一个数据的基本连接信息。
- 在mappers节点中,定义Mapper,也就是指定我们上一步所写的Mapper的路径。
接下来编写Main方法
package org.example.mybatis01;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.example.mybatis01.model.User;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sessionFactory.openSession();
User user = sqlSession.selectOne("org.example.mybatis01.mapper.getUserById", 3);
System.out.println(user);
sqlSession.close();
}
}
但是运行会报错,
Caused by: java.io.IOException: Could not find resource org/example/mybatis01/mapper/UserMapper.xml
at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:114)
at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:100)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:377)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120)
... 4 more
因为没有在Maven里面配置扫描路径,在pom.xml中进行添加。
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
配置后重新运行程序成功
package org.example.mybatis01;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.example.mybatis01.model.User;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sessionFactory.openSession();
User user = sqlSession.selectOne("org.example.mybatis01.mapper.getUserById", 3);
System.out.println(user); //User{id=3, username='cc', address='guangzhou'}
sqlSession.close();
}
}
2.2.SqlSessionFactory单例模式
- 从上面的启动类看出,加载主配置文件,生成一个SqlSessionFactory类,再由SqlSessionFactory生成一个SqlSession,SqlSession相当于我们的一个会话,类似于JDBC中的一个连接,在SQL操作完成后,这个会话可以关闭的。
- SqlSessionFactoryBuilder用于创建SqlSessionFactory,SqlSessionFactory一旦创建就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产,所以可将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
- SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
- SqlSession中封装了对数据库的操作,如增删改查等。通过SqlSessionFactory创建SqlSession,而SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建。SqlSession是一个面向用户的接口,SqlSession中定义了数据库操作,默认使用DefaultSqlSession实现类。每个线程都应该有自己的SqlSession实例。
- SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。打开一个SqlSession,使用完毕就要关闭它。通常把这个关闭操作放到finally块中以确保每次都能执行关闭。
现在对SqlSessionFactory进行封装
package org.example.mybatis01.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
public class SqlSessionFactoryUtils {
private static SqlSessionFactory SQL_SESSION_FACTORY = null;
public static SqlSessionFactory getInstance() {
if (SQL_SESSION_FACTORY == null) {
try {
SQL_SESSION_FACTORY = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
return SQL_SESSION_FACTORY;
}
}
在需要使用的时候,通过这个工厂方法来获取SqlSessionFactory的实例。
2.3.通过Log4j查看MyBatis运行日志
首先在pom.xml中导入log4j的相关依赖:
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha7</version>
</dependency>
</dependencies>
在resources编写log4j的配置文件log4j.properties(位置和文件名都必须是这个)
log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d[yyyy-MM-dd] method:%l%n%m%n
2.4.MyBatis增删改查
这里还以之前的数据库案例作为演示
2.4.1.增
添加记录,id有两种不同的处理方式,一种就是自增长,另一种则是Java代码传入id,这个id可以是UUID也可以是其他自定义id。MyBatis中对这两种方式都提供了相应的支持。
- 主键自增长
在Mapper中添加SQL插入语句:
<insert id="addUser" parameterType="org.example.mybatis01.model.User">
insert into user(username,address) values(#{username},#{address});
</insert>
parameterType表示传入的参数类型。参数都是通过#来引用。然后在Java代码中调用这个方法。
public class Main {
public static void main(String[] args) throws IOException {
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sessionFactory.openSession();
User user = new User();
user.setUsername("testUsername01");
user.setAddress("testAddress01");
int insert = sqlSession.insert("org.example.mybatis01.mapper.addUser", user);
System.out.println(insert);
sqlSession.commit();
sqlSession.close();
}
}
注意,插入完成后,一定要进行提交,sqlSession.commit()。
- 使用UUID主键
使用UUID作主键有两种方法,一种是在Java代码中生成UUID直接作为参数传入到SQL中,这种方式和普通参数传递一样,另一种方式是使用MySQL自带的UUID函数来生成UUID。
在这里使用MySQL自带的UUID函数,步骤:首先调用MySQL中的UUID函数,获取到一个UUID,然后将这个UUID赋值给User对象的ID属性,然后再执行插入操作,插入时使用UUID。注意,数据ID类型改为varchar。
<insert id="addUser2" parameterType="org.example.mybatis01.model.User">
<selectKey resultType="java.lang.String" keyProperty="id" order="BEFORE">
select uuid();
</selectKey>
insert into user(id,username,address) values(#{id},#{username},#{address});
</insert>
selectKey表示查询key。
keyProperty属性表示将查询的结果赋值给传递进来的User对象的id属性。
resultType表示查询结果的返回类型。
order表示这个查询操作的执行时机,BEFORE表示这个查询操作在insert之前执行。
在selectKey节点的外面定义insert操作。
package org.example;
import org.apache.ibatis.session.SqlSession;
import org.example.mybatis01.model.User;
import org.example.mybatis01.utils.SqlSessionFactoryUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class Test01 {
private SqlSession sqlSession;
@Before
public void before() {
sqlSession= SqlSessionFactoryUtils.getInstance().openSession();
}
@Test
public void addUser2() {
User user = new User();
user.setUsername("测试添加用户01");
user.setAddress("北京");
int insert = sqlSession.insert("org.example.mybatis01.mapper.addUser2", user);
System.out.println(insert);
sqlSession.commit();
}
@After
public void after() {
sqlSession.close();
}
}
2.4.2.删
删除操作比较容易,首先在UserMapper中定义删除SQL:
<delete id="deleteUserById" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
小坑:由于上例中将数据ID类型改为varchar后生成的UUID超出了int类型的长度,所以直接执行修改字段属性会报错,报错信息为:Incorrect integer value:‘e15b42ad-f8fe-11ec-b263-005056c00001’ for column ‘id’ at row 6。所以要先将UUID删除,然后修改成功,alter table user modify id int;
@Test
public void deleteUserById() {
int delete = sqlSession.delete("org.example.mybatis01.mapper.deleteUserById", 8);
System.out.println(delete);
sqlSession.commit();
}
SQL执行后返回数据库受影响的行数。
2.4.3.改
在UserMapper.xml中定义SQL:
<update id="updateUser" parameterType="org.example.mybatis01.model.User">
update user set username=#{username} where id=#{id}
</update>
然后在测试类中进行测试
@Test
public void updateUser() {
User user = new User();
user.setId(1);
user.setUsername("aaaa");
int update = sqlSession.update("org.example.mybatis01.mapper.updateUser", user);
System.out.println(update); // 1
sqlSession.commit();
}
调用返回值也是SQL受影响的行数。
2.4.4.查
要查询所有记录
<select id="getAllUser" resultType="org.example.mybatis01.model.User">
select * from user;
</select>
@Test
public void getAllUser() {
List<User> selectList = sqlSession.selectList("org.example.mybatis01.mapper.UserMapper.getAllUser");
System.out.println(selectList);
sqlSession.commit();
}
2.5.MyBatis引入Mapper
之前编写的增删改查存在冗余代码过多,模板化代码过多的问题。比如,每个方法都要获取SqlSession,有增删改操作的话,还要用到commit,SqlSession使用完毕后还要进行关闭,SqlSession执行时需要的参数就是方法的参数,SqlSession要执行的sql和xml中的定义是一一对应的。是一个模板化程度很高的代码。
既然模板化程度很高,可以将当前方法简化成一个接口:
package org.example.mybatis01.mapper;
import org.example.mybatis01.model.User;
import java.util.List;
public interface UserMapper {
User getUserById(Integer id);
Integer addUser(User user);
Integer addUser2(User user);
Integer deleteUserById(Integer id);
Integer updateUser(User user);
List<User> getAllUser();
}
这个接口对应的Mapper文件如下(UserMapper.xml和UserMapper需要放在同一个包下)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/schema/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mybatis01.mapper.UserMapper">
<!-- 根据userid进行查找-->
<select id="getUserById" resultType="org.example.mybatis01.model.User">
select * from user where id=#{id};
</select>
<!--增加用户-->
<insert id="addUser" parameterType="org.example.mybatis01.model.User">
insert into user(username,address) values(#{username},#{address});
</insert>
<!--增加用户,使用UUID-->
<insert id="addUser2" parameterType="org.example.mybatis01.model.User">
<selectKey resultType="java.lang.String" keyProperty="id" order="BEFORE">
select uuid();
</selectKey>
insert into user(id,username,address) values(#{id},#{username},#{address});
</insert>
<!--根据id删除用户-->
<delete id="deleteUserById" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
<!--修改用户信息-->
<update id="updateUser" parameterType="org.example.mybatis01.model.User">
update user set username=#{username} where id=#{id}
</update>
<!--查询用户信息-->
<select id="getAllUser" resultType="org.example.mybatis01.model.User">
select * from user;
</select>
</mapper>
这个接口可以替代之前编写的冗余代码,因为这个接口提供了Dao层所需的最核心的东西,根据这个接口可以自动生成Dao层的代码:
- 首先Dao定义了SqlSessionFactory,这是固定的代码。
- UserMapper所在的包+UserMapper类名+UserMapper中定义好的方法名,可以定位到要调用的sql。
- 要调用SqlSession中的哪个方法,根据定位到的sql节点就能确定。
因此在MyBatis开发中,实际上不需要自己提供Dao层的实现只需要提供一个UserMapper即可。然后在全局配置中,配置一下UserMapper:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis01?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="org.example.mybatis01.mapper"/>
</mappers>
</configuration>
然后加载配置文件获取UserMapper,并调用它里边的方法:
public class Main2 {
public static void main(String[] args) {
SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
SqlSession sqlSession = instance.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> allUser = mapper.getAllUser();
System.out.println(allUser);
}
}
注意,在Maven默认情况下要求我们将xml配置、properties配置等都放在resources目录下,如果我们强行放在Java目录下,默认情况下打包时配置文件会被自动忽略掉。对于这两个问题有两种解决方法:
- 不要忽略xml配置
可以在pom.xml中,添加如下配置,让Maven不要忽略在Java目录下的xml配置:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
- 按照Maven的要求来
按照Maven要求来,将xml文件放到resources目录下,但MyBatis默认情况要求UserMapper.xml和UserMapper接口必须放在一起,所以需要手动在resources目录下,创建一个和UserMapper接口相同的目录,这样就不需要在pom.xml文件中添加配置了,因为这种写法同时满足Maven和MyBatis的要求。
- 点赞
- 收藏
- 关注作者
评论(0)