avatar

Mybatis的使用

简介

1
Mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程;Mybatis可以通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,mybatis框架执行sql并将结果映射为java对象并返回;采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作

Mybatis官网

原始JDBC操作

数据库中的user表

列名 数据类型
id int
name int
birthday bigint

创建User实体类

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
public class User {

private Integer id;
private Integer name;
private Date birthday;

public User() {}

public User(Integer id, Integer name) {
this.id = id;
this.name = name;
}

public User(Integer id, Integer name, Date birthday) {
this.id = id;
this.name = name;
this.birthday = birthday;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public Integer getName() {
return name;
}

public void setName(Integer name) {
this.name = name;
}

public Date getBirthday() {
return birthday;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

@Override
public String toString() {
return "id=" + id + ", name=" + name + ", birthday=" + birthday;
}
}

查询数据

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
public static void main(String[] args) throws SQLException {

/*
获取连接
url : 数据库连接地址
user : 用户名
password : 密码
*/
Connection connection = DriverManager.getConnection("jdbc:mysql:///mysqltest?serverTimezone=UTC", "root", "root");

/* 获取statement */
PreparedStatement statement = connection.prepareStatement("select * from user");

/* 执行查询 */
ResultSet resultSet = statement.executeQuery();

/* 遍历结果集 */
while (resultSet.next()){

User user = new User();

user.setId(resultSet.getInt("id"));
user.setName(resultSet.getInt("name"));

System.out.println(user);

}

resultSet.close();
statement.close();
connection.close();

}

插入数据

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
public static void main(String[] args) throws SQLException, ClassNotFoundException {

/* 模拟实体对象 */
User user = new User();

user.setId(10);
user.setName(123);

/* 注册驱动 */
Class.forName("com.mysql.cj.jdbc.Driver");

/*
获取连接
url : 数据库连接地址
user : 用户名
password : 密码
*/
Connection connection = DriverManager.getConnection("jdbc:mysql:///mysqltest?serverTimezone=UTC", "root", "root");

/* 获取statement */
PreparedStatement statement = connection.prepareStatement("insert into user values (?, ?)");

/* 设置占位符参数 */
statement.setInt(1,user.getId());
statement.setInt(2,user.getName());

/* 执行更新操作 */
statement.executeUpdate();

/* 释放资源 */
statement.close();
connection.close();

}

使用原始jdbc开发存在的问题

1
2
3
数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码
查询操作时,需要手动将结果集中的数据手动封装到实体中;插入操作时,需要手动将实体的数据设置到sql语句的占位符位置

解决方法

1
2
3
使用数据库连接池初始化连接资源
将sql语句抽取到xml配置文件中
使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射

使用Mybatis操作

开发步骤

1
2
3
4
5
6
添加MyBatis的坐标
创建user数据表
编写User实体类
编写映射文件UserMapper.xml
编写核心文件SqlMapConfig.xml
编写测试类

导入要使用的坐标

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
<!--mybatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--mysql驱动坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<!--单元测试坐标-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--日志坐标-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>

编写UserMapper.xml映射文件,需要同实体类所在的包一致;如:如果实体类在java的com.example.demo下,那么mapper映射文件就应该在resources的com.example.demo下进行创建

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
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 映射文件DTD约束头 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--
根标签
namespace:命名空间,与下面语句的id一起组成查询的标识
-->
<mapper namespace="userMapper">

<!--
select: 查询标签
insert: 添加标签
update: 修改标签
delete: 删除标签
分别对应数据库中的增删改查
-->

<!--
查询所有语句
id: 语句的id标识,与上面的命名空间一起组成查询的标识
resultType: 返回结果封装成的实体类
User: 实体类别名
-->
<select id="findAll" resultType="User">
select * from user
</select>

<!--
添加数据
parameterType: 接收的数据类型
-->
<insert id="add" parameterType="com.example.demo.User">
insert into user values(#{id},#{name})
</insert>

<!-- 修改数据 -->
<update id="update" parameterType="com.example.demo.User">
update user set id = #{id} where name = #{name}
</update>

<!-- 删除数据 -->
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>

</mapper>

编写MyBatis核心文件(sqlMapConfig.xml),在resources下进行创建

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
<?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>

<!-- 可以加载额外配置的properties文件 -->
<properties resource="jdbc.properties"/>

<typeAliases>
<!--
设置类型别名: 为实体类定义别名
type: Mapper.xml中经常会使用到的实体类
alias: 定义别名
-->
<typeAlias type="com.example.demo.User" alias="User"/>
</typeAliases>

<!--
default: 指定默认的环境名称
-->
<environments default="development">

<!--
id: 指定当前的环境名称
-->
<environment id="development">

<!--
type: 指定事务管理类型
JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域
MANAGED: 从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期;默认情况下它会关闭连接,然而一些容器并不希望
这样,因此需要将closeConnection属性设置为 false来阻止它默认的关闭行为
-->
<transactionManager type="JDBC"/>

<!--
type: 指定当前数据源类型
UNPOOLED: 这个数据源的实现只是每次被请求时打开和关闭连接
POOLED: 这种数据源的实现利用“池”的概念将JDBC连接对象组织起来
JNDI: 这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用
-->
<dataSource type="POOLED">

<!--
数据源配置的基本参数
-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

<!-- 加载映射文件 -->
<mappers>
<!--
resource: 使用相对于类路径的资源引用
url: 使用完全限定资源定位符(URL)
class: 使用映射器接口实现类的完全限定类名
-->
<mapper resource="org/mybatis/example/BlogMapper.xml"/>

<!-- 将包内的映射器接口实现全部注册为映射器
<package name="org.mybatis.builder"/>
-->
</mappers>

</configuration>

编写测试类

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
@Test
public void contextLoads() throws IOException {

/* 加载核心配置文件 */
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

/* 获得sqlSession工厂对象 */
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

/* 获得sqlSession对象 */
SqlSession sqlSession = sqlSessionFactory.openSession();

/* 遍历sql语句 */
List<User> userList = sqlSession.selectList("userMapper.findAll");
System.out.println(userList);

/* 添加sql语句 */
int insert = sqlSession.insert("userMapper.add", new User(12,46));
System.out.println(insert);

/* 修改sql语句 */
int update = sqlSession.update("userMapper.update", new User(15, 123));
System.out.println(update);

/* 删除sql语句 */
int delete = sqlSession.delete("userMapper.delete", 9);
System.out.println(delete);

/* 提交事务 */
sqlSession.commit();

/* 释放资源 */
sqlSession.close();

}

MyBatis核心配置文件层级关系

MyBatis核心配置文件层级关系

MyBatis常用别名

别名 数据类型
string String
long Long
int Integer
double Double
boolean Boolean

MyBatis相应API

SqlSession工厂构建器SqlSessionFactoryBuilder

常用API:SqlSessionFactory build(InputStream inputStream)

通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象

1
2
3
4
5
6
7
String resource = "org/mybatis/builder/mybatis-config.xml";

InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

SqlSessionFactory factory = builder.build(inputStream);

Resources 工具类,这个类在 org.apache.ibatis.io 包中。Resources 类帮助你从类路径下、文件系统或一个web URL中加载资源文件

SqlSession工厂对象SqlSessionFactory

SqlSessionFactory常用的创建SqlSession实例的方法

方法 解释
openSession() 会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库中
openSession(boolean autoCommit) 参数为是否自动提交,如果设置为true,就不需要手动提交事务

SqlSession会话对象

SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法

1
2
3
4
5
6
执行语句的方法主要有:
<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

操作事务的方法主要有:

1
2
void commit()  
void rollback()

Mybatis的Dao层实现

传统开发方式

编写UserDao接口

1
2
3
public interface UserDao {
List<User> findAll() throws IOException;
}

编写UserDaoImpl实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public List<User> findAll() throws Exception {

InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

SqlSession sqlSession = sqlSessionFactory.openSession();

List<User> userList = sqlSession.selectList("userMapper.findAll");

sqlSession.close();

return userList;

}

测试代码

1
2
3
4
5
6
7
8
9
@Test
public void testTraditionDao() throws IOException {

UserDao userDao = new UserDaoImpl();

List<User> all = userDao.findAll();
System.out.println(all);

}

代理开发方式

Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法

Mapper 接口开发需要遵循的规范

1
2
3
4
Mapper.xml文件中的namespace与mapper接口的全限定名相同
Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

编写UserMapper接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 要求与映射文件的类名,包名一致 */
public interface UserMapper {

/*
User: 对应配置文件中的返回数据封装类型
findAll: 对应配置文件中的id名称
*/
List<User> findAll();

/*
User: 对应配置文件中的接收数据的类型
*/
Integer add(User user);

}

编写UserMapper配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.UserMapper">

<!-- resultType: 返回封装成的实体类 -->
<select id="findAll" resultType="com.example.demo.User">

select * from user

</select>

<!-- parameterType: 接收的数据类型 -->
<insert id="add" parameterType="com.example.demo.User">
insert into user values(#{id},#{name})
</insert>

</mapper>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void testProxyDao() throws Exception {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

//获得MyBatis框架生成的UserMapper接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

/* 遍历所有数据 */
List<User> users = userMapper.findAll();
System.out.println(users);

/* 添加数据 */
Integer add = userMapper.add(new User(123, 5));
System.out.println(add);

/* 手动提交 */
sqlSession.commit();

sqlSession.close();

}

MyBatis映射文件深入

动态sql语句

Mybatis 的映射文件中,有些时候业务逻辑复杂时,SQL是需要动态变化的,而之前的 SQL 就不能满足业务要求

if

UserMapper.xml配置文件: 根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如如果name不为空时要加入id作为条件

1
2
3
4
5
6
7
8
9
10
11
<select id="findByCondition" parameterType="user" resultType="user">
select * from User
<where>
<if test="id != 0 and id != null">
and id=#{id}
</if>
<if test="name != 0 and name!=null">
and name=#{name}
</if>
</where>
</select>

UserMapper

1
User findByCondition(User user);

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void findByCondition() throws Exception {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

/* 获得MyBatis框架生成的UserMapper接口的实现类 */
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

User user1 = new User();

user1.setName(123);

User user = userMapper.findByCondition(user1);

System.out.println(user);

}

foreach

配置文件: 循环执行sql的拼接操作;例如:select * from user where id in (1,2,5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="findByIds" parameterType="list" resultType="com.example.demo.User">
select * from user
<where>
<!--
foreach: 用于遍历集合
collection: 代表要遍历的集合元素,注意编写时不要写#{}
open: 代表语句的开始部分
close: 代表结束部分
item: 代表遍历集合的每个元素,生成的变量名
sperator: 代表分隔符
-->
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>

UserMapper

1
List<User> findByIds(List<Integer> list);

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test
public void findByIds() throws Exception {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

//获得MyBatis框架生成的UserMapper接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

ArrayList<Integer> list = new ArrayList();

list.add(1);
list.add(2);
list.add(3);

List<User> users = userMapper.findByIds(list);

System.out.println(users);

}

SQL片段抽取

可将映射文件中重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的

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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.UserMapper">

<!--抽取sql片段简化编写-->
<sql id="selectUser">select * from user</sql>

<!-- resultType: 返回封装成的实体类 -->
<select id="findAll" resultType="com.example.demo.User">

<include refid="selectUser"/>

</select>

<select id="findByCondition" parameterType="com.example.demo.User" resultType="com.example.demo.User">

<include refid="selectUser"/>

<!-- where条件 -->
<where>
<!-- if判断 -->
<if test="id != 0 and id != null">
and id=#{id}
</if>
<if test="name != 0 and name != null">
and name=#{name}
</if>
</where>
</select>

<select id="findByIds" parameterType="list" resultType="com.example.demo.User">

<include refid="selectUser"/>

<where>
<!-- 循环 -->
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>

</mapper>

MyBatis核心配置文件深入

typeHandlers标签

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)

类型处理器 Java类型 JDBC类型
BooleanTypeHandler java.lang.Boolean,boolean 数据库兼容的BOOLEAN
ByteTypeHandler java.lang.Byte,byte 数据库兼容的NUMERIC或BYTE
ShortTypeHandler java.lang.Short,short 数据库兼容的NUMERIC或SHORT INTEGER
IntegerTypeHandler java.lang.Integer,int 数据库兼容的NUMERIC或INTEGER
LongTypeHandler java.lang.Long,long 数据库兼容的NUMERIC或LONG INTEGER

可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口,或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler,然后可以选择性地将它映射到一个JDBC类型。例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换

数据表: 改变表,增加列birthday,数据类型为bigint

创建DateTypeHandler类

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
/* 继承BaseTypeHandler,指定泛型为Date;如:BaseTypeHandler<Date> */
public class DateTypeHandler extends BaseTypeHandler<Date> {

/* 将java类型转换成数据库需要的类型 */
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {

preparedStatement.setLong(i, date.getTime());

}

/*
将数据库中的类型转换成java类型
ResultSet: 查询出的结果集
String: 要转换的字段名称
*/
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {

/* 获取结果集中所需要的数据(Long)转换成Date类型返回 */
long aLong = resultSet.getLong(s);

return new Date(aLong);

}

/* 将数据库中的类型转换成java类型 */
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {

/* 获取结果集中所需要的数据(Long)转换成Date类型返回 */
long aLong = resultSet.getLong(i);

return new Date(aLong);

}

/* 将数据库中的类型转换成java类型 */
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {

/* 获取结果集中所需要的数据(Long)转换成Date类型返回 */
long aLong = callableStatement.getLong(i);

return new Date(aLong);

}
}

在Mybatis核心配置文件中注册自定义类型处理器

1
2
3
4
<!-- 注册自定义类型处理器,需要写在environments标签上面 -->
<typeHandlers>
<typeHandler handler="com.example.demo.DateTypeHandler"/>
</typeHandlers>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void save() throws Exception {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);

//获得MyBatis框架生成的UserMapper接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

userMapper.save(new User(154, 111, new Date()));

sqlSession.close();

}

数据库存储后的图

数据库表

plugins标签

MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

在pom.xml中导入通用PageHelper坐标

1
2
3
4
5
6
7
8
9
10
11
<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>

在Mybatis核心配置文件中配置PageHelper插件

1
2
3
4
5
6
7
<plugins>
<!-- 分页助手的插件,需要配置在typeHandlers下面,environments标签上面 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>

测试代码

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
@Test
public void testPageHelper() throws Exception {

/*
设置分页参数
pageNum: 当前页
pageSize: 每页显示的条数
*/
PageHelper.startPage(2,2);

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);

//获得MyBatis框架生成的UserMapper接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

List<User> userList = userMapper.findAll();

for (User user : userList) {

System.out.println(user);

}

/* 获取与分页相关的参数 */
PageInfo<User> pageInfo = new PageInfo<User>(userList);
System.out.println("上一页:"+pageInfo.getPrePage());
System.out.println("下一页:"+pageInfo.getNextPage());
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示长度:"+pageInfo.getPageSize());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());

sqlSession.close();

}

核心配置文件详解

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
<?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>

<!-- 可以加载额外配置的properties文件 -->
<properties resource="jdbc.properties"/>

<!-- 设置类型别名 -->
<typeAliases>
<!--
设置类型别名: 为实体类定义别名
type: Mapper.xml中经常会使用到的实体类
alias: 定义别名
-->
<typeAlias type="com.example.demo.User" alias="User"/>
</typeAliases>

<!-- 配置自定义类型处理器 -->
<typeHandlers>
<!-- 注册自定义类型处理器 -->
<typeHandler handler="com.example.demo.DateTypeHandler"/>
</typeHandlers>

<!-- 配置MyBatis的插件 -->
<plugins>
<!-- 分页助手的插件,需要配置在通用mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>

<!--
environments: 数据源环境配置标签
default: 指定默认的环境名称
-->
<environments default="development">

<!-- id: 指定当前的环境名称 -->
<environment id="development">

<!--
type: 指定事务管理类型
JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域
MANAGED: 从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期;默认情况下它会关闭连接,然而一些容器并不希望
这样,因此需要将closeConnection属性设置为 false来阻止它默认的关闭行为
-->
<transactionManager type="JDBC"/>

<!--
type: 指定当前数据源类型
UNPOOLED: 这个数据源的实现只是每次被请求时打开和关闭连接
POOLED: 这种数据源的实现利用“池”的概念将JDBC连接对象组织起来
JNDI: 这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用
-->
<dataSource type="POOLED">

<!--
数据源配置的基本参数
${}: 获取jdbc.properties配置文件中的数据
-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

<!-- 加载映射文件 -->
<mappers>
<!--
resource: 使用相对于类路径的资源引用
url: 使用完全限定资源定位符(URL)
class: 使用映射器接口实现类的完全限定类名
-->
<mapper resource="org/mybatis/example/BlogMapper.xml"/>

<!-- 将包内的映射器接口实现全部注册为映射器
<package name="org.mybatis.builder"/>
-->
</mappers>

</configuration>

多表操作

数据库中的orders表

列名 数据类型
id int
orderTime varchar
total double

一对一

创建Order实体类

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
public class Order {

private Integer id;
private Date orderTime;
private Double total;

/* 当前订单属于哪个用户 */
private User user;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public Date getOrderTime() {
return orderTime;
}

public void setOrderTime(Date orderTime) {
this.orderTime = orderTime;
}

public Double getTotal() {
return total;
}

public void setTotal(Double total) {
this.total = total;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public Order() {
}

public Order(Integer id, Date orderTime, Double total, User user) {
this.id = id;
this.orderTime = orderTime;
this.total = total;
this.user = user;
}

@Override
public String toString() {
return "Order{" +
"id=" + id +
", orderTime=" + orderTime +
", total=" + total +
", user{" + user +
"}}";
}
}

创建OrderMapper接口

1
2
3
4
5
public interface OrderMapper {

List<Order> findAll();

}

创建OrderMapper.xml配置文件

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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.OrderMapper">

<!-- 手动指定字段与实体属性的映射关系 -->
<resultMap id="orderMap" type="com.example.demo.Order">
<!--
column: 数据库的字段名称
property: 实体类的属性名称
-->
<id column="id" property="id"/>
<result column="orderTime" property="orderTime"/>
<result column="total" property="total"/>
<!-- 以下封装方式任意开启一种即可,也可都开启 -->
<!--
封装方式一
注: 两个类的column不能相同,否则返回的数据会被主实体类的数据代替,无法得到主实体类中引入的实体类数据
-->
<result column="uid" property="user.id"/>
<result column="name" property="user.name"/>
<result column="birthday" property="user.birthday"/>
<!--
封装方式二
property: 当前实体类中的属性名称
javaType: 当前实体类中的属性类型
-->
<!-- <association property="user" javaType="com.example.demo.User">
<id column="uid" property="id"/>
<result column="name" property="name"/>
<result column="birthday" property="birthday"/>
</association> -->
</resultMap>

<select id="findAll" resultMap="orderMap">
select *, u.id uid from orders o, user u where o.uid = u.id
</select>

</mapper>

在Mybatis核心配置文件中的mappers下引入映射文件

1
<mapper resource="com/example/demo/OrderMapper.xml"/>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void orderTest() throws IOException {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);

OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

List<Order> orderList = orderMapper.findAll();

for (Order order : orderList) {

System.out.println(order);

}

sqlSession.close();

}

一对多

User实体类

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
public class User {

private List<Order> orderList;

public User(Integer id, Integer name, Date birthday, List<Order> orderList) {
this.id = id;
this.name = name;
this.birthday = birthday;
this.orderList = orderList;
}

public List<Order> getOrder() {
return orderList;
}

public void setOrder(List<Order> orderList) {
this.orderList = orderList;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name=" + name +
", birthday=" + birthday +
", orderList{" + orderList +
"}}";
}
}

UserMapper接口

1
List<User> findAllUserAndOrder();

UserMapper.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<resultMap id="userMap" type="com.example.demo.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="birthday" property="birthday"/>
<!--
配置集合信息
property: 集合名称
ofType: 集合中的数据类型
-->
<collection property="orderList" ofType="com.example.demo.Order">
<!-- 封装Order数据 -->
<id column="oid" property="id"/>
<result column="orderTime" property="orderTime"/>
<result column="total" property="total"/>
<result column="uid" property="uid"/>
</collection>
</resultMap>

<select id="findAllUserAndOrder" resultMap="userMap">
select *, o.id oid from user u, orders o where u.id = o.uid
</select>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void userTest() throws IOException {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

List<User> userList = userMapper.findAllUserAndOrder();

for (User user : userList) {

System.out.println(user);

}

sqlSession.close();

}

多对多

数据库中的role表

列名 数据类型
id int
name varchar

数据库中的user_role表

列名 数据类型
userId int
roleId int

数据库中的role表

列名 数据类型
id int
rname varchar

创建Role实体类

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
public class Role {

private Integer id;
private String name;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Role() {
}

public Role(Integer id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

User实体类

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
private List<Role> roleList;

public User(Integer id, Integer name, Date birthday, List<Role> roleList) {
this.id = id;
this.name = name;
this.birthday = birthday;
this.roleList = roleList;
}

public List<Role> getRoleList() {
return roleList;
}

public void setRoleList(List<Role> roleList) {
this.roleList = roleList;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name=" + name +
", birthday=" + birthday +
", roleList{" + roleList +
"}}";
}

UserMapper接口

1
List<User> findAllUserAndRole();

UserMapper.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap id="userAndRoleMap" type="com.example.demo.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="birthday" property="birthday"/>
<collection property="roleList" ofType="com.example.demo.Role">
<id column="roleId" property="id"/>
<id column="rname" property="name"/>
</collection>
</resultMap>

<select id="findAllUserAndRole" resultMap="userAndRoleMap">
select * from user u, user_role ur, role r where u.id = ur.userId and r.id = ur.roleId
</select>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void userAndRoleTest() throws IOException {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

List<User> userList = userMapper.findAllUserAndRole();

for (User user : userList) {

System.out.println(user);

}

sqlSession.close();

}

注解开发

新建一个项目,导入实体类,DateTypeHandler,mapper接口,sqlMapConfig.xml核心配置文件,若数据源环境使用了配置文件,再将jdbc.properties配置文件导入

Mybatis常用注解

注解 作用 说明
@Insert 新增
@Update 更新
@Delete 删除
@Select 查询
@Result 结果集封装 代替id和result标签,属性:column:数据库的列名,property:需要装配的属性名,one:需要使用@One注解,many:需要使用@Many注解
@Results 封装多个结果集,与@Result一起使用 代替resultMap标签,使用格式@Results({@Result(), @Result()})或@Results(@Result())
@One 一对一结果集封装 代替association标签,使用格式:@Result(column=””, property=””,one=@One(select=””)),select:指定用来多表查询的mapper
@Many 一对多结果集封装 代替collection标签,使用格式:@Result(column=””, property=””, many=@Many(select=””))

使用注解进行单表的增删改查

sqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
<!-- 注册自定义类型处理器 -->
<typeHandlers>
<!-- 替换handler属性所指向的地址 -->
<typeHandler handler="com.example.test.DateTypeHandler"/>
</typeHandlers>
<!-- 加载映射文件 -->
<mappers>
<!-- 使用包加载,将其指向mapper接口的所在位置 -->
<package name="com.example.test"/>
</mappers>

UserMapper接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface UserMapper {

@Insert("insert into user values(#{id},#{name},#{birthday})")
void save(User user);

@Update("update user set name = #{name} where id = #{id}")
void update(User user);

@Delete("delete from user where id = #{id}")
void delete(Integer id);

@Select("select * from user where id = #{id}")
User findById(Integer id);

@Select("select * from user")
List<User> findAll();

}

测试代码

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
public class AnnotationTest {

private UserMapper userMapper;
private SqlSession sqlSession;

/* 加了该注解方法会在加了Test注解的方法前执行 */
@Before
public void before() throws IOException {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession(true);
userMapper = sqlSession.getMapper(UserMapper.class);

}

@Test
public void testSave(){

userMapper.save(new User(1231,1231, new Date()));

sqlSession.close();

}

@Test
public void testUpdate(){

userMapper.update(new User(1,75));

sqlSession.close();

}

@Test
public void testDelete(){

userMapper.delete(5);

sqlSession.close();

}

@Test
public void testFindById(){

System.out.println(userMapper.findById(1));

sqlSession.close();

}

@Test
public void testFindAll(){

List<User> userList = userMapper.findAll();

for (User user : userList) {

System.out.println(user);

}

sqlSession.close();

}
}

一对一注解开发

OrderMapper接口

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
/* 方式一 */
@Select("select *, u.id uid from orders o, user u where o.uid = u.id")
@Results({
@Result(column = "id", property = "id"),
@Result(column = "orderTime", property = "orderTime"),
@Result(column = "total", property = "total"),
@Result(column = "uid", property = "user.id"),
@Result(column = "name", property = "user.name"),
@Result(column = "birthday", property = "user.birthday")
})
List<Order> findAll();

/* 方式二 */
@Select("select * from orders")
@Results({
@Result(column = "id", property = "id"),
@Result(column = "orderTime", property = "orderTime"),
@Result(column = "total", property = "total"),
@Result(
/* 根据指定字段去查询表中数据 */
column = "uid",
/* 要封装的属性名称 */
property = "user",
/* 要封装的实体类型 */
javaType = User.class,
/* select属性: 使用指定接口下的方法获得数据 */
one = @One(select = "com.example.test.UserMapper.findById")
)
})
List<Order> findAll();

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testFindAllOrder() throws IOException {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

List<Order> orderList = orderMapper.findAll();

for (Order order : orderList) {

System.out.println(order);

}

sqlSession.close();

}

一对多注解开发

OrderMapper接口

1
2
@Select("select * from orders where uid = #{uid}")
List<Order> findById(Integer uid);

UserMapper接口

1
2
3
4
5
6
7
8
9
10
11
12
13
@Select("select * from user")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "birthday", property = "birthday"),
@Result(
column = "id",
javaType = List.class,
property = "orderList",
many = @Many(select = "com.example.test.OrderMapper.findById")
)
})
List<User> findAllUserAndOrder();

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testFindAllUserAndOrder() throws IOException {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

List<User> userList = userMapper.findAllUserAndOrder();

for (User user : userList) {

System.out.println(user);

}

sqlSession.close();

}

多对多注解开发

RoleMapper接口

1
2
@Select("select * from role r, user_role ur where ur.roleId = r.id and ur.userId = #{uid}")
List<Role> findById(Integer uid);

UserMapper接口

1
2
3
4
5
6
7
8
9
10
11
12
13
@Select("select * from user")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "birthday", property = "birthday"),
@Result(
column = "id",
javaType = List.class,
property = "roleList",
many = @Many(select = "com.example.test.RoleMapper.findById")
)
})
List<User> findAllUserAndRole();

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testFindAllUserAndRole() throws IOException {

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

List<User> userList = userMapper.findAllUserAndRole();

for (User user : userList) {

System.out.println(user);

}

sqlSession.close();

}
文章作者: 123
文章链接: https://gao5805123.github.io/123/2020/05/20/Mybatis%E7%9A%84%E4%BD%BF%E7%94%A8/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 123
打赏
  • 微信
    微信
  • 支付宝
    支付宝