1. MybatisPlus注解体系概览
MybatisPlus作为Mybatis的增强工具,其注解体系是框架的核心组成部分。这些注解主要分为四大类:实体类注解、条件构造注解、SQL操作注解和扩展功能注解。在实际项目开发中,合理使用这些注解可以显著减少样板代码,提升开发效率。
实体类注解主要用于定义ORM映射关系,包括:
- @TableName:指定实体类对应的数据库表名
- @TableId:标识主键字段
- @TableField:处理字段与列名的映射关系
条件构造注解用于构建查询条件:
- @Param:参数映射注解
- @Select:自定义查询SQL
- @Update:自定义更新SQL
SQL操作注解提供了更灵活的SQL控制:
- @Insert:自定义插入SQL
- @Delete:自定义删除SQL
- @Results:结果集映射
扩展功能注解则包含了一些高级特性:
- @Version:乐观锁注解
- @EnumValue:枚举类型处理
- @InterceptorIgnore:拦截器忽略
提示:MybatisPlus注解的设计遵循"约定优于配置"原则,大部分注解都有默认行为,只有在需要覆盖默认行为时才需要显式配置。
1.1 注解的继承与组合使用
MybatisPlus注解支持继承和组合使用,这为复杂场景提供了灵活解决方案。例如,@TableName注解可以定义在父类上,子类会自动继承表名配置。同时,多个注解可以组合使用来实现复杂功能。
java复制@TableName("sys_user")
public class BaseEntity {
@TableId(type = IdType.AUTO)
private Long id;
}
public class User extends BaseEntity {
@TableField("user_name")
private String name;
@TableField(exist = false)
private String tempField;
}
在这个例子中,User类继承了BaseEntity的@TableName和@TableId配置,同时添加了自己的字段映射规则。这种设计既减少了重复配置,又保持了灵活性。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心注解深度解析
2.1 @TableField注解的进阶用法
@TableField注解远比表面看起来强大,它支持多种特殊场景的字段映射:
- 非表字段映射:通过exist属性标记不属于数据库表的字段
java复制@TableField(exist = false)
private String virtualField;
- 字段填充策略:实现自动填充创建时间、更新时间等字段
java复制@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
- 字段加密:结合自定义TypeHandler实现字段加解密
java复制@TableField(typeHandler = EncryptTypeHandler.class)
private String password;
- 字段条件构造:控制字段是否参与条件构造
java复制@TableField(condition = SqlCondition.LIKE)
private String name;
注意:当使用fill属性实现自动填充时,需要配置MetaObjectHandler。这是一个常见的配置遗漏点,会导致自动填充失效。
2.2 @Version乐观锁实现原理
@Version注解是MybatisPlus实现乐观锁的核心机制。它的工作原理是:
- 在实体类中标记版本号字段
java复制@Version
private Integer version;
- 更新时,MybatisPlus会自动在SQL中添加版本条件
sql复制UPDATE table SET ..., version = version + 1 WHERE id = ? AND version = ?
- 如果版本不匹配,更新操作返回影响行数为0,业务层可以据此判断并发冲突
实际项目中,乐观锁配置还需要以下步骤:
- 配置乐观锁插件
java复制@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
- 处理并发冲突
java复制User user = userService.getById(id);
user.setName(newName);
boolean success = userService.updateById(user);
if (!success) {
// 处理并发冲突
}
2.3 @EnumValue处理枚举类型
MybatisPlus提供了优雅的枚举处理方案,@EnumValue注解用于标记枚举中对应数据库值的属性:
java复制public enum Gender {
MALE(1, "男"),
FEMALE(2, "女");
@EnumValue
private final int code;
private final String desc;
// constructor and getters
}
配置后,MybatisPlus会自动进行枚举与数据库值的转换。对于更复杂的枚举处理,可以结合Mybatis的TypeHandler机制:
java复制@TableField(typeHandler = EnumTypeHandler.class)
private Gender gender;
3. 条件构造与SQL注解
3.1 @Param注解的妙用
@Param注解虽然简单,但在复杂查询中作用关键。它主要有两个用途:
- 参数名绑定:当方法有多个参数时,为参数指定名称
java复制List<User> selectByCondition(@Param("name") String name, @Param("age") Integer age);
- 在XML映射文件中引用参数
xml复制<select id="selectByCondition" resultType="User">
SELECT * FROM user
WHERE name LIKE #{name} AND age > #{age}
</select>
常见问题排查:
- 参数绑定失败:检查@Param值是否与XML中的引用一致
- 参数为null:考虑使用@Param(required = false)标记可选参数
- 与Spring的@RequestParam混淆:两者作用不同,@Param用于Mybatis参数映射
3.2 自定义SQL注解
MybatisPlus支持在Mapper接口方法上直接使用Mybatis的SQL注解:
java复制@Select("SELECT * FROM user WHERE age > #{age}")
List<User> selectByAge(@Param("age") int age);
@Update("UPDATE user SET name = #{name} WHERE id = #{id}")
int updateNameById(@Param("id") Long id, @Param("name") String name);
这些注解虽然方便,但在复杂SQL场景下,XML映射文件仍然是更好的选择,因为:
- XML支持更好的SQL格式化
- 可以复用SQL片段
- 更易于维护复杂SQL
4. 高级特性与实战技巧
4.1 多租户注解实现
基于MybatisPlus实现多租户通常需要以下步骤:
- 定义租户注解
java复制@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Tenant {
String value() default "";
}
- 实现多租户拦截器
java复制public class TenantInterceptor implements InnerInterceptor {
@Override
public void beforeQuery(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) {
// 解析租户注解并添加租户条件
}
}
- 在实体类或Mapper方法上使用注解
java复制@Tenant("company_id")
public class Order {
// ...
}
@Tenant("org_id")
List<Order> selectByUser(Long userId);
4.2 字段加解密拦截器
实现字段自动加解密的典型方案:
- 定义加解密注解
java复制@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
String algorithm() default "AES";
}
- 实现TypeHandler处理加解密
java复制public class EncryptTypeHandler extends BaseTypeHandler<String> {
private Encryptor encryptor = new AESEncryptor();
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
String parameter, JdbcType jdbcType) {
ps.setString(i, encryptor.encrypt(parameter));
}
// 其他方法实现
}
- 在实体字段上使用
java复制@TableField(typeHandler = EncryptTypeHandler.class)
@Encrypt
private String mobile;
4.3 分页查询优化
针对MybatisPlus分页查询的500条限制问题,解决方案包括:
- 自定义分页插件
java复制public class CustomPaginationInterceptor extends PaginationInnerInterceptor {
@Override
protected void handlerLimit(long limit) {
if (limit > 1000) {
throw new RuntimeException("分页大小超过限制");
}
}
}
- 流式查询处理大数据量
java复制@Select("SELECT * FROM large_table")
@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
@ResultType(LargeData.class)
void streamLargeData(ResultHandler<LargeData> handler);
- 分批处理
java复制public <T> void batchProcess(IService<T> service, int batchSize, Consumer<List<T>> processor) {
int count = service.count();
int pages = (count + batchSize - 1) / batchSize;
for (int i = 1; i <= pages; i++) {
Page<T> page = new Page<>(i, batchSize);
List<T> records = service.page(page).getRecords();
processor.accept(records);
}
}
5. 常见问题排查
5.1 注解不生效的排查步骤
当MybatisPlus注解没有按预期工作时,可以按照以下步骤排查:
-
检查注解是否正确导入
- 确保使用的是com.baomidou.mybatisplus.annotation包下的注解
- 避免误用javax.persistence或org.apache.ibatis.annotations中的同名注解
-
验证配置是否正确加载
- 检查@MapperScan是否配置正确
- 确认MybatisPlusInterceptor已正确配置并包含所需拦截器
-
检查实体类扫描
- 确保实体类所在的包被正确扫描
- 对于非标准命名位置的实体,可能需要额外配置typeAliasesPackage
-
查看生成的SQL
- 开启SQL日志:mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
- 分析生成的SQL是否符合预期
5.2 注解冲突与兼容性问题
在多框架集成时,注解可能会产生冲突:
-
与JPA注解的冲突
- @Table与@TableName:建议统一使用MybatisPlus注解
- @Column与@TableField:功能类似,避免混用
-
与Lombok的兼容性
- 确保IDE安装了Lombok插件
- 注解顺序:Lombok注解应放在类/字段最前面
-
与Spring注解的混淆
- @Autowired与@Resource:不影响MybatisPlus但影响代码风格
- @Transactional:注意事务传播行为对ORM操作的影响
5.3 性能优化建议
-
注解扫描优化
- 限制typeAliasesPackage范围,避免扫描整个类路径
- 对于大型项目,考虑按模块拆分Mapper扫描
-
缓存策略
- 合理配置二级缓存:@CacheNamespace
- 对于读多写少的数据,考虑开启缓存
-
批量操作
- 使用saveBatch替代循环save
- 对于大批量插入,考虑使用原生JDBC或MyBatis的批量模式
-
延迟加载
- 对于关联查询,合理使用@TableField(exist = false)和延迟加载
- 避免N+1查询问题
在实际项目中,我通常会创建一个BaseMapper扩展接口,将常用的批量操作方法集中定义:
java复制public interface MyBaseMapper<T> extends BaseMapper<T> {
/**
* 批量插入(MySQL语法)
*/
int insertBatchSomeColumn(List<T> entityList);
/**
* 根据ID批量更新
*/
int updateBatchByIds(@Param("list") List<T> entityList);
}
这种扩展方式既保持了MybatisPlus的简洁性,又补充了实际项目需要的批量操作能力。
