当前位置: 首页 > news >正文

SpringBoot整合MyBatis的详细步骤与踩坑记录

从零搭SpringBoot+MyBatis,我差点被这些坑劝退

版本冲突是最常见的坑,而且它总是悄无声息地潜伏在pom.xml里。如果你直接复制网上三年前的教程,大概率会遇到DataSource配置失败、Mapper扫描不到、分页插件报错等问题。我经历过一次项目重构,花了整整一个下午排查,结果只是spring-boot-starter-parentmybatis-spring-boot-starter的版本不匹配。现在官方推荐的做法是:SpringBoot 2.x对应mybatis-spring-boot-starter 2.x,SpringBoot 3.x对应3.x,千万别混用。另外,数据库驱动也得跟上——mysql-connector-java在8.0以后改名为mysql-connector-j,com.mysql.cj.jdbc.Driver取代了旧驱动,很多老文章还在用com.mysql.jdbc.Driver驱动类名写错直接导致启动失败,而且报错信息极其隐晦,只提示“cannot load driver class”。

依赖引入的陷阱:多一个少一个都是灾难

最基本的依赖清单:spring-boot-starter-webmybatis-spring-boot-starter、数据库驱动(比如mysql-connector-j)、连接池(HikariCP默认自带),外加一个spring-boot-starter-test。很多人会忘记加spring-boot-starter-jdbc,其实mybatis-spring-boot-starter已经传递依赖了DataSource相关类,但如果你要手动配置事务管理器或JdbcTemplate,那个依赖可能缺了。另一个高频失误是忘记排除旧版驱动中的Logging冲突,比如Slf4J和Logback的实现冲突,启动时控制台疯狂输出“Failed to load class org.slf4j.impl.StaticLoggerBinder”。解决办法是在依赖里排除冲突项,或者统一使用Spring Boot指定的日志版本。如果你用Lombok,记得加上annotationProcessorPaths配置,否则编译时无法生成getter/setter,SQL查询结果映射全是null。

application.yml配置:看似简单,实则暗藏玄机

数据源配置是第一步,也是最容易出错的地方。新版本Spring Boot要求spring.datasource.url而不是jdbc-url,很多新手从别的框架转过来会写成jdbc-url,导致HikariCP找不到连接。标准写法:

spring: datasource: url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

MyBatis的专属配置:

mybatis: mapper-locations: classpath:mapper/.xml type-aliases-package: com.example.demo.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

这里有个经典踩坑:map-underscore-to-camel-case明明设为true,但查询结果中user_name字段还是映射不到userName属性。原因在于MyBatis的configuration块需要放在mybatis.configuration下,而有些教程写成mybatis.configuration.map-underscore-to-camel-case,这没问题;但如果你不小心写成了mybatis.configuration-properties(注意是复数),配置就完全无效。而且,当你使用PageHelper或MyBatis-Plus时,它们的配置项与原生配置可能互相覆盖,建议分开定义。

Mapper扫描:@MapperScan到底放哪?包路径怎么写?

有两种方式注册Mapper:一是每个Mapper接口上加@Mapper注解;二是在任意@Configuration类上加@MapperScan("com.example.demo.mapper")第一种方式繁琐且容易遗漏,第二种更推荐。但坑在于扫描路径必须精确到Mapper接口所在的包,如果你扫描了父包但子包有内部类或无用接口,可能会注入非预期的Bean。更隐蔽的坑是:如果你同时使用了@Mapper@MapperScan,且扫描范围不包含某个Mapper,那个Mapper依然会生效(因为@Mapper单独注册了),但如果你扫描范围包含了多个包,且Mapper接口跨越多个模块,可能会引发Bean重复定义的错误。另外,@MapperScan必须放在Spring Boot主类或配置类上,而且确保该类能被@ComponentScan扫描到,否则扫描不生效。有个同事把@MapperScan写在一个普通的Service类上,结果项目启动后报“Invalid bound statement (not found)”,排查了半天。

XML映射文件:namespace写错等于白写

