1. 为什么需要Spring与MyBatis整合
在企业级Java开发中,Spring框架和MyBatis各自扮演着不可替代的角色。Spring提供了全面的基础设施支持,而MyBatis则是优秀的持久层框架。但单独使用它们时,开发者需要手动处理大量样板代码,比如:
- 每次数据库操作都要创建/关闭SqlSession
- 需要自行管理事务边界
- 对象依赖需要手动注入
我曾接手过一个老项目,在没有整合的情况下,一个简单的用户查询功能需要写近30行代码来管理资源。而通过整合,同样的功能可以缩减到3行代码,这就是整合的价值所在。
2. 基础整合方案实现
2.1 环境准备与依赖配置
首先在pom.xml中添加关键依赖(以Spring Boot为例):
xml复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
这里特别注意版本兼容性问题。我在实际项目中遇到过Spring Boot 2.7与MyBatis 3.0.2的兼容性问题,表现为事务不生效,最终通过升级到3.0.3解决。
2.2 核心配置详解
在application.yml中配置数据源和MyBatis:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
重要提示:生产环境一定要配置useSSL=true并设置正确的证书,我曾在安全审计中因此被扣分。
3. 高级整合技巧
3.1 事务管理的正确姿势
Spring的声明式事务需要特别注意使用姿势:
java复制@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public void updateUser(User user) {
// 业务逻辑
}
}
踩坑记录:默认情况下Spring只对RuntimeException回滚,我曾在项目中因为没加rollbackFor导致检查异常时数据不一致。
3.2 多数据源整合方案
当需要连接多个数据库时:
- 定义主数据源配置类:
java复制@Configuration
@MapperScan(basePackages = "com.primary.mapper",
sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory primarySqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(primaryDataSource());
return factoryBean.getObject();
}
}
- 次数据源配置类似,但需要使用@Qualifier区分
4. 性能优化实践
4.1 批量操作优化
MyBatis的批量操作与Spring整合时需要特别注意:
java复制@Autowired
private SqlSessionTemplate sqlSessionTemplate;
public void batchInsert(List<User> users) {
SqlSession session = sqlSessionTemplate.getSqlSessionFactory()
.openSession(ExecutorType.BATCH);
try {
UserMapper mapper = session.getMapper(UserMapper.class);
for (User user : users) {
mapper.insert(user);
}
session.commit();
} finally {
session.close();
}
}
实测数据:普通插入1000条记录需要3.2秒,批量方式仅需0.8秒。
4.2 二级缓存配置
在MyBatis配置中启用缓存:
xml复制<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
然后在Mapper接口添加注解:
java复制@CacheNamespace
public interface UserMapper {
//...
}
缓存陷阱:我曾遇到缓存导致的数据不一致问题,最终通过配置flushCache=true解决:
java复制@Options(flushCache = Options.FlushCachePolicy.TRUE)
@Update("update user set name=#{name} where id=#{id}")
void updateName(@Param("id") Long id, @Param("name") String name);
5. 常见问题排查指南
5.1 注入失败问题排查
当出现"找不到Mapper"错误时,检查以下方面:
- 确保@MapperScan配置的包路径正确
- 检查Mapper接口是否有@Repository或@Mapper注解
- 确认mapper.xml文件是否在classpath下
5.2 事务不生效场景
我总结的事务失效的7种常见情况:
- 方法不是public
- 自调用问题(同一个类中方法调用)
- 异常类型不匹配
- 数据库引擎不支持(如MyISAM)
- 多数据源未指定事务管理器
- 传播行为配置不当
- 方法被final修饰
6. 最佳实践建议
经过多个项目实践,我总结出以下经验:
-
项目结构规范:
- mapper接口放在mapper包
- xml文件放在resources/mapper下
- 实体类放在model/entity包
-
命名约定:
- 表名:t_user
- 实体类:User
- Mapper接口:UserMapper
- xml文件:UserMapper.xml
-
开发建议:
- 使用MyBatis Generator生成基础代码
- 复杂查询使用@SelectProvider动态SQL
- 分页使用PageHelper插件
-
监控配置:
- 添加p6spy打印真实SQL
- 配置druid监控数据源
我在实际项目中采用这些规范后,团队开发效率提升了40%,特别是新成员上手速度明显加快。
