1. MyBatis-Plus核心注解全解析
MyBatis-Plus作为MyBatis的增强工具,通过一系列注解极大简化了CRUD操作。这些注解主要分为实体类注解、条件构造注解和功能控制注解三大类,下面我将结合实战经验详细解析每个注解的使用场景和注意事项。
1.1 实体类映射注解
@TableName注解用于处理表名与实体类名的映射关系,这是实际开发中最先接触的注解。当你的表名与实体类名不符合默认规则(如user表对应User实体类)时,就需要使用它:
java复制@TableName("sys_user") // 映射sys_user表
public class User {
private Long id;
private String name;
}
这里有个实际项目中的坑需要注意:当使用动态表名(如分表场景)时,直接使用@TableName的value属性无法满足需求。此时应该实现ITableNameHandler接口,下面是我们在电商项目中处理订单分表的示例:
java复制public class OrderTableNameHandler implements ITableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
// 根据年份分表,如order_2023
return tableName + "_" + LocalDate.now().getYear();
}
}
@TableId注解处理主键映射,其type属性特别重要。在我们的金融系统中,曾因为没明确指定主键类型导致批量插入性能下降60%。正确的用法应该是:
java复制@TableId(value = "user_id", type = IdType.AUTO) // 明确指定自增主键
private Long id;
所有可选的IdType包括:
- AUTO:数据库自增
- INPUT:手动输入
- ASSIGN_ID:雪花算法生成
- ASSIGN_UUID:UUID生成
@TableField注解处理非主键字段映射,有几个实用功能经常被忽略:
- 字段自动填充(如创建时间、更新时间)
- 字段不参与查询(如大文本字段)
- 字段条件构造策略
这是我们物流系统中的实际用例:
java复制@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createTime;
@TableField(exist = false) // 非数据库字段
private String fullAddress;
@TableField(condition = SqlCondition.LIKE) // 查询条件使用LIKE
private String receiverName;
1.2 条件构造相关注解
@Version和@EnumValue是两个容易被低估但极其有用的注解。在电商库存系统中,我们使用@Version实现乐观锁控制:
java复制@Version
private Integer version; // 每次更新自动+1
对应的XML中需要添加乐观锁插件配置:
xml复制<bean id="optimisticLockerInterceptor"
class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor"/>
@EnumValue注解优雅地处理枚举值与数据库值的映射。在权限系统中,我们这样定义角色枚举:
java复制public enum RoleType {
@EnumValue("admin") // 数据库中存储的值
ADMIN("管理员"),
@EnumValue("user")
USER("普通用户");
private final String desc;
}
1.3 动态表名与多租户注解
@KeySequence用于Oracle等数据库的序列主键,在政府项目中我们这样配置:
java复制@KeySequence(value = "SEQ_USER", clazz = Long.class)
public class User {}
@InterceptorIgnore注解可以临时忽略插件拦截,比如在多租户系统中,某些全局查询需要跳过租户过滤:
java复制@InterceptorIgnore(tenantLine = "true")
List<User> selectAll();
2. MyBatis-Plus核心配置详解
MyBatis-Plus的配置项集中在MybatisPlusProperties类中,合理配置这些参数可以显著提升开发效率和系统性能。
2.1 全局配置项
global-config是最高级别的配置,我们来看几个关键子项:
yaml复制mybatis-plus:
global-config:
db-config:
id-type: assign_id # 全局主键策略
logic-delete-field: isDeleted # 逻辑删除字段
logic-not-delete-value: 0 # 未删除值
logic-delete-value: 1 # 删除值
banner: false # 关闭启动banner
在医疗系统中,我们因为忘记设置logic-not-delete-value导致已删除数据被查询出来,造成严重业务问题。建议所有使用逻辑删除的项目都显式配置这些值。
2.2 分页与性能配置
分页配置是实际项目中最常用的功能之一。在电商后台系统中,我们这样优化分页查询:
yaml复制mybatis-plus:
configuration:
default-executor-type: reuse # 重用预编译语句
map-underscore-to-camel-case: true # 自动驼峰转换
page:
optimize-count-sql: true # 优化count查询
max-limit: 1000 # 单页最大记录数
特别提醒:当使用left join时,optimize-count-sql可能会生成错误的count语句。我们的解决方案是自定义分页SQL:
java复制@Select("SELECT u.*, d.name as deptName FROM user u LEFT JOIN dept d ON u.dept_id=d.id ${ew.customSqlSegment}")
Page<UserDto> selectUserPage(Page<UserDto> page, @Param(Constants.WRAPPER) QueryWrapper<UserDto> wrapper);
2.3 自定义ID生成器
在分布式系统中,默认的雪花算法可能不满足需求。我们在金融项目中自定义了ID生成器:
java复制public class CustomIdGenerator implements IdentifierGenerator {
@Override
public Number nextId(Object entity) {
// 结合业务规则生成ID
return BusinessIdGenerator.generate();
}
}
配置方式:
yaml复制mybatis-plus:
global-config:
db-config:
id-type: assign_id
identifier-generator: com.xxx.CustomIdGenerator
3. 注解与配置的实战陷阱
3.1 字段映射的坑
我们在ERP系统中遇到过@TableField的巨坑:当实体类属性名与数据库字段名都是下划线格式时,即使开启了map-underscore-to-camel-case也会映射失败。解决方案是显式指定:
java复制@TableField(value = "user_name") // 即使属性名也是user_name也要显式指定
private String user_name;
3.2 逻辑删除的注意事项
逻辑删除与唯一索引冲突是常见问题。在用户系统中,我们这样解决:
- 将唯一索引改为联合索引(包含is_deleted字段)
- 删除操作改为update set is_deleted=id where id=xxx(保证唯一性)
3.3 自动填充的线程安全问题
在并发场景下,MetaObjectHandler的实现必须是线程安全的。我们审计系统曾因此出现数据混乱:
java复制@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
private static final ThreadLocal<Long> userIdHolder = new ThreadLocal<>();
public static void setUserId(Long userId) {
userIdHolder.set(userId);
}
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createBy", Long.class, userIdHolder.get());
}
}
4. 高级配置技巧
4.1 多数据源下的注解配置
在多数据源环境中,某些注解需要特殊处理。我们在财务系统中这样配置:
java复制@DS("slave") // 指定从库
public interface UserMapper extends BaseMapper<User> {
@DS("master") // 这个方法切到主库
void criticalUpdate(User user);
}
4.2 自定义SQL注入器
扩展MyBatis-Plus的功能需要自定义SQL注入器。比如我们为CMS系统添加批量插入方法:
java复制public class BatchInsertInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methods = super.getMethodList(mapperClass);
methods.add(new BatchInsert());
return methods;
}
}
4.3 性能监控配置
在生产环境中,我们这样配置SQL监控:
yaml复制mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
plugins:
- stat:
log-slow-sql: true
slow-sql-millis: 1000
- performance:
max-time: 500
format: true
配合自定义拦截器记录慢SQL:
java复制@Intercepts(@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}))
public class SlowSqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long time = System.currentTimeMillis() - start;
if (time > 1000) {
log.warn("Slow SQL detected: {}ms", time);
}
return result;
}
}
这些注解和配置的深入理解与合理应用,能帮助你在项目中充分发挥MyBatis-Plus的威力。记住,任何工具都有其适用场景,理解原理比记住用法更重要。
