1. 问题现象与背景分析
最近在Spring Boot 3项目中整合MyBatis时,遇到了一个典型的启动报错:"Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"。这个错误看似简单,但背后涉及Spring Boot 3与MyBatis版本兼容性的深层问题。作为一名长期使用MyBatis的开发者,我完整经历了从问题出现到定位解决的整个过程,现将详细排查思路和解决方案分享给大家。
这个错误通常发生在Spring容器尝试初始化MyBatis相关Bean时,系统无法自动装配sqlSessionFactory或sqlSessionTemplate这两个关键组件。在Spring Boot 2.x时代,mybatis-spring-boot-starter会自动配置这些Bean,但在升级到Spring Boot 3后,原有的自动配置机制可能失效。
2. 错误根源深度解析
2.1 版本兼容性分析
问题的核心在于Spring Boot 3基于Spring Framework 6构建,而Spring 6进行了大量破坏性变更。关键变化包括:
- 移除了NestedIOException等类
- 重构了部分核心接口
- 修改了Bean初始化流程
MyBatis在2.x版本中仍依赖这些被移除的类,导致自动配置失败。具体表现为:
- MyBatisAutoConfiguration无法完成初始化
- SqlSessionFactoryBean创建失败
- 最终抛出缺少sqlSessionFactory/sqlSessionTemplate的异常
2.2 典型错误堆栈分析
完整的错误堆栈通常包含以下关键信息:
code复制Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'xxxMapper' defined in file [...]
Unsatisfied dependency expressed through bean property 'sqlSessionFactory'
Caused by: java.lang.IllegalArgumentException:
Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
这个堆栈清晰地表明:
- MyBatis Mapper接口依赖注入失败
- 根本原因是缺少SQL会话工厂或模板
- 问题出在Spring容器的依赖解析阶段
3. 完整解决方案
3.1 版本升级方案(推荐)
目前MyBatis已发布3.0.0+版本完全支持Spring Boot 3。在pom.xml中更新依赖:
xml复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version> <!-- 截至2024年最新稳定版 -->
</dependency>
同时确保Spring Boot父POM版本正确:
xml复制<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version> <!-- 使用最新稳定版 -->
</parent>
3.2 手动配置方案(临时方案)
如果暂时无法升级MyBatis版本,可以手动配置必要的Bean:
java复制@Configuration
public class MyBatisManualConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
// 其他必要配置
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
4. 配置检查清单
即使升级了版本,仍需检查以下配置项:
- 数据源配置是否正确
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/your_db
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
- MyBatis mapper扫描路径是否设置
yaml复制mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.model
- 主类是否添加Mapper扫描注解
java复制@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
5. 常见问题排查指南
5.1 版本冲突问题
使用mvn dependency:tree检查依赖树,确保没有旧版本残留。常见冲突包括:
- mybatis-spring 1.x与2.x混用
- 不同数据源starter冲突
5.2 配置加载顺序问题
如果使用自定义配置,确保配置类加载顺序正确:
- 数据源配置最先加载
- 然后是事务管理器
- 最后是MyBatis配置
5.3 多数据源特殊处理
对于多数据源场景,需要明确指定每个Mapper使用的SqlSessionTemplate:
java复制@Bean
@Primary
public SqlSessionTemplate primarySqlSessionTemplate(
@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
// 主数据源配置
}
@Bean
public SqlSessionTemplate secondarySqlSessionTemplate(
@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
// 次数据源配置
}
6. 最佳实践建议
-
版本管理策略:
- 保持Spring Boot与MyBatis starter版本同步升级
- 使用Spring Boot的BOM管理依赖版本
- 定期检查MyBatis官方发布说明
-
配置优化建议:
- 生产环境启用MyBatis二级缓存
- 合理设置mapper文件扫描路径
- 使用typeAliases简化XML配置
-
开发环境工具链:
- 安装MyBatis插件辅助开发
- 使用MyBatis Log Plugin查看实际执行SQL
- 配置热加载避免频繁重启
7. 高级调试技巧
当问题难以定位时,可以启用Spring Boot的debug模式:
yaml复制debug: true
这会输出完整的自动配置报告,其中MyBatis相关部分会显示:
- 哪些自动配置类被启用
- 哪些条件不满足导致配置跳过
- 最终生效的配置项
对于更深层次的问题,可以调试MyBatisAutoConfiguration类的执行过程,重点关注:
- @Conditional条件判断结果
- Bean创建过程中的异常
- 属性绑定是否正确
8. 性能优化方向
解决基础问题后,还可以进一步优化MyBatis性能:
- 批处理优化:
java复制SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
Mapper mapper = session.getMapper(Mapper.class);
for (Item item : items) {
mapper.insert(item);
}
session.commit();
} finally {
session.close();
}
- 缓存配置优化:
xml复制<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="localCacheScope" value="STATEMENT"/>
</settings>
- SQL执行监控:
java复制@Bean
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor interceptor = new PerformanceInterceptor();
interceptor.setMaxTime(1000); // SQL执行最大时长(ms)
interceptor.setFormat(true); // 格式化SQL
return interceptor;
}
9. 项目结构建议
合理的项目结构能减少配置问题:
code复制src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ ├── config/ # 配置类
│ │ ├── mapper/ # Mapper接口
│ │ └── model/ # 实体类
│ └── resources/
│ ├── mapper/ # XML映射文件
│ └── application.yml # 配置文件
关键原则:
- 保持Mapper接口与XML文件同名
- 集中管理配置类
- 合理规划包结构
10. 延伸问题解决方案
10.1 MyBatis-Plus兼容问题
如果使用MyBatis-Plus,需要对应升级:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version> <!-- 支持Spring Boot 3的最新版 -->
</dependency>
10.2 多模块项目配置
对于多模块项目,确保:
- 核心配置放在common模块
- 每个模块明确自己的mapper扫描路径
- 使用@MapperScan的basePackageClasses属性精确指定
10.3 测试环境配置
测试类需要特殊配置:
java复制@SpringBootTest
@AutoConfigureMybatis
public class MapperTest {
@Autowired
private TestMapper testMapper;
@Test
void testSelect() {
// 测试代码
}
}
11. 版本升级检查清单
从Spring Boot 2.x升级到3.x时,完整检查步骤:
- 备份现有项目
- 更新父POM版本
- 升级MyBatis starter
- 检查所有依赖的兼容性
- 测试核心功能
- 处理废弃API
- 验证自动配置
- 检查事务管理
- 测试性能表现
- 更新文档说明
12. 生产环境部署建议
- 连接池配置:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
- MyBatis生产配置:
yaml复制mybatis:
configuration:
default-fetch-size: 100
default-statement-timeout: 30
map-underscore-to-camel-case: true
aggressive-lazy-loading: false
- 监控集成:
- 集成Micrometer监控SQL执行
- 配置合理的告警阈值
- 定期检查慢查询日志
13. 开发者工具推荐
- IDEA插件:
- MyBatisX:Mapper接口与XML跳转
- Free MyBatis Tool:SQL生成与格式化
- MyBatis Log Plugin:控制台SQL日志
- 命令行工具:
- MyBatis Generator:代码生成
- MyBatis Migrations:数据库迁移
- 可视化工具:
- MyBatis-Plus的代码生成器UI
- DBeaver等数据库工具
14. 单元测试策略
- Mapper层测试:
java复制@MybatisTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testFindById() {
User user = userMapper.findById(1L);
assertNotNull(user);
}
}
- 事务测试:
java复制@SpringBootTest
@Transactional
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
void testCreateUser() {
User user = new User("test");
userService.create(user);
assertNotNull(user.getId());
}
}
- 集成测试:
java复制@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class UserControllerTest {
@LocalServerPort
private int port;
@Test
void testGetUser() {
// 使用TestRestTemplate测试完整链路
}
}
15. 性能监控方案
- Spring Boot Actuator集成:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
metrics:
tags:
application: ${spring.application.name}
- 自定义指标:
java复制@Repository
public class UserRepository {
private final Counter queryCounter;
public UserRepository(MeterRegistry registry) {
this.queryCounter = registry.counter("user.query.count");
}
public User findById(Long id) {
queryCounter.increment();
// 查询逻辑
}
}
- 告警规则示例:
- SQL执行时间 > 1s的告警
- 连接池使用率 > 90%的告警
- 慢查询数量突增告警
16. 安全加固建议
- SQL注入防护:
- 始终使用#{}而非${}参数绑定
- 启用MyBatis的SQL过滤
- 定期审计XML中的动态SQL
- 敏感数据处理:
java复制@Intercepts({
@Signature(type= ResultSetHandler.class,
method="handleResultSets",
args={Statement.class})
})
public class SensitiveDataInterceptor implements Interceptor {
// 实现数据脱敏逻辑
}
- 审计日志记录:
java复制@Bean
public AuditLogInterceptor auditLogInterceptor() {
return new AuditLogInterceptor();
}
17. 复杂查询优化
- 分页查询优化:
xml复制<select id="selectPage" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">
name LIKE #{name}
</if>
</where>
ORDER BY id DESC
LIMIT #{offset}, #{pageSize}
</select>
- 动态SQL技巧:
xml复制<update id="updateSelective">
UPDATE user
<set>
<if test="name != null">name=#{name},</if>
<if test="email != null">email=#{email},</if>
</set>
WHERE id=#{id}
</update>
- 结果集映射:
xml复制<resultMap id="userResult" type="User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<collection property="roles" ofType="Role">
<id property="id" column="role_id"/>
</collection>
</resultMap>
18. 事务管理实践
- 声明式事务配置:
java复制@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
- 事务传播行为:
java复制@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void createUser(User user) {
// 业务逻辑
}
}
- 事务隔离级别:
java复制@Transactional(isolation = Isolation.READ_COMMITTED)
public User getById(Long id) {
// 查询逻辑
}
19. 缓存集成方案
- 二级缓存配置:
xml复制<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>
- Redis缓存集成:
java复制@Bean
public Cache mybatisCache(RedisConnectionFactory connectionFactory) {
return new RedisCacheBuilder(connectionFactory)
.configuration(
new RedisCacheConfiguration()
.entryTtl(Duration.ofMinutes(30))
).build();
}
- 本地缓存策略:
java复制@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
configuration.setCacheEnabled(true);
configuration.setLocalCacheScope(LocalCacheScope.SESSION);
};
}
20. 扩展开发指南
- 自定义插件开发:
java复制@Intercepts({
@Signature(type= Executor.class,
method="update",
args={MappedStatement.class,Object.class})
})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 前置处理
Object result = invocation.proceed();
// 后置处理
return result;
}
}
- 类型处理器扩展:
java复制@MappedTypes(PhoneNumber.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class PhoneNumberTypeHandler extends BaseTypeHandler<PhoneNumber> {
// 实现类型转换逻辑
}
- 方言扩展:
java复制public class CustomDialect extends AbstractDialect {
@Override
public String getLimitString(String sql, int offset, int limit) {
// 实现分页方言
}
}
