Spring Boot集成MyBatis-Plus持久层框架实现数据库连接,支持自定义增删改查、条件构造器、分页插件、代码生成。

简介 | MyBatis-Plus (baomidou.com)

1、快速整合MyBatis-Plus

第一步,在 pom.xml 中引入 starter。

1
2
3
4
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>

第二步,使用 @MapperScan 注解扫描 mapper 文件。

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@ComponentScan("com.github.paicoding.forum.service")
@MapperScan(basePackages = {
"com.github.paicoding.forum.service.article.repository.mapper",
"com.github.paicoding.forum.service.user.repository.mapper",
"com.github.paicoding.forum.service.comment.repository.mapper",
"com.github.paicoding.forum.service.config.repository.mapper",
"com.github.paicoding.forum.service.statistics.repository.mappe
"com.github.paicoding.forum.service.notify.repository.mapper",}
public class ServiceAutoConfig {
}

ServiceAutoConfig 是单独的配置类,mapper 接口按照业务进行了分类,mapper.xml 放在 resources 目录下。

第三步,在 application.yml 文件中增加MyBatis-Plus 的统一配置。

1
2
3
4
5
6
# mybatis 相关统一配置
mybatis-plus:
configuration:
#开启下划线转驼峰
map-underscore-to-camel-case: true

将数据库表中的下划线命名方式 (underscore case)映射为 Java 对象中的驼峰命名方式(camel case)。例如,数据库 表中的列名为 user_name,对应的 Java 对象的属性名为 userName。

2、MyBatis-Plus的基本使用

2.1、Service CRUD

示例:比如说我们要保存一个文章的标签。

1
2
3
@Autowired
private TagDao tagDao;
tagDao.save(tagDO);

tagDao 是我们定义的数据访问对象(Data Access Object,简称 DAO),它继承自 MyBatis-Plus 提供的 ServiceImpl 类。 @Autowired 注解将 TagDao 自动注入到当前类 中。这是 Spring 提供的依赖注入(DI)功能,可以让我们在当前类中方便地使用 TagDao。

1
2
3
@Repository
public class TagDao extends ServiceImpl<TagMapper, TagDO> {
}

@Repository 注解:这是 Spring 提供的注解,用于标识这个类是一个数据访问层 (DAO)组件。Spring 会自动扫描并将其实例化为一个 Bean,方便在其他类中通过依赖 注入(DI)使用。

ServiceImpl 是 MyBatis-Plus 提供的一个抽象类,提供了通用的 CRUD 方法。泛型参数 意味着 TagDao 类 主要用于处理 TagDO 数据对象的数据库操作,并使用 TagMapper 接口定义的方法进行操作。

通过继承 ServiceImpl 类,TagDao 就可以使用 MyBatis-Plus 提供的通用 CRUD 方法,如 save、getById、updateById 等。这些方法已经实现了基本的数据库操作,通常无需自 己编写 SQL 语句。

参数 tagDO 是一个数据对象(Data Object,简称 DO),表示数据库中的 tag 表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tag")
public class TagDO extends BaseDO {
private static final long serialVersionUID = 3796460143933607644L;
/**
* 标签名称
*/
private String tagName;
/**
* 标签类型:1-系统标签,2-自定义标签
*/
private Integer tagType;
/**
* 状态:0-未发布,1-已发布
*/
private Integer status;
/**
* 是否删除
*/
private Integer deleted;
}

@Data 注解是 Lombok 提供的,用于自动生成类的 getter、setter、equals、 hashCode 和 toString 方法,简化了代码编写。

@EqualsAndHashCode(callSuper = true) 注解也是 Lombok 提供的注解, cal lSuper = true 表示要调用父类(BaseDO)的 equals 和 hashCode 方法。

BaseDO 是我们自定义的 DO 基类,实现了 Serializable 接口 ,并且定义了主键 id( @TableI d(type = IdType.AUTO) 表示自增长,是 MyBatis-Plus 提供的注解),创建时间 createTime和更新时间 updateTime。

1
2
3
4
5
6
7
@Data
public class BaseDO implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
private Date createTime;
private Date updateTime;
}

@TableName(“tag”) 注解是 MyBatis-Plus 提供的注解,用于指定数据库表名。

另外定义了四个属性:tagName(标签名称)、tagType(标签类型)、status(状 态)和 deleted(是否删除)。这些属性对应数据库表中的列。

2.2、Mapper CRUD

MyBatis-Plus 除了提供 Service 的 CRUD, 还提供了基于 Mapper 的 CRUD。 通常一些特殊的增删改查是通过 MyBatis-Plus 的 Mapper CRUD 接口实现的。

