引言
MyBatis-Plus(简称MP)作为MyBatis的增强版ORM框架,在简化单调的CRUD操作的同时,也为开发者提供了众多高级特性,让数据操作更加高效、灵活。在这篇博客中,我们将深入探讨MP的一些高阶用法。
高级查询
动态SQL与条件构造器
MP的条件构造器是动态SQL的强大工具,它让你能够根据不同的条件动态构建SQL。
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNull("email")
.between("age", 18, 30)
.like("name", "John")
.or(wq -> wq.eq("status", "ACTIVE").ne("status", "INACTIVE"))
.orderByDesc("age");
List<User> users = userMapper.selectList(queryWrapper);
在上面的例子中,我们构建了一个复杂的查询,它将查找没有电子邮件、年龄在18到30之间、名字中包含"John",以及状态为"ACTIVE"或不是"INACTIVE"的用户,并按年龄降序排序。
Lambda条件构造器
使用Lambda条件构造器可以避免硬编码字段名,减少出错的可能性。
LambdaQueryWrapper<User> lambdaQuery = Wrappers.lambdaQuery();
lambdaQuery
.eq(User::getAge, 25)
.or()
.startsWith(User::getName, "Tom");
List<User> userLambdas = userMapper.selectList(lambdaQuery);
自定义SQL
有时候,我们需要执行一些MP的自动CRUD无法覆盖的操作。MP允许我们在Mapper接口中自定义SQL。
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT * FROM user WHERE age > #{age}")
List<User> selectUsersOlderThanAge(@Param("age") int age);
}
高级映射
结果映射
MP支持复杂的结果映射,可以处理多表连接查询和映射到实体类的嵌套属性。
<resultMap id="userDetailMap" type="UserDetailDTO">
<association property="user" column="user_id" javaType="User">
<result property="name" column="user_name"/>
<!-- 其他字段映射 -->
</association>
<collection property="orders" ofType="Order">
<result property="orderDate" column="order_date"/>
<!-- 其他字段映射 -->
</collection>
</resultMap>
自定义TypeHandler
如果你需要处理一种MyBatis不直接支持的数据类型,可以通过实现TypeHandler
接口来自定义类型处理器。
@MappedTypes(CustomObject.class)
public class CustomObjectTypeHandler extends BaseTypeHandler<CustomObject> {
// 实现四个方法:setNonNullParameter、getNullableResult
}
实现好后,你可以在mybatis-config.xml中注册这个TypeHandler:
<typeHandlers>
<typeHandler handler="com.example.CustomObjectTypeHandler"/>
</typeHandlers>
逻辑删除
逻辑删除是指在数据库中用一个字段标识数据是否被删除,而不是真的将数据行删除。在MP中使用逻辑删除非常简单:
-
在你的实体类中标注逻辑删除属性:
@TableLogic private Integer deleted;
-
在application.properties中配置逻辑删除字段的策略:
mybatis-plus.global-config.db-config.logic-delete-field=deleted mybatis-plus.global-config.db-config.logic-delete-value=1 mybatis-plus.global-config.db-config.logic-not-delete-value=0
性能分析插件
MP提供了一个非常实用的性能分析插件,能够帮助你监控和优化SQL执行性能。
@Bean
@Profile({"dev","test"}) // 只在开发和测试环境开启
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1000); // 设置SQL执行的最大时间,如果超过这个时间,将会停止运行
performanceInterceptor.setFormat(true); // 是否格式化SQL
return performanceInterceptor;
}
乐观锁插件
在面对并发更新时,乐观锁是一种常用策略。MP的乐观锁插件可以让你轻松实现这一功能。
-
在实体类中添加版本号字段(带@Version注解):
@Version private Integer version;
-
开启乐观锁插件:
@Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }
自动填充
在实际开发中,我们经常需要自动填充某些字段,比如创建时间和更新时间。MP提供了自动填充的功能,极大地简化了这一过程。
定义填充策略
首先,你需要在实体类字段上使用@TableField
注解标明其填充策略:
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
实现自定义填充处理器
接着,实现MetaObjectHandler
接口以自定义填充逻辑:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
分页插件
分页是web开发中常见的需求,MP的分页插件让分页变得非常简单。
配置分页插件
在你的配置类中添加分页插件的配置:
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
使用分页
使用分页功能时,只需构造Page
对象并传递给Mapper方法:
Page<User> page = new Page<>(1, 10); // 查询第1页,每页返回10条
Page<User> result = userMapper.selectPage(page, null);
多租户SQL解析器
在多租户系统中,你可能需要在每次查询时自动加入租户ID过滤,MP的多租户功能可以帮助你轻松实现。
实现TenantIdHandler接口
首先,实现TenantIdHandler
接口:
public class MyTenantIdHandler implements TenantIdHandler {
@Override
public Expression getTenantId() {
// 返回当前租户ID的表达式
return new LongValue(TenantContext.getCurrentTenant());
}
@Override
public boolean doTableFilter(String tableName) {
// 这里可以决定哪些表需要进行租户隔离
return "user".equals(tableName);
}
}
配置多租户插件
然后,在你的配置类中添加多租户插件的配置:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
TenantLineInnerInterceptor tenantLineInnerInterceptor = new TenantLineInnerInterceptor();
tenantLineInnerInterceptor.setTenantLineHandler(new MyTenantIdHandler());
interceptor.addInnerInterceptor(tenantLineInnerInterceptor);
return interceptor;
}
自定义主键策略
在某些情况下,你可能需要自定义ID生成策略。MP允许你灵活地定义主键生成规则。
使用@TableId注解
在实体类的ID字段上使用@TableId
注解,并指定type属性来自定义ID生成策略:
@TableId(type = IdType.ASSIGN_ID)
private Long id;
自定义ID生成器
如果内置的ID生成策略无法满足需求,你可以实现IdentifierGenerator
接口:
@Component
public class CustomIdGenerator implements IdentifierGenerator {
@Override
public Number nextId(Object entity) {
// 实现自定义ID生成逻辑
return ...
}
}
结语
通过上述的高阶用法,MyBatis-Plus显著提高了数据处理的效率和灵活度。无论是复杂查询、动态SQL构造,还是结果映射和性能优化,MP都提供了强大的工具和简洁的API来满足你的需求。不过,这些只是冰山一角,要想完全掌握MyBatis-Plus的高级特性,你需要更多的实践和深入学习。不断探索,你将能够更充分地利用MyBatis-Plus的强大能力,编写出更高质量、更高效率的代码。