MyBatis是一款优秀的开源持久层框架,支持自定义SQL查询、存储过程和高级映射。

在MyBatis中,我们可以在XML中编写SQL语句,然后绑定到Java方法中,通过参数和结果集的自动映射来实现复杂的查询逻辑。MyBatis消除了几乎所有JDBC操作和手动绑定参数操作,使用起来非常方便!

在SpringBoot中集成MyBatis

  1. pom.xml中添加MyBatis提供的Spring Boot Starter;
1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-starter.version}</version>
</dependency>
  1. application.yml中配置好编写SQL实现的xml文件路径,这里我们存放在resources/dao目录下;
1
2
3
mybatis:
mapper-locations:
- classpath:dao/*.xml
  1. 添加Java配置,通过@MapperScan配置好Dao接口路径,这样就可以开始使用了。
1
2
3
4
@Configuration
@MapperScan("com.macro.mall.tiny.dao")
public class MyBatisConfig {
}

基本使用

基本使用我已经很熟悉,不多赘述。

这里单独强调一点:如果数据库中表单的命名规则和Java中实体类的命名规则有差异,需要对其名称进行映射,否则可能导致某个字段的数据无法正确注入。

比如,对于一些数据库表中以下划线分割的返回字段无法自动映射,可以通过对字段取别名的方式来进行映射;

1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="selectById" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
where id = #{id}
</select>

或者也可以通过在application.yml中开启全局下划线自动转驼峰功能来解决。

1
2
3
4
mybatis:
configuration:
# 下划线自动转驼峰
map-underscore-to-camel-case: true

动态SQL

通过MyBatis的动态SQL功能,比如条件筛选搜索功能。我们可以灵活地在xml中实现各种复杂的操作,动态SQL功能需要依赖MyBatis的各种标签。

if

  • if标签可以实现判断逻辑,这里我们以根据用户名和Email模糊查询用户为例,来聊聊它的使用;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="selectByUsernameAndEmailLike" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
where 1=1
<if test="username!=null and username!=''">
and username like concat('%',#{username},'%')
</if>
<if test="email!=null and email!=''">
and email like concat('%',#{email},'%')
</if>
</select>

这里的where 1=1是为了保证SQL语句语法的正确性。

choose

  • choose标签也可以实现判断逻辑,上面的例子中当我们不输入用户名和Email时,会查询出全部用户,我们如果想不查询出用户,可以使用它;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<select id="selectByUsernameAndEmailLike2" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
where 1=1
<choose>
<when test="username!=null and username!=''">
and username like concat('%',#{username},'%')
</when>
<when test="email!=null and email!=''">
and email like concat('%',#{email},'%')
</when>
<otherwise>
and 1=2
</otherwise>
</choose>
</select>

where

  • 上面的例子中我们为了SQL拼接不出错,添加了where 1=1这样的语句,其实可以使用where标签来实现查询条件,当标签内没有内容时会自动去除where关键字,同时还会去除开头多余的and关键字。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<select id="selectByUsernameAndEmailLike3" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
<where>
<if test="username!=null and username!=''">
and username like concat('%',#{username},'%')
</if>
<if test="email!=null and email!=''">
and email like concat('%',#{email},'%')
</if>
</where>
</select>

set

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
<update id="updateByIdSelective">
update ums_admin
<set>
<if test="username!=null and username!=''">
username = #{username},
</if>
<if test="password!=null and password!=''">
password = #{password},
</if>
<if test="icon!=null and icon!=''">
icon = #{icon},
</if>
<if test="email!=null and email!=''">
email = #{email},
</if>
<if test="nickName!=null and nickName!=''">
nick_name = #{nickName},
</if>
<if test="note!=null and note!=''">
note = #{note},
</if>
<if test="createTime!=null">
create_time = #{createTime},
</if>
<if test="loginTime!=null">
login_time = #{loginTime},
</if>
</set>
where id = #{id}
</update>

foreach

  • 通过foreach我们可以实现一些循环拼接SQL的逻辑,例如我们现在需要编写一个批量插入用户的方法;
1
2
3
4
5
6
<insert id="insertBatch">
insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time) values
<foreach collection="entityList" separator="," item="item">
(#{item.username}, #{item.password}, #{item.icon}, #{item.email}, #{item.nickName}, #{item.note}, #{item.createTime}, #{item.loginTime})
</foreach>
</insert>

高级映射

需要进行一对一的联表查询时,可以使用下面的方式查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="selectResourceWithCategory" resultType="com.macro.mall.tiny.domain.UmsResourceExt">
select ur.id,
ur.create_time as createTime,
ur.name,
ur.url,
ur.description,
ur.category_id as categoryId,
urc.id as "category.id",
urc.name as "category.name",
urc.sort as "category.sort",
urc.create_time as "category.createTime"
from ums_resource ur
left join ums_resource_category urc on ur.category_id = urc.id
where ur.id = #{id}
</select>

需要进行一对多的联表查询时,需要先在xml中配置一个ResultMap,通过collection标签建立一对多关系;

在此之前我们在编写xml文件的时候,一般习惯于先给当前文件写一个BaseResultMap,用于对当前表的字段和对象属性进行直接映射,例如在UmsResourceCategoryDao.xml中这样实现;

1
2
3
4
5
6
7
8
9
10
11
<?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.macro.mall.tiny.dao.UmsResourceCategoryDao">

<resultMap id="BaseResultMap" type="com.macro.mall.tiny.model.UmsResourceCategory">
<id property="id" column="id"/>
<result property="createTime" column="create_time"/>
<result property="name" column="name"/>
<result property="sort" column="sort"/>
</resultMap>
</mapper>
1
2
3
<resultMap id="selectCategoryWithResourceMap" type="com.macro.mall.tiny.domain.UmsResourceCategoryExt" extends="BaseResultMap">
<collection property="resourceList" columnPrefix="resource_" resultMap="com.macro.mall.tiny.dao.UmsResourceDao.BaseResultMap"/>
</resultMap>

然后在xml中编写具体的SQL实现,使用该ResultMap。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="selectCategoryWithResource" resultMap="selectCategoryWithResourceMap">
select urc.id,
urc.create_time,
urc.name,
urc.sort,
ur.id resource_id,
ur.create_time resource_create_time,
ur.name resource_name,
ur.url resource_url,
ur.description resource_description,
ur.category_id resource_category_id
from ums_resource_category urc
left join ums_resource ur on urc.id = ur.category_id
where urc.id = #{id}
</select>

分页插件PageHelper

  • 我们平时实现查询逻辑时,往往还会遇到分页查询的需求,直接使用开源的PageHelper插件即可,首先在pom.xml中添加它的Starter;
1
2
3
4
5
6
<!--MyBatis分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper-starter.version}</version>
</dependency>
  • 然后在查询方法之前使用它的startPage方法传入分页参数即可,分页后的得到的数据可以在PageInfo中获取到。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class UmsResourceServiceImpl implements UmsResourceService {

@Autowired
private UmsResourceDao umsResourceDao;

@Override
public PageInfo<UmsResource> page(Integer pageNum, Integer pageSize,Long categoryId) {
PageHelper.startPage(pageNum,pageSize);
List<UmsResource> resourceList = umsResourceDao.selectListByCategoryId(categoryId);
PageInfo<UmsResource> pageInfo = new PageInfo<>(resourceList);
return pageInfo;
}
}

MyBatis Generator(MBG)的用法

MyBatis Generator(简称MBG)是MyBatis官方提供的代码生成工具。可以通过数据库表直接生成实体类、单表CRUD代码、mapper.xml文件,从而减少开发者手动进行的大量重复性的编码。

基本使用

  1. pom.xml中添加如下依赖,主要添加了MyBatis、PageHelper、Druid、MBG和MySQL驱动等依赖;
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
<dependencies>
<!--SpringBoot整合MyBatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-starter.version}</version>
</dependency>
<!--MyBatis分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper-starter.version}</version>
</dependency>
<!--集成druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- MyBatis 生成器 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>${mybatis-generator.version}</version>
</dependency>
<!--Mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
</dependency>
</dependencies>
  1. application.yml中对数据源和MyBatis的mapper.xml文件路径进行配置,这里做个约定,MBG生成的放在resources/com/**/mapper目录下,自定义的放在resources/dao目录下;