示例:比如说我们要保存文章,可以通过下面这种方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Repository
public class ArticleDao extends ServiceImpl<ArticleMapper, ArticleDO> {
@Resource
private ArticleDetailMapper articleDetailMapper;

public Long saveArticleContent(Long articleId, String content) {
ArticleDetailDO detail = new ArticleDetailDO();
detail.setArticleId(articleId);
detail.setContent(content);
detail.setVersion(1L);
articleDetailMapper.insert(detail);
return detail.getId();
}
}

articleDetailMapper 是我们在当前类中注入的一个 Mapper 接口,它继承自 MyBatis-Plus 的 BaseMapper 接口。

1
2
public interface ArticleDetailMapper extends BaseMapper<ArticleDetailDO>{
}
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
/**
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
* <p>这个 Mapper 支持 id 泛型</p>
*
* @author hubin
* @since 2016-01-23
*/
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);

/**
* 根据 entity 条件,删除记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAP

/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
}

这样,articleDetailMapper 也就具备了基本的增删改查功能。

2.3、常用注解

@TableName 用于指定数据库表名,通常在实体类(DO 或 Entity)上使用。例如: @TableName(“user”) 。
@TableId 用于指定表中的主键字段。通常在实体类的主键属性上使用。例如: @ TableId(value = “id”, type = IdType.AUTO) ,其中 value 表示主键字段名, type 表示主键生成策略。
@TableField 用于指定表中的非主键字段。可以用于实体类的属性上,以映射属 性和数据库字段。
@TableLogic 用于指定逻辑删除字段。逻辑删除是指在数据库中标记某个记录已 删除,而不是真正地删除记录。例如: @TableLogic(value = “0”, delval = “1”) ,其中 value 表示未删除状态的默认值,delval 表示删除状态的值。
@Version 用于指定乐观锁字段。乐观锁是一种并发控制策略,用于解决多个线程 同时修改同一条记录的问题。例如: @Version private Integer version;

3、MyBatis-Plus查询

3.1、普通查询

MyBatis-Plus 的 BaseMapper 提供了多种查询方法,比如说根据 ID 查找文章是这样用的:

1
ArticleDO article = baseMapper.selectById(articleId);

除此之外,还有根据ID 批量查询的 selectBatchIds:

1
baseMapper.selectBatchIds(Arrays.asList(1,2));

根据键值对查询的 selectByMap:

1
2
3
Map<String, Object> map = new HashMap<>();
map.put("id", 15L);
List<ArticleDO> dtoList = baseMapper.selectByMap(map);

3.2、条件构造器

MyBatis-Plus 的 Wrapper 是一个条件构造器,用于简化复杂的 SQL 查询条件的构建。它 提供了一系列易于使用的 API,让你能够以链式编程的方式编写查询条件,而不需要手动编 写 SQL 语句。

示例:假如我们来查询这样一个结果,包含“j”且状态是已发布的标签。我们可以这样来构建条件构造器。

1
2
3
4
5
6
7
8
9
10
@Test
public void testWrapper() {
QueryWrapper<TagDO> wrapper = new QueryWrapper<>();
// 包含“j”且状态是已发布
wrapper.like("tag_name", "j").eq("status", 1);
BaseMapper<TagDO> baseMapper = tagDao.getBaseMapper();
List<TagDO> tagList = baseMapper.selectList(wrapper);
tagList.forEach(System.out::println);
}

QueryWrapper:用于构建查询条件。它继承自 AbstractWrapper,提供了各种查询条件 的构建方法,如 eq, ne, gt, ge, lt, le, like, isNull, orderBy 等等。详细见:条件构造器 | MyBatis-Plus (baomidou.com)

但是,通过表的字段总感觉很不舒服,万一哪一天数据库表发生变化了怎么办呢?代码和数据库就不匹配了呀。 更优雅的做法是采用 Lambda 的方式,如下查询标签示例:

1
2
3
4
5
6
7
8
9
10
11
12
public List<TagDTO> listOnlineTag(String key, PageParam pageParam) {
LambdaQueryWrapper<TagDO> query = Wrappers.lambdaQuery();
query.eq(TagDO::getStatus, PushStatusEnum.ONLINE.getCode())
.eq(TagDO::getDeleted, YesOrNoEnum.NO.getCode())
.and(!StringUtils.isEmpty(key), v -> v.like(TagDO::getTagNa
.orderByDesc(TagDO::getId);
if (pageParam != null) {
query.last(PageParam.getLimitSql(pageParam));
}
List<TagDO> list = baseMapper.selectList(query);
return ArticleConverter.toDtoList(list);
}

①、可以通过 Wrappers.lambdaQuery() 静态方法创建一个 Lambda 条件构造器。

②、 eq(TagDO::getStatus, PushStatusEnum.ONLINE.getCode()) :表示查询条件 为 status 等于 PushStatusEnum.ONLINE 的值(即查询上线的标签)。

③、 eq(TagDO::getDeleted, YesOrNoEnum.NO.getCode()) :表示查询条件为 deleted 等于 YesOrNoEnum.NO 的值(即查询未删除的记录)。

④、 and(!StringUtils.isEmpty(key), v -> v.like(TagDO::getTagName, ke y)) :表示如果 key 不为空,则添加一个查询条件,要求 tag_name 包含 key。

⑤、 orderByDesc(TagDO::getId) :表示按照 id 字段降序排序。

⑥、 if (pageParam != null) { query.last(PageParam.getLimitSql(pagePara  m)); } :如果 pageParam 不为 null,则添加分页参数。

这样的话,就可以和数据库的字段隔离开,完全通过代码的方式去查询。

3.3、MyBatis-Plus自定义SQL

MyBatis-Plus 支持自定义 SQL 语句,我们可以在 Mapper 接口中编写自定义 SQL 方法, 并使用注解添加自定义的 SQL 语句。

示例:微信登录的时候会执行这条 SQL 语句。

1
2
3
4
5
6
7
8
9
10
public interface UserMapper extends BaseMapper<UserDO> {
/**
* 根据三方唯一id进行查询
*
* @param accountId
* @return
*/
@Select("select * from user where third_account_id = #{account_id}
UserDO getByThirdAccountId(@Param("account_id") String accountId);
}