Mapper XML文件放在classpath:mapper/下,命名规范:UserMapper.xml对应UserMapper接口。XML的namespace必须与Mapper接口的全限定名完全一致,否则MyBatis在运行时找不到SQL语句。很多人复制粘贴时忘记改包名,结果不断报“Invalid bound statement (not found)”。此外,resultMap中的column和property要注意大小写匹配,开启驼峰映射后,column的user_name会自动转成userName,但如果你手动定义的resultMap覆盖了默认映射,驼峰转换就不再生效,必须手动指定property。另一个常见问题:使用#{}和${}的场景混淆。#{}会预编译并防止SQL注入,适合传递参数;${}直接拼接,适合动态表名或列名。但如果你用${}传递用户输入的参数,就是给自己挖坑,比如order by ${sortColumn},攻击者可以通过sortColumn传入恶意SQL片段。我在生产环境遇到过有人传1;DROP TABLE users --,还好用了#{}仅做参数,不直接拼接。

事务管理:@Transactional不生效的几种情况

Spring声明式事务基于AOP,只有被Spring管理的Bean上的@Transactional才有效。如果你在Controller的方法上直接加@Transactional,但Controller本身没有被代理(比如不是Spring Bean,或者方法被final修饰),事务就不会开启。另一个常见情况:在同一个类中,方法A调用方法B,B上有@Transactional,此时事务不会生效,因为这是内部调用,没有经过代理对象。解决方案是:要么把B方法放到另一个Service中,要么通过AopContext.currentProxy()获取代理对象。还有,事务的管理默认只对RuntimeException回滚,如果抛的是Checked Exception(比如IOException),事务不会回滚。需要指定@Transactional(rollbackFor = Exception.class)。多数据源场景下,@Transactional默认使用主数据源的事务管理器,如果你操作了从库,事务会跨数据源失效。记得为每个数据源配置独立的PlatformTransactionManager,并在@Transactional中指定transactionManager名称

分页插件PageHelper:版本必须和MyBatis对齐

PageHelper是常用的分页插件,但它的版本要求非常严格。mybatis-spring-boot-starter 2.x必须配合PageHelper 5.x,且需要对page-helper-spring-boot-starter的版本号格外留意。如果你引入的是PageHelper 4.x,启动时可能出现“java.lang.AbstractMethodError”或者分页参数无法传递。更隐蔽的是:在整合PageHelper后,如果你在Mapper方法里手动写了select count()的SQL,并且使用了PageHelper.startPage(),那么PageHelper会尝试重写你的SQL,但如果你SQL中有复杂子查询或UNION,重写可能失败,导致count查询异常。解决办法:使用PageHelper.startPage(pageNum, pageSize)后,立即跟着一个MyBatis查询,中间不能有其他数据库操作。另外,分页结果一定要用PageInfo包装,否则你可能拿到的是原始List,丢失了总记录数等分页信息。

多数据源:动态切换的坑与解决方案

当项目需要连接多个数据库(比如主库写、从库读),动态数据源是常见方案。但实现动态数据源本质是继承AbstractRoutingDataSource,并重写determineCurrentLookupKey方法。这个过程中有几点容易踩坑:第一,必须确保在数据库操作之前通过ThreadLocal设置好当前要用的数据源key,而且事务开启后就不能再切换了(因为事务绑定了一个Connection)。第二,@Transactional注解下的方法,如果需要在方法内部切换数据源,必须将事务传播行为设为NOT_SUPPORTED或REQUIRES_NEW,否则Connection会被复用,导致用的仍是旧数据源。第三,连接池配置要分别给每个数据源设置不同的HikariCP配置,比如连接数、超时时间,不能共用。我见过一个生产事故:主从库用了同一个连接池配置,结果从库最大连接数被主库流量占满,导致从库查询超时。

生产环境调优:日志、连接池、缓存与批量操作

上线后,第一件事:关闭MyBatis的SQL日志输出log-impl设为STDOUT_LOGGING会让所有SQL打印到控制台,在高并发下直接拖垮性能。应该改用logbacklog4j2按级别过滤,或者只在debug模式下开启。连接池HikariCP配置:maximum-pool-size建议根据服务器CPU核心数和业务类型调节,一般10~20足够;minimum-idle设置5左右,避免频繁创建连接。开启cache-enabled=true可以让MyBatis使用二级缓存,但注意脏读问题,比如一个查询把结果缓存了,另一个更新操作却没有清缓存,下次读取的还是旧数据。建议在Mapper XML里用<cache>标签配置缓存策略,或者干脆在业务层用Redis做缓存。还有批量插入:如果你的业务需要一次插入几千条数据,别用for循环单条插,要使用SqlSession的批量模式,或者用MyBatis-PlussaveBatch方法。批量模式性能提升数十倍,因为减少了与数据库的网络往返次数。

