MybatisPlus的使用跟扩展
# MybatisPlus的使用跟扩展
# 介绍
不整虚的,直接来官网。它是什么呢,
MyBatis-Plus (opens new window) 是一个 MyBatis (opens new window) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
- 官网- https://baomidou.com/
本质是通过反射和动态代理,生成常用的sql的一个工具。
官网有快速入门,这里就不多做介绍了。下面介绍的是一些不常用的场景跟注意事项。
# 分页插件
以下是mybatisplus分页插件的配置,更多信息请查阅官网
@Configuration
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
return interceptor;
// 添加分页插件
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 快速实现实体类查询
@Data
@TableName("`user`")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
2
3
4
5
6
7
8
- mapper
public interface UserMapper extends BaseMapper<User> {
Page<ExUser> selectPageVo(Page<ExUser> page, QueryWrapper queryWrapper);
}
2
3
4
- xml
<?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.sugar.mybatisplus_demo.mapper.UserMapper">
<!-- 其他 SQL 语句 -->
<select id="selectPageVo" resultMap="com.sugar.mybatisplus_demo.bean.ExUser">
select * from user
</select>
</mapper>
2
3
4
5
6
7
8
9
如此一来继承BaseMapper后,UserMapper ,就会拥有一些常用的sql操作。
当然还有复杂操作
Page<User> page = new Page<>(1, 2);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(User::getName, "小")
.eq(User::getAge, 18);
userMapper.selectPage(page,queryWrapper);
2
3
4
5
# 业务类的继承类
@Service
@Slf4j
public class XxxxServiceImpl extends ServiceImpl<XxxxMapper, XxxxEntity> implements XxxxService {
// ........其他正常的业务注入与业务代码
}
2
3
4
5
6
使用mybatisplus开发时,常会看到以上代码。
这样写法的作用,其中 implements XxxxService 为我自己定义的接口类,与框架无关,实际需要的是 extends ServiceImpl<XxxxMapper, XxxxEntity> 这一部分
MyBatis-Plus 的 ServiceImpl 类是一个实现了 IService 接口的抽象类,它提供了对数据库操作的通用实现,从而减少了重复代码的编写。在使用 ServiceImpl 时,你通常会创建自己的服务实现类,继承自 ServiceImpl,并指定相应的 Mapper 接口和实体类。这样,你的服务类就继承了一系列 CRUD(创建、读取、更新、删除)操作。
它的内部向我们提供了 如下公共方法,可以直接使用,甚至还为我们添加了事务回滚。
- 简化CRUD操作:通过继承
ServiceImpl
类,XXxxxServiceImpl
自动获得了MyBatisPlus提供的基础CRUD(增删改查)操作方法,如save
、remove
、getById
、list
等。这大大减少了手动编写这些方法的工作量,提高了开发效率。 - 事务管理:
ServiceImpl
类中包含了对事务的处理,例如批量插入、更新和删除操作的事务支持。这意味着在进行这些操作时,可以确保数据的一致性和完整性。 - 泛型支持:
ServiceImpl<XXxxxoMapper, XXxxx>
中的泛型参数指定了具体的Mapper接口和实体类。这样,``XXxxxServiceImpl就可以针对
XXxxx`实体进行操作,而不需要重复编写针对该实体的数据库操作代码。 - 接口实现:通过实现
IXXxxxService
接口,XXxxxServiceImpl
可以定义和实现特定于业务逻辑的方法。这使得服务层的代码更加模块化和易于维护。 - 代码复用:继承
ServiceImpl
类意味着可以复用MyBatisPlus提供的通用方法,避免了重复代码的编写。同时,通过实现自定义的接口,可以在需要时扩展特定的功能
换句话说,在该业务类中,我们不需要注入对应Mapper文件,直接
this.mapper.xxx
即可操作持久层。
# VO类的查询结果的处理
通常情况下,有些实体类的查询结构是不能直接展示给前端的,需要进行一些处理,比如,增加字段,或者合并2个结果,下面就展示几个解决方案。
# 增加字段
// 增加 景区字段 done
List<String> sellPoints = new ArrayList<String>();
// OrderId
List<Long> orderIdLists = new ArrayList<Long>();
for (SOrderBaseinfoVo vo:list) {
orderIdLists.add(vo.getOrderId());
}
// 产品Id
List<Long> productIdList = new ArrayList();
for (Long orderId:orderIdLists) {
com.cdtskj.vo.front.SOrderGoodsVo byOrderIdDetail = orderGoodsService.getByOrderIdDetail(orderId);
productIdList.add(byOrderIdDetail.getProductId());
}
for (Long productId:productIdList) {
SProduct sProduct = productService.getBaseMapper().selectById(productId);
sellPoints.add(sProduct.getSellpoint());
}
// 关键代码 在次
List<SOrderBaseinfoVoEx> orderAndSellPoints = BeanUtil.copyListProperties(list,
SOrderBaseinfoVoEx::new);
// SOrderBaseinfoVoEx 该类的DynamicAttribute 是一个map
int num = 0;
for (SOrderBaseinfoVoEx vo:orderAndSellPoints) {
vo.setDynamicAttribute("sellPoint",sellPoints.get(num));
num++;
}
pageEx.setRecords(orderAndSellPoints);
return pageEx;
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
SOrderBaseinfoVoEx::new
是一种方法引用的写法,用于简化代码。它的原本写法是通过 Lambda 表达式 或 匿名类 来实现的。
如果没有使用方法引用,而是直接写成 Lambda 表达式,代码会是这样的:
List<SOrderBaseinfoVoEx> vos = originalList.stream()
.map(item -> new SOrderBaseinfoVoEx(item)) // 使用 Lambda 表达式创建新对象
.collect(Collectors.toList());
2
3
- 通过装饰者模式实现动态增加字段
实现List的拷贝
/**
* 集合数据的拷贝
* @param sources: 数据源类
* @param target: 目标类::new(eg: UserVO::new)
* @return
*/
public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
return copyListProperties(sources, target, null);
}
/**
* 带回调函数的集合数据的拷贝(可自定义字段拷贝规则)
* @param sources: 数据源类
* @param target: 目标类::new(eg: UserVO::new)
* @param callBack: 回调函数
* @return
*/
public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, BeanCopyUtilCallBack<S, T> callBack) {
List<T> list = new ArrayList<>(sources.size());
for (S source : sources) {
T t = target.get();
copyProperties(source, t);
list.add(t);
if (callBack != null) {
// 回调
callBack.callBack(source, t);
}
}
return list;
}
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
- 新增函数式接口
@FunctionalInterface
public interface BeanCopyUtilCallBack <S, T> {
/**
* 定义默认回调方法
* @param t
* @param s
*/
void callBack(S t, T s);
}
2
3
4
5
6
7
8
9
10
11
# 减少字段
减少字段,用自带的方法即可实现
Page<User> page = new Page<>(1, 2);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(User::getId,User::getName);
Page<User> userPage = userMapper.selectPage(page, queryWrapper);
userPage.getRecords().forEach(System.out::println);
2
3
4
5
6
queryWrapper.select();实现指定列。
# 自定义复杂连表查询
在实际开发中,常常需要多表联合查询,那此时,我们编写自定义的sql时,mapper文件中,可以传递QueryWrapper参数,以及Page参数吗,或者说该如何实现。
# 多表查询
# 映射别名跟ResultMap
如果多表查询的表名,跟VO类中的名不同,此时可以考虑使用resultMap进行转换。
11
# 额外的参数
- 简单场景:若只有 1-2 个参数,可以直接用
@Param("key")
分开传递。 - 复杂场景:优先使用 DTO 对象 替代
Map
,提升代码健壮性和可维护性。
<select id="selectUserPage" resultType="com.example.vo.UserVo">
SELECT * FROM user
WHERE 1 = 1
<if test="param.userName != null and param.userName != ''">
AND name LIKE CONCAT('%', #{param.userName}, '%')
</if>
<if test="param.minAge != null">
AND age >= #{param.minAge}
</if>
<if test="param.maxAge != null">
AND age <= #{param.maxAge}
</if>
</select>
2
3
4
5
6
7
8
9
10
11
12
13
- Mapper定义
public interface UserMapper extends BaseMapper<User> {
// 使用对象作为参数(替代 Map)
Page<UserVo> selectUserPage(
Page<UserVo> page,
@Param("param") UserQueryParam param // 替换为自定义对象
);
}
2
3
4
5
6
7
# 注意事项
# 关于别名
多表查询时,对于返回的结果,使用别名是,不能加t1.ass,(t1为表的别名),只能用一个字段名 ass。