接口中定义了一个名为 getByThirdAccountId 的方法,它接收一个名为 accountId 的参 数。

该方法使用了 @Select 注解,这个注解用于编写自定义的 SQL 查询。 @Select 注解内的 SQL 语句是: select * from user where third_account_id = #{account_i d} limit 1 ,它会根据传入的 account_id 参数查询 user 表中的记录。 同时,方法参数 accountId 使用了 @Param 注解,指定了参数在 SQL 语句中的名称为 account_id。这样,在执行 SQL 语句时,MyBatis 会将参数值替换到对应的位置上。

除此之外,可以使用 xml 的方式,用来定义一些复杂的 SQL。

比如说,我们要统计网站的 PV、UV,那么我们在 resources 目录下新建一个名为 QueryCountMapper.xml 的文件,内容如下:

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://my
<mapper namespace="com.github.paicoding.forum.service.statistics.reposi
<select id="getPvTotalCount" resultType="java.lang.Long">
select sum(cnt) from request_count
</select>
<select id="getPvDayList" resultType="com.github.paicoding.forum.ap
SELECT sum(cnt) as count, date
FROM request_count
group by date order by date asc
limit #{day};
</select>
<select id="getUvDayList" resultType="com.github.paicoding.forum.ap
SELECT count(*) as count, date
FROM request_count
group by date order by date asc
limit #{day};
</select>
</mapper>

①、在 resources 目录下的好处是,MyBatis-Plus 默认帮我们配置了 xml 的位置,这样我们就不需要在 application.yml 中再配置了。

②、该 XML 文件定义了一个RequestCountMapper 映射器,它包含三个自定义查询:getPvTotalCount、getPvDayList 和 getUvDayList。与com.github.paicoding.forum.service.statistics.repository.mapper.RequestCountMapper 相匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface RequestCountMapper extends BaseMapper<RequestCountDO> 
/**
* 获取 PV 总数
*
* @return
*/
Long getPvTotalCount();
/**
* 获取 PV 数据列表
* @param day
* @return
*/
List<StatisticsDayDTO> getPvDayList(@Param("day") Integer day);
/**
* 获取 UV 数据列表
*
* @param day
* @return
*/
List<StatisticsDayDTO> getUvDayList(@Param("day") Integer day);
}

4、MyBatis-Plus主键策略

策略 含义
IdType.AUTO 自增策略,也就是说,在插入数据时,无需设置主键值,数据库会自动分配主键值,数据库表的 ID 会设置为 Auto Increment。
IdType.NONE 无主键策略。表示不使用任何主键生成策略,主键值需要手动设置。
IdType.UUID 使用 UUID 作为主键。插入数据时,MyBatis-Plus 会自动生成一个 UUID 值作为主键值。
IdType.ID_WORKER 使用雪花算法生成分布式唯一 ID。插入数据时,MyBatis-Plus 会 自动生成一个雪花 ID 作为主键值。
1
2
3
4
5
public class User {
@TableId(type = IdType.ID_WORKER) //默认
private Long id;
// ...
}


本站总访问量