1. 项目概述SpringBoot3与MyBatis-Plus 3.5.9整合实践最近在重构一个老项目的持久层时我选择了SpringBoot 3.x MyBatis-Plus 3.5.9这套技术组合。作为Java生态中最主流的ORM框架组合之一这套方案在开发效率、性能表现和可维护性上达到了很好的平衡。本文将分享我在实际项目中的完整整合过程包含版本适配、核心功能实现和那些官方文档没写的实战技巧。2. 环境准备与基础配置2.1 依赖管理关键点在pom.xml中需要特别注意版本匹配问题。SpringBoot 3.x默认使用Jakarta EE 9的命名空间这与MyBatis-Plus 3.5.9的依赖存在潜在冲突。以下是经过生产验证的依赖配置dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.9/version !-- 排除旧版javax.persistence依赖 -- exclusions exclusion groupIdjavax.persistence/groupId artifactIdpersistence-api/artifactId /exclusion /exclusions /dependency !-- 添加Jakarta EE兼容版本 -- dependency groupIdjakarta.persistence/groupId artifactIdjakarta.persistence-api/artifactId version3.1.0/version /dependency提示如果项目中使用Lombok需要确保版本≥1.18.24否则可能遇到注解处理器与Java 17的兼容性问题。2.2 数据源配置优化在application.yml中建议采用HikariCP连接池并启用MyBatis-Plus的性能优化参数spring: datasource: type: com.zaxxer.hikari.HikariDataSource hikari: maximum-pool-size: 20 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境SQL日志 default-scripting-language: org.apache.ibatis.scripting.defaults.RawLanguageDriver global-config: db-config: logic-delete-field: deleted # 逻辑删除字段名 logic-not-delete-value: 0 logic-delete-value: 13. 核心功能实现详解3.1 实体类与Mapper设计MyBatis-Plus 3.5.9对注解体系进行了增强。推荐使用新版TableName和TableFieldData TableName(value sys_user, autoResultMap true) public class User { TableId(type IdType.ASSIGN_ID) // 雪花算法ID private Long id; TableField(value username, condition SqlCondition.LIKE) private String name; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; Version private Integer version; }对应的Mapper接口只需继承BaseMapper即可获得CRUD能力Mapper public interface UserMapper extends BaseMapperUser { // 自定义复杂查询 Select(SELECT * FROM sys_user WHERE status #{status}) ListUser selectByStatus(Param(status) Integer status); }3.2 动态SQL构建最佳实践MyBatis-Plus 3.5.9的LambdaQueryWrapper在类型安全方面有显著提升public ListUser searchUsers(String keyword, Integer status) { return lambdaQuery() .and(wrapper - wrapper .like(User::getName, keyword) .or() .like(User::getEmail, keyword)) .eq(status ! null, User::getStatus, status) .orderByDesc(User::getCreateTime) .list(); }经验对于超过3个条件的复杂查询建议使用QueryChainWrapper保持代码可读性public ListUser complexQuery(QueryParams params) { return new QueryChainWrapper(userMapper) .like(StringUtils.isNotBlank(params.getKeyword()), User::getName, params.getKeyword()) .ge(params.getStartTime() ! null, User::getCreateTime, params.getStartTime()) .le(params.getEndTime() ! null, User::getCreateTime, params.getEndTime()) .list(); }4. 高级特性实战4.1 多租户方案实现MyBatis-Plus 3.5.9内置了多租户支持以下是基于请求头中tenant_id的完整实现Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 租户处理器 TenantLineInnerInterceptor tenantInterceptor new TenantLineInnerInterceptor(); tenantInterceptor.setTenantLineHandler(new TenantLineHandler() { Override public String getTenantIdColumn() { return tenant_id; } Override public Expression getTenantId() { return new StringValue(RequestContextHolder.getRequestAttributes() .getAttribute(tenant_id, RequestAttributes.SCOPE_REQUEST)); } Override public boolean ignoreTable(String tableName) { return !Arrays.asList(sys_user, sys_role).contains(tableName); } }); interceptor.addInnerInterceptor(tenantInterceptor); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }4.2 乐观锁与自动填充乐观锁配置需要在实体类添加Version注解见3.1节自动填充则需要实现MetaObjectHandlerComponent 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()); } }5. 性能优化与监控5.1 SQL执行监控结合p6spy实现完整SQL日志记录生产环境需谨慎dependency groupIdp6spy/groupId artifactIdp6spy/artifactId version3.9.1/version /dependency配置spy.propertiesmodule.logcom.p6spy.engine.logging.P6LogFactory appendercom.p6spy.engine.spy.appender.Slf4JLogger logMessageFormatcom.p6spy.engine.spy.appender.CustomLineFormat customLogMessageFormat%(executionTime)ms | %(category) | connection %(connectionId) | %(sqlSingleLine)5.2 分页查询优化对于大数据量分页推荐使用优化后的分页插件Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); PaginationInnerInterceptor pagination new PaginationInnerInterceptor(DbType.MYSQL); pagination.setOptimizeJoin(true); // 优化JOIN查询 pagination.setMaxLimit(1000L); // 单页最大1000条 interceptor.addInnerInterceptor(pagination); return interceptor; }使用示例PageUser page new Page(1, 10); page.setSearchCount(false); // 大数据量时关闭count查询 userMapper.selectPage(page, queryWrapper);6. 常见问题排查6.1 类型转换异常处理当遇到java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long这类错误时通常是因为实体类中使用了基本类型long应改为包装类型LongMyBatis类型处理器配置不当建议检查TableId的type属性6.2 事务失效场景SpringBoot 3.x与MyBatis-Plus整合时常见的事务问题确保Transactional注解添加到实现类而非接口检查是否配置了EnableTransactionManagement避免同类内方法调用AOP代理失效6.3 缓存一致性方案对于使用MyBatis二级缓存的系统建议采用以下策略保证一致性CacheNamespace(implementation MybatisRedisCache.class, eviction MybatisRedisCache.class) public interface UserMapper extends BaseMapperUser { Override Options(flushCache Options.FlushCachePolicy.TRUE) int insert(User entity); CacheEvict(key user:#id) default void clearUserCache(Long id) { // 空方法仅用于触发缓存清除 } }配套的Redis缓存实现需要自定义public class MybatisRedisCache implements Cache { private final String id; private final RedisTemplateString, Object redisTemplate; public MybatisRedisCache(String id) { this.id id; this.redisTemplate (RedisTemplateString, Object) SpringContextHolder.getBean(redisTemplate); } Override public void putObject(Object key, Object value) { redisTemplate.opsForValue().set(key.toString(), value, 30, TimeUnit.MINUTES); } // 其他方法实现... }7. 升级迁移建议从MyBatis-Plus旧版升级到3.5.9时需要注意分页插件配置方式变化旧版PaginationInterceptor已废弃Lambda表达式API更加严格部分链式调用需要调整新版SQL注入器机制重构自定义SQL需要适配新接口对于从MyBatis原生迁移的项目建议分阶段进行先引入MyBatis-Plus但不修改原有Mapper逐步将XML中的SQL迁移到Wrapper构建最后替换BaseMapper中的通用方法实际项目中我通过这套技术栈将DAO层代码量减少了60%以上复杂查询的开发效率提升明显。特别是在动态数据源切换、多租户隔离这些场景下MyBatis-Plus 3.5.9提供的抽象封装让代码保持简洁的同时不失灵活性。
阅读完成 · 觉得有帮助?