总结:没有亲手踩过的坑,都不算真正掌握

回顾整个整合过程,最让人崩溃的往往不是复杂的逻辑,而是那些看似不起眼的配置细节。比如驱动类名的大小写、XML namespace的拼写、@Transactional的内部调用失效、PageHelper的版本冲突、驼峰映射的配置位置……它们每一个都可能让你浪费半天甚至一天时间。我建议你在新项目中,先把一个最简单的单表CRUD跑通,再逐步增加分页、多数据源、缓存等功能。每加一个功能,就进行一次完整的启动测试,不要等所有代码写完再debug。另外,保持依赖版本的一致性非常关键,推荐使用Spring Initializr生成项目骨架,或者参考官方文档的版本对应表。记住一点:任何看似“诡异”的错误,都可以在控制台堆栈信息的第三行找到答案——学会阅读异常堆栈,比盲目百度更有效。如果你现在正准备把一个旧项目从SSM迁移到SpringBoot+MyBatis,那就从我最开始提到的版本冲突开始排查吧,最好把pom.xml里所有第三方依赖的版本都检查一遍,确保没有遗留的旧版jar。这些坑,我替你先踩过了,希望能帮你节省宝贵的开发时间。

http://www.jsqmd.com/news/1171386/

相关文章:

  • A-Ops未来路线图:2024年新功能预测与技术演进方向
  • B3959 [GESP202403 四级] 做题 题解
  • 开放式耳机哪个牌子好用又实惠?2026年开放式耳机性价比高的品牌前十名
  • 蓝牙5.0+ 多设备连接实战:Mac/Windows 实测7设备并发稳定性与延迟
  • 2026年 广州纸箱厂家推荐榜单:天河彩箱/淘宝纸箱/东莞拉连纸箱/物流纸箱/出口纸箱/特硬纸箱/过膜纸箱/纸盒/彩盒/飞机盒/超大纸箱源头工厂优选 - 甄选服务推荐
  • C++核心基础与内存管理:从变量作用域到智能指针的实战指南
  • AI图像生成中的形象漂移:从提示词冲突到精准控制
  • 大模型上下文窗口爆了怎么办
  • 高压隔离技术:ISOM8710与PIC18F25K42应用指南
  • 20分钟将Scikit-learn模型部署为带鉴权的REST API
  • Java代码跑得慢?Oracle临时表空间早该收拾了,别让它拖死你
  • Linux firewalld 与 Windows Defender 防火墙:4个关键端口(8080/3306/9200/6379)配置对比
  • Vivado xvlog 与 VSCode 集成:解决5类常见Verilog语法报错
  • AlphaDev:AI在汇编层发现新排序算法的原理与实践
  • MongoDB 删除操作深度对比:deleteOne、deleteMany、remove 与 drop 的 4 种场景选择指南
  • 布尔盲注 vs 时间盲注:5个关键场景下的选择策略与实战对比
  • 惠州巴彦县亨得利官方名表服务中心电话公示(2026年7月最新) - 亨得利官方
  • UML 2.5 实战:从需求到类图的 3 步建模法(附公务员招考系统案例)
  • ROS 导航参数调优:AMCL 与 move_base 10 个关键参数解析与避坑指南
  • AI吃播生成技术:从数字人到视频合成的完整实践指南
  • Wireshark 4.2 与 tcpdump 对比:3种场景下的PCAP文件分析与过滤效率实测
  • Seedance2.0与即梦AI5.0:本地化AI内容创作完整解决方案
  • 网盘直链下载助手:告别限速困扰,一键获取真实下载地址的终极解决方案
  • 基于STM32与NAU8224的高效D类音频放大器设计
  • 百达翡丽中国官方售后服务中心|服务热线及完整维修地址权威信息通告(2026年7月最新) - 百达翡丽服务中心
  • 用集合思维理解Pandas DataFrame操作原理
  • Git 2.45 实战:3步命令同步Fork仓库新增分支,避免删除重Fork
  • Cisco Catalyst 2960 交换机实战:从用户模式到VLAN配置的7步完整流程
  • 2026 年现阶段藤县口碑好的耐候钢装饰板公司深度解析,锈迹斑斑反而更值钱?这3个细节暴露了你的无知 - 领域鉴赏官
  • 知识图谱 vs 传统知识库:从 Google V3 API 看 3 大核心差异与选型