MyBatis核心之实践初体验

MyBatis核心之实践初体验

一、MyBatis搭建开发环境

首先我们需要在Maven项目中引入依赖坐标,包括MyBatis核心依赖、Junit测试依赖、MySQL数据库依赖以及日志log4j依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>

然后,我们需要引入日志配置文件log4j.xml,可以方便我们观察MyBatis的运行时信息,帮助我们理解MyBatis的运行原理,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>

其次,我们需要编写MyBatis的核心配置文件,这是MyBatis实践中最重要的一环,其大致内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--数据源连接的配置信息-->
<properties resource="jdbc.properties"/>
<typeAliases>
<!--实体类所在的包-->
<package name="com.huling.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--需要跟Mapper接口所在的包保持一致-->
<package name="com.huling.mapper"/>
</mappers>
</configuration>

上述配置文件中引入了数据源连接的配置文件jdbc.properties,其内容如下:

1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
jdbc.username=root
jdbc.password=root

接着,我们还需要建立项目的源代码目录结构如实体类包com.huling.pojo、数据持久层包com.huling.mapper等,整体项目的结构如下图:

image-20231124152126118

其中,实体类User关联数据库中的t_user表,对象属性与数据表字段一一对应:

image-20231124152306799

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.huling.pojo;

public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String sex;
private String email;

public User() {
}

public User(Integer id, String username, String password, Integer age, String sex, String email) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.sex = sex;
this.email = email;
}

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 getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
'}';
}
}

最后,我们需要建立数据持久层的接口UserMapper及其对应的XML映射文件UserMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.huling.mapper;

import com.huling.pojo.User;

public interface UserMapper {
int insertSimpleUser();

int updateSimpleUser();

int deleteSimpleUser();

User getSimpleUser();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huling.mapper.UserMapper">
<insert id="insertSimpleUser">
insert into t_user values (null,'tom','123456',22,'男','tom@163.com')
</insert>

<update id="updateSimpleUser">
update t_user set username = 'admin' where id = 3
</update>

<delete id="deleteSimpleUser">
delete from t_user where id = 3
</delete>

<select id="getSimpleUser" resultType="user">
select * from t_user where id = 2
</select>
</mapper>

二、MyBatis搭建测试环境

通过单元测试Junit技术,我们可以方便地测试MyBatis的各项功能,下面是一个简单的测试程序代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MyBatisTest {

/**
* 测试MyBatis的简单功能
*/
@Test
public void test1() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession(true)) {
UserMapper mapper = session.getMapper(UserMapper.class);
// 添加简单用户
// int insertResult = mapper.insertSimpleUser();
// System.out.println("insertResult = " + insertResult);
// 修改简单用户
// int updateResult = mapper.updateSimpleUser();
// System.out.println("updateResult = " + updateResult);
// 删除简单用户
// int deleteResult = mapper.deleteSimpleUser();
// System.out.println("deleteResult = " + deleteResult);
// 查询简单用户
User user = mapper.getSimpleUser();
System.out.println("user = " + user);
}
}

}

image-20231124152941645

对象属性与数据库的表字段直接如何映射转换的呢,我们先做一个实验,给实体类User的构造方法和Getter、Setter方法添加打印信息,看一看会发生什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.huling.pojo;

public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String sex;
private String email;

public User() {
System.out.println("User.User无参构造");
}

public User(Integer id, String username, String password, Integer age, String sex, String email) {
System.out.println("User.User有参构造");
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.sex = sex;
this.email = email;
}

public Integer getId() {
System.out.println("User.getId");
return id;
}

public void setId(Integer id) {
System.out.println("User.setId:" + id);
this.id = id;
}

public String getUsername() {
System.out.println("User.getUsername");
return username;
}

public void setUsername(String username) {
System.out.println("User.setUsername:" + username);
this.username = username;
}

public String getPassword() {
System.out.println("User.getPassword");
return password;
}

public void setPassword(String password) {
System.out.println("User.setPassword:" + password);
this.password = password;
}

public Integer getAge() {
System.out.println("User.getAge");
return age;
}

public void setAge(Integer age) {
System.out.println("User.setAge:" + age);
this.age = age;
}

public String getSex() {
System.out.println("User.getSex");
return sex;
}

public void setSex(String sex) {
System.out.println("User.setSex:" + sex);
this.sex = sex;
}

public String getEmail() {
System.out.println("User.getEmail");
return email;
}

public void setEmail(String email) {
System.out.println("User.setEmail:" + email);
this.email = email;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
'}';
}
}

image-20231124154251654

因此可以发现,默认从查询的行记录转换为Java对象的情况下,对于对象属性与表字段一致时,直接通过对象的Setter方法注入属性值,而对象也是先通过无参构造函数创建的实例。

其实与上面的情况相对应,当我们的方法参数中包含Java对象时,也需要通过Getter方法取出对象的属性值,下面添加一个新的Mapper测试接口方法,观察一下会发生什么?

1
2
3
4
5
6
7
8
9
10
// UserMapper接口引入的测试方法
int insertUser(User user);

// UserMapper.xml的SQL语句实现
<insert id="insertUser">
insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
</insert>

// MyBatisTest测试程序引入的测试代码
int result = mapper.insertUser(new User(null,"test","test",12,"女","mail"));

image-20231124155844580