news 2026/10/15 13:26:09

SpringBoot3与MyBatis-Plus 3.5.9整合实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot3与MyBatis-Plus 3.5.9整合实战指南

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> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.9</version> <!-- 排除旧版javax.persistence依赖 --> <exclusions> <exclusion> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加Jakarta EE兼容版本 --> <dependency> <groupId>jakarta.persistence</groupId> <artifactId>jakarta.persistence-api</artifactId> <version>3.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: 1

3. 核心功能实现详解

3.1 实体类与Mapper设计

MyBatis-Plus 3.5.9对注解体系进行了增强。推荐使用新版@TableName和@TableField:

@Data @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 BaseMapper<User> { // 自定义复杂查询 @Select("SELECT * FROM sys_user WHERE status = #{status}") List<User> selectByStatus(@Param("status") Integer status); }

3.2 动态SQL构建最佳实践

MyBatis-Plus 3.5.9的LambdaQueryWrapper在类型安全方面有显著提升:

public List<User> 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 List<User> 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节),自动填充则需要实现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()); } }

5. 性能优化与监控

5.1 SQL执行监控

结合p6spy实现完整SQL日志记录(生产环境需谨慎):

<dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency>

配置spy.properties:

module.log=com.p6spy.engine.logging.P6LogFactory appender=com.p6spy.engine.spy.appender.Slf4JLogger logMessageFormat=com.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; }

使用示例:

Page<User> 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"这类错误时,通常是因为:

  1. 实体类中使用了基本类型long,应改为包装类型Long
  2. MyBatis类型处理器配置不当,建议检查@TableId的type属性

6.2 事务失效场景

SpringBoot 3.x与MyBatis-Plus整合时常见的事务问题:

  1. 确保@Transactional注解添加到实现类而非接口
  2. 检查是否配置了@EnableTransactionManagement
  3. 避免同类内方法调用(AOP代理失效)

6.3 缓存一致性方案

对于使用MyBatis二级缓存的系统,建议采用以下策略保证一致性:

@CacheNamespace(implementation = MybatisRedisCache.class, eviction = MybatisRedisCache.class) public interface UserMapper extends BaseMapper<User> { @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 RedisTemplate<String, Object> redisTemplate; public MybatisRedisCache(String id) { this.id = id; this.redisTemplate = (RedisTemplate<String, 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时需要注意:

  1. 分页插件配置方式变化(旧版PaginationInterceptor已废弃)
  2. Lambda表达式API更加严格,部分链式调用需要调整
  3. 新版SQL注入器机制重构,自定义SQL需要适配新接口

对于从MyBatis原生迁移的项目,建议分阶段进行:

  1. 先引入MyBatis-Plus但不修改原有Mapper
  2. 逐步将XML中的SQL迁移到Wrapper构建
  3. 最后替换BaseMapper中的通用方法

实际项目中,我通过这套技术栈将DAO层代码量减少了60%以上,复杂查询的开发效率提升明显。特别是在动态数据源切换、多租户隔离这些场景下,MyBatis-Plus 3.5.9提供的抽象封装让代码保持简洁的同时不失灵活性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 13:26:05

Recharts动态资源图:秒级时间序列可视化实战

1. 项目概述&#xff1a;为什么一张“会呼吸”的资源图比静态图表更值得投入时间你有没有遇到过这样的场景&#xff1a;在某个后台系统里&#xff0c;盯着CPU使用率那条平直的绿线发呆&#xff0c;心里却清楚——这数据可能已经滞后三分钟了&#xff1b;或者刚切到内存监控页&a…

作者头像 李华
网站建设 2026/10/15 13:26:01

OpenClaw 总体架构技术拆解:从 Gateway 到 Agent 的 WebSocket 会话链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 13:25:58

context-mode:本地AI上下文工程实践与SQLite+FTS5+BM25落地指南

1. “context-mode”到底是什么&#xff1f;别被术语绕晕&#xff0c;它本质是让AI真正“听懂上下文”的工程实践最近在多个技术社区和开源项目里频繁看到“context-mode”这个词&#xff0c;尤其和MCP、SQLite、FTS5、BM25这些词绑在一起出现。很多人第一反应是——这又是个新…

作者头像 李华
网站建设 2026/10/15 13:25:43

DEM模块配置实战:从GDAL预处理到DaVinCi地形渲染与避坑指南

简介&#xff1a;《DaVinCi配置DEM模块的技术手册》是一份面向汽车电子AUTOSAR开发者的中文技术文档&#xff0c;围绕Vector DaVinCi工具链&#xff0c;系统讲解诊断事件管理器&#xff08;DEM&#xff09;的功能模块、API接口与DTC诊断事件配置方法&#xff0c;适用于在CAN等车…

作者头像 李华
网站建设 2026/10/15 13:25:33

context-mode:用SQLite+FTS5+BM25实现可解释AI上下文检索

1. “context-mode”到底是什么&#xff1f;别被术语绕晕&#xff0c;它本质是让AI真正“听懂上下文”的工程化开关 你最近在技术社区、开源项目文档甚至IDE插件配置里反复看到 context-mode 这个词&#xff0c;旁边还跟着 MCP、SQLite、FTS5、BM25 ——看起来像一串密码&…

作者头像 李华
网站建设 2026/10/15 13:25:23

Vue项目用HBuilderX打包App:白屏原因与排查指南

1. 从一个真实场景说起&#xff1a;为什么要把 Vue 项目塞进 App很多做前端的朋友都遇到过这种需求&#xff1a;手上已经有一个用 Vue CLI 3 搭起来的移动端 H5 项目&#xff0c;页面、路由、接口全都跑通了&#xff0c;浏览器里看着也挺顺眼&#xff0c;结果产品经理一句“咱们…

作者头像 李华