1
2
3
4
5
6
7
8
9
10
11
12
# 数据源配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root

# MyBatis mapper.xml路径配置
mybatis:
mapper-locations:
- classpath:dao/*.xml
- classpath*:com/**/mapper/*.xml
  1. 添加Java配置,用于扫码Mapper接口路径,这里还有个约定,MBG生成的放在mapper包下,自定义的放在dao包下。
1
2
3
4
@Configuration
@MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"})
public class MyBatisConfig {
}
  1. 使用代码生成器
    在使用MBG生成代码前,我们还需要对其进行一些配置,首先在generator.properties文件中配置好数据库连接信息;
1
2
3
4
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://localhost:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.userId=root
jdbc.password=root

然后在generatorConfig.xml文件中对MBG进行配置,配置属性说明直接参考注释即可;

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
<properties resource="generator.properties"/>
<context id="MySqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
<!-- 配置SQL语句中的前置分隔符 -->
<property name="beginningDelimiter" value="`"/>
<!-- 配置SQL语句中的后置分隔符 -->
<property name="endingDelimiter" value="`"/>
<!-- 配置生成Java文件的编码 -->
<property name="javaFileEncoding" value="UTF-8"/>
<!--生成mapper.xml时覆盖原文件-->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
<!-- 为模型生成序列化方法-->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- 为生成的Java模型创建一个toString方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<!--可以自定义生成model的代码注释-->
<commentGenerator type="com.macro.mall.tiny.mbg.CommentGenerator">
<!-- 是否阻止生成的注释 -->
<property name="suppressAllComments" value="true"/>
<!-- 是否阻止生成的注释包含时间戳 -->
<property name="suppressDate" value="true"/>
<!-- 是否添加数据库表的备注信息 -->
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!-- 配置MBG要连接的数据库信息 -->
<jdbcConnection driverClass="${jdbc.driverClass}"
connectionURL="${jdbc.connectionURL}"
userId="${jdbc.userId}"
password="${jdbc.password}">
<!-- 解决mysql驱动升级到8.0后不生成指定数据库代码的问题 -->
<property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>
<!-- 用于控制实体类的生成 -->
<javaModelGenerator targetPackage="com.macro.mall.tiny.mbg.model" targetProject="mall-tiny-generator\src\main\java"/>
<!-- 用于控制Mapper.xml文件的生成 -->
<sqlMapGenerator targetPackage="com.macro.mall.tiny.mbg.mapper" targetProject="mall-tiny-generator\src\main\resources"/>
<!-- 用于控制Mapper接口的生成 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.macro.mall.tiny.mbg.mapper"
targetProject="mall-tiny-generator\src\main\java"/>
<!--生成全部表tableName设为%-->
<table tableName="ums_admin">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
<table tableName="ums_role">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
<table tableName="ums_admin_role_relation">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
<table tableName="ums_resource">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
<table tableName="ums_resource_category">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>

这里值得一提的是targetRuntime这个属性,设置不同的属性生成的代码和生成代码的使用方式会有所不同,常用的有MyBatis3MyBatis3DynamicSql两种,这里使用的是MyBatis3

如果你想自定义MBG生成的代码的话,可以自己写一个CommentGenerator来继承DefaultCommentGenerator

最后我们写个Generator类用于生成代码,直接运行main方法即可生成所有代码;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Generator {
public static void main(String[] args) throws Exception {
//MBG 执行过程中的警告信息
List<String> warnings = new ArrayList<String>();
//当生成的代码重复时,覆盖原代码
boolean overwrite = true;
//读取我们的 MBG 配置文件
InputStream is = Generator.class.getResourceAsStream("/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(is);
is.close();

DefaultShellCallback callback = new DefaultShellCallback(overwrite);
//创建 MBG
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
//执行生成代码
myBatisGenerator.generate(null);
//输出警告信息
for (String warning : warnings) {
System.out.println(warning);
}
}
}

MyBatis官方代码生成器MBG还是很强大的,可以生成一些常用的单表CRUD方法,减少了我们的工作量。但是对于子查询、多表查询和一些复杂查询支持有点偏弱,依然需要在mapper.xml中手写SQL实现。