mybatis-plus 对原生 mybatis 做了扩展增强而不改变任何 mybatis 的原有逻辑, 安全可靠的同时极大降低了实体映射的代码量及配置量, 提升了程序员的工程效率!
ORM 配置
映射关系表达:
- 使用
@com.baomidou.mybatisplus.annotation.TableName
注解申明一个实体和一张数据库表的关系; - 使用
@com.baomidou.mybatisplus.annotation.TableId
注解申明实体的字段与数据库表主键的关系; - 使用
@com.baomidou.mybatisplus.annotation.TableField
注解申明实体的字段与数据库表字段的关系;
1 | "t_xxx") ( |
数据操纵
mybatis-plus 提供了功能强大的数据操纵方法, 在继承了 com.baomidou.mybatisplus.core.mapper.BaseMapper
后, 对于基础的增删改查, 几乎可以做到无需创建 mapper.xml 文件自己手写 sql 语句;
Mapper 接口只需继承 BaseMapper 即可:1
2
3 .apache.ibatis.annotations.Mapper
public interface XXXMapper extends BaseMapper<XXXDO> {
}
随后即可使用 BaseMapper 强大的数据操纵能力:
insert:1
xxxMapper.insert(xxxDO);
select:1
2
3
4
5
6
7
8
9
10
11// select single
final LambdaQueryWrapper<XXXDO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(XXXDO::getName, name)
.eq(XXXDO::getTenant, tenant);
final XXXDO xxxDO = xxxMapper.selectOne(queryWrapper);
// select list
final LambdaQueryWrapper<XXXDO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(XXXDO::getTag, tag)
.eq(XXXDO::getTenant, tenant);
final List<XXXDO> xxxDOList = xxxMapper.selectList(queryWrapper);
update:1
2
3
4
5
6
7
8
9// update by condition
final LambdaUpdateWrapper<XXXDO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(XXXDO::getTags, ConvertUtil.commaJoin(tags))
.eq(XXXDO::getName, name)
.eq(XXXDO::getTenant, tenant);
return xxxMapper.update(null, updateWrapper);
// update by id
xxxMapper.updateById(xxxDO);
高级用法
动态表名处理
例如: 针对指定表的数据操纵, 统一为表名添加后缀;
此类场景可以使用 mybatis-plus 的 com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler
解决, 方案如下:
用于数据操纵中感知目标发布环境的工具:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 (access = AccessLevel.PRIVATE)
public final class PublishEnvAwares {
private static final ThreadLocal<Pair<String, Env>> publishEnvThreadLocal = new ThreadLocal<>();
public static Pair<String, Env> getPublishEnvAwaredTableName() {
return publishEnvThreadLocal.get();
}
/**
* 在 procedure 过程中, 让 targetTableName 感知到目标发布环境 publishEnv
*/
public static <T> T awarePublishEnv(Supplier<T> procedure, String targetTableName, Env publishEnv) {
try {
publishEnvThreadLocal.set(Pair.of(targetTableName, publishEnv));
return procedure.get();
} finally {
publishEnvThreadLocal.remove();
}
}
}
基于目标发布环境的数据库表名动态计算扩展:1
2
3
4
5
6
7
8
9
10
11
12
13
14public class PublishEnvTableNameHandler implements TableNameHandler {
private static final String TABLE_NAME_PUBLISH_ENV_JOINER = "_";
public String dynamicTableName(String sql, String tableName) {
final Pair<String, Env> tableNameToPublishEnv = PublishEnvAwares.getPublishEnvAwaredTableName();
if (tableNameToPublishEnv == null || !StringUtils.equals(tableNameToPublishEnv.getLeft(), tableName)) {
return tableName;
}
return tableName + TABLE_NAME_PUBLISH_ENV_JOINER + tableNameToPublishEnv.getRight().getName();
}
}
将动态表名处理器注册到 mybatis-plus:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyBatisPlusConfig {
public MybatisPlusInterceptor mybatisPlusInterceptor() {
final MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 新增分页拦截器, 并设置数据库类型为 mysql
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 表名动态计算
final DynamicTableNameInnerInterceptor tableNameInterceptor = new DynamicTableNameInnerInterceptor();
tableNameInterceptor.setTableNameHandler(new PublishEnvTableNameHandler());
interceptor.addInnerInterceptor(tableNameInterceptor);
return interceptor;
}
}
实现表名后缀自动添加扩展的 Mapper:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface XXXMapper extends BaseMapper<XXXDO> {
String TARGET_TABLE_NAME = "xxxx";
default int insertWithPublishEnv(XXXDO xxxDO, Env publishEnv) {
return PublishEnvAwares.awarePublishEnv(() -> this.insert(xxxDO),
TARGET_TABLE_NAME, publishEnv);
}
default int updateByIdWithPublishEnv(@Param("et") XXXDO entity, Env publishEnv) {
return PublishEnvAwares.awarePublishEnv(() -> this.updateById(entity),
TARGET_TABLE_NAME, publishEnv);
}
default XXXDO selectWithPublishEnv(@Param("ew") Wrapper<XXXDO> queryWrapper,
Env publishEnv) {
return PublishEnvAwares.awarePublishEnv(() -> this.selectOne(queryWrapper),
TARGET_TABLE_NAME, publishEnv);
}
}
复杂查询语句
可以使用 LambdaQueryChainWrapper: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
40final LambdaQueryChainWrapper<XXXDO> queryWrapper = new LambdaQueryChainWrapper<>(xxxMapper);
final List<XXXDO> xxxDOList = queryWrapper.eq(XXXDO::getTenant, tenant)
.eq(!ObjectUtils.isEmpty(type), XXXDO::getType, type)
.apply(!CollectionUtils.isEmpty(devStatus), buildDevStatusPredicateSql(devStatus), devStatus)
.apply(!CollectionUtils.isEmpty(tags), buildTagsPredicateSql(tags), tags)
.apply(!CollectionUtils.isEmpty(nameSegments), buildNamePredicateSql(nameSegments), nameSegments)
.in(XXXDO::getApp, appCodes)
.orderBy(order != null, Order.isAsc(order), XXXDO::getGmtModified)
.last(String.format("limit %d,%d", (pageNum - 1) * pageSize, pageSize));
.list();
/**
* @return e.g. name LIKE '%com.api.name%1%'
*/
private String buildNamePredicateSql(List<String> nameSegments) {
return "name LIKE '" + nameSegments.stream().collect(Collectors.joining("%", "%", "%")) + "'";
}
/**
* @return e.g. dev_status ->> '$.DAILY' = 'UNPUBLISHED' AND dev_status ->> '$.PRE' = 'UNPUBLISHED' AND dev_status ->> '$.PROD' = 'UNPUBLISHED'
*/
private static String buildDevStatusPredicateSql(Map<Env, DevStatus> devStatus) {
if (CollectionUtils.isEmpty(devStatus)) {
return EMPTY_STRING;
}
return devStatus.entrySet().stream().map(envDevStatusEntry -> "devStatus ->> '$." + envDevStatusEntry.getKey() + "' = '" + envDevStatusEntry.getValue() + "'")
.collect(Collectors.joining(" AND "));
}
/**
* @return e.g. tags LIKE '%tag1% OR %tag2%'
*/
private static String buildTagsPredicateSql(Set<String> tags) {
if (CollectionUtils.isEmpty(tags)) {
return EMPTY_STRING;
}
return tags.stream().map(tag -> "tags LIKE '%" + tag + "%'")
.collect(Collectors.joining(" OR "));
}