从零搭SpringBootMyBatis我差点被这些坑劝退版本冲突是最常见的坑而且它总是悄无声息地潜伏在pom.xml里。如果你直接复制网上三年前的教程大概率会遇到DataSource配置失败、Mapper扫描不到、分页插件报错等问题。我经历过一次项目重构花了整整一个下午排查结果只是spring-boot-starter-parent和mybatis-spring-boot-starter的版本不匹配。现在官方推荐的做法是SpringBoot 2.x对应mybatis-spring-boot-starter 2.xSpringBoot 3.x对应3.x千万别混用。另外数据库驱动也得跟上——mysql-connector-java在8.0以后改名为mysql-connector-jcom.mysql.cj.jdbc.Driver取代了旧驱动很多老文章还在用com.mysql.jdbc.Driver驱动类名写错直接导致启动失败而且报错信息极其隐晦只提示“cannot load driver class”。依赖引入的陷阱多一个少一个都是灾难最基本的依赖清单spring-boot-starter-web、mybatis-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/setterSQL查询结果映射全是null。application.yml配置看似简单实则暗藏玄机数据源配置是第一步也是最容易出错的地方。新版本Spring Boot要求spring.datasource.url而不是jdbc-url很多新手从别的框架转过来会写成jdbc-url导致HikariCP找不到连接。标准写法spring: datasource: url: jdbc:mysql://localhost:3306/demo?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.DriverMyBatis的专属配置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调用方法BB上有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打印到控制台在高并发下直接拖垮性能。应该改用logback或log4j2按级别过滤或者只在debug模式下开启。连接池HikariCP配置maximum-pool-size建议根据服务器CPU核心数和业务类型调节一般10~20足够minimum-idle设置5左右避免频繁创建连接。开启cache-enabledtrue可以让MyBatis使用二级缓存但注意脏读问题比如一个查询把结果缓存了另一个更新操作却没有清缓存下次读取的还是旧数据。建议在Mapper XML里用cache标签配置缓存策略或者干脆在业务层用Redis做缓存。还有批量插入如果你的业务需要一次插入几千条数据别用for循环单条插要使用SqlSession的批量模式或者用MyBatis-Plus的saveBatch方法。批量模式性能提升数十倍因为减少了与数据库的网络往返次数。总结没有亲手踩过的坑都不算真正掌握回顾整个整合过程最让人崩溃的往往不是复杂的逻辑而是那些看似不起眼的配置细节。比如驱动类名的大小写、XML namespace的拼写、Transactional的内部调用失效、PageHelper的版本冲突、驼峰映射的配置位置……它们每一个都可能让你浪费半天甚至一天时间。我建议你在新项目中先把一个最简单的单表CRUD跑通再逐步增加分页、多数据源、缓存等功能。每加一个功能就进行一次完整的启动测试不要等所有代码写完再debug。另外保持依赖版本的一致性非常关键推荐使用Spring Initializr生成项目骨架或者参考官方文档的版本对应表。记住一点任何看似“诡异”的错误都可以在控制台堆栈信息的第三行找到答案——学会阅读异常堆栈比盲目百度更有效。如果你现在正准备把一个旧项目从SSM迁移到SpringBootMyBatis那就从我最开始提到的版本冲突开始排查吧最好把pom.xml里所有第三方依赖的版本都检查一遍确保没有遗留的旧版jar。这些坑我替你先踩过了希望能帮你节省宝贵的开发时间。