1. 项目概述
SpringBoot整合Mybatis是Java后端开发中最经典的技术组合之一。作为一个常年混迹在企业级Java开发一线的老码农,我几乎在每个新项目启动时都要重新搭建这套环境。虽然现在各种脚手架工具能快速生成项目骨架,但真正理解每个配置项的作用和整合原理,才能在遇到问题时快速定位和解决。
这套组合之所以流行,是因为它完美结合了SpringBoot的"约定优于配置"理念和Mybatis的SQL灵活性。SpringBoot帮我们省去了大量繁琐的XML配置,而Mybatis则让我们能够精细控制每一条SQL语句。在实际项目中,这种组合既能保证开发效率,又能满足复杂业务场景下的SQL优化需求。
2. 核心需求解析
2.1 为什么选择这个技术组合
在企业级应用开发中,我们通常需要:
- 快速搭建可运行的项目骨架
- 对数据库操作有精细控制能力
- 良好的事务管理支持
- 与现有Spring生态无缝集成
SpringBoot+Mybatis正好满足这些需求。相比JPA,Mybatis给了开发者更多SQL层面的控制权,这对需要复杂查询和性能优化的场景特别重要。我在电商系统中处理过大量需要手动优化SQL的案例,这时候Mybatis的优势就体现出来了。
2.2 典型应用场景
这套组合特别适合以下场景:
- 需要复杂SQL查询的报表系统
- 对数据库性能敏感的交易系统
- 遗留数据库迁移项目(需要兼容已有SQL)
- 需要同时使用ORM和原生SQL的混合场景
3. 环境准备与基础配置
3.1 创建SpringBoot项目
推荐使用Spring Initializr创建项目骨架:
curl https://start.spring.io/starter.zip \ -d dependencies=web,mybatis \ -d language=java \ -d type=maven-project \ -d javaVersion=17 \ -d artifactId=mybatis-demo \ -o mybatis-demo.zip关键依赖说明:
spring-boot-starter-web: 提供Web MVC支持mybatis-spring-boot-starter: Mybatis官方提供的SpringBoot集成包数据库驱动: 根据使用的数据库选择,如MySQL需要添加mysql-connector-java
3.2 数据库配置
在application.properties中配置数据源:
# 数据源配置 spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # Mybatis配置 mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.example.demo.entity注意:生产环境密码应该加密存储,可以使用Jasypt等工具
4. Mybatis集成核心实现
4.1 实体类与Mapper接口
首先定义实体类:
@Data public class User { private Long id; private String name; private Integer age; private String email; }然后创建Mapper接口:
@Mapper public interface UserMapper { @Select("SELECT * FROM user WHERE id = #{id}") User findById(Long id); @Insert("INSERT INTO user(name, age, email) VALUES(#{name}, #{age}, #{email})") @Options(useGeneratedKeys = true, keyProperty = "id") int insert(User user); @Update("UPDATE user SET name=#{name}, age=#{age}, email=#{email} WHERE id=#{id}") int update(User user); @Delete("DELETE FROM user WHERE id=#{id}") int delete(Long id); }4.2 XML映射文件配置
对于复杂SQL,推荐使用XML方式:
<!-- src/main/resources/mapper/UserMapper.xml --> <mapper namespace="com.example.demo.mapper.UserMapper"> <resultMap id="BaseResultMap" type="User"> <id column="id" property="id" /> <result column="name" property="name" /> <result column="age" property="age" /> <result column="email" property="email" /> </resultMap> <select id="selectComplexQuery" resultMap="BaseResultMap"> SELECT * FROM user <where> <if test="name != null"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="minAge != null"> AND age >= #{minAge} </if> </where> ORDER BY id DESC </select> </mapper>4.3 服务层实现
典型的服务层代码结构:
@Service @RequiredArgsConstructor public class UserService { private final UserMapper userMapper; public User getUserById(Long id) { return userMapper.findById(id); } public List<User> searchUsers(String name, Integer minAge) { return userMapper.selectComplexQuery(name, minAge); } @Transactional public void updateUser(User user) { if (userMapper.update(user) == 0) { throw new RuntimeException("更新失败"); } } }5. 高级特性与优化
5.1 分页插件集成
Mybatis分页推荐使用PageHelper:
// 添加依赖 implementation 'com.github.pagehelper:pagehelper-spring-boot-starter:1.4.6' // 使用示例 public PageInfo<User> getUsersByPage(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); List<User> users = userMapper.selectAll(); return new PageInfo<>(users); }5.2 多数据源配置
对于需要连接多个数据库的场景:
@Configuration @MapperScan(basePackages = "com.example.demo.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory") public class PrimaryDataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/primary/*.xml")); return sessionFactory.getObject(); } @Bean public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }5.3 动态数据源路由
基于AbstractRoutingDataSource实现:
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceKey(); } } // 使用AOP切换数据源 @Around("@annotation(dataSource)") public Object around(ProceedingJoinPoint point, DataSource dataSource) throws Throwable { DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value()); try { return point.proceed(); } finally { DynamicDataSourceContextHolder.clearDataSourceKey(); } }6. 性能优化实践
6.1 SQL执行监控
集成p6spy监控真实SQL:
# application.properties spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/demo spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver # spy.properties module.log=com.p6spy.engine.logging.P6LogFactory filter=true exclude=spring.datasource6.2 二级缓存配置
启用Mybatis二级缓存:
<!-- 在mapper.xml中 --> <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/> <!-- 配置Redis缓存 --> @Bean public Cache mybatisRedisCache() { return new MybatisRedisCache("mybatisCache"); }6.3 批量操作优化
使用BatchExecutor提升批量操作性能:
@Autowired private SqlSessionFactory sqlSessionFactory; public void batchInsert(List<User> users) { try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper mapper = session.getMapper(UserMapper.class); for (User user : users) { mapper.insert(user); } session.commit(); } }7. 常见问题排查
7.1 映射文件找不到
症状:Invalid bound statement (not found)解决方案:
- 检查
mybatis.mapper-locations配置路径是否正确 - 确保XML文件在resources目录下对应路径
- 检查Mapper接口和XML的namespace是否一致
7.2 事务不生效
可能原因:
- 方法不是public
- 异常类型不是RuntimeException
- 同类方法调用(this.method())
- 数据源配置不正确
7.3 分页插件失效
排查步骤:
- 检查PageHelper.startPage()是否在查询前调用
- 确认没有在startPage和查询之间执行其他SQL
- 检查是否有多个SqlSessionFactory导致拦截器未生效
8. 生产环境最佳实践
8.1 安全规范
SQL注入防护:
- 永远不要拼接SQL
- 使用#{}而不是${}
- 对用户输入进行严格校验
敏感数据保护:
- 数据库密码加密
- 生产环境关闭SQL日志
- 使用Mybatis的TypeHandler处理加密字段
8.2 性能规范
查询优化:
- 避免SELECT *
- 合理使用索引提示
- 大数据量查询使用流式处理
连接池配置:
spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.idle-timeout=30000
8.3 监控指标
关键监控项:
- SQL执行时间
- 慢查询统计
- 连接池使用情况
- 缓存命中率
集成Prometheus监控示例:
@Bean public CollectorRegistry mybatisMetrics(DataSource dataSource) { CollectorRegistry registry = new CollectorRegistry(); new DataSourceExports(dataSource).register(registry); return registry; }9. 测试策略
9.1 单元测试
使用Mybatis Test:
@MybatisTest @AutoConfigureTestDatabase(replace = Replace.NONE) class UserMapperTest { @Autowired private UserMapper userMapper; @Test void testFindById() { User user = userMapper.findById(1L); assertNotNull(user); } }9.2 集成测试
SpringBoot Test示例:
@SpringBootTest @Transactional class UserServiceTest { @Autowired private UserService userService; @Test void testUpdateUser() { User user = new User(); user.setId(1L); user.setName("newName"); assertDoesNotThrow(() -> userService.updateUser(user)); } }9.3 性能测试
使用JMeter测试建议:
- 模拟并发用户逐渐增加
- 监控数据库连接池使用情况
- 关注GC日志和内存变化
10. 扩展与演进
10.1 与Spring Data JPA混合使用
有些场景适合混合使用:
public interface UserRepository extends JpaRepository<User, Long> { // JPA方式 List<User> findByName(String name); // Mybatis方式 @Query(nativeQuery = true, value = "SELECT * FROM user WHERE age > :age") List<User> findByAgeGreaterThan(@Param("age") int age); }10.2 迁移到Mybatis-Plus
平滑迁移步骤:
- 添加Mybatis-Plus依赖
- 逐步替换Mapper接口为BaseMapper
- 保留复杂SQL的XML配置
- 利用LambdaQueryWrapper简化代码
10.3 云原生适配
Kubernetes部署建议:
- 使用ConfigMap管理不同环境的配置
- 合理设置Pod的resources限制
- 使用Sidecar模式管理数据库连接
- 实现健康检查接口:
@GetMapping("/health") public ResponseEntity<String> healthCheck() { try { userMapper.count(); return ResponseEntity.ok("UP"); } catch (Exception e) { return ResponseEntity.status(503).body("DOWN"); } }在实际项目开发中,SpringBoot整合Mybatis的这套组合给我带来了极高的开发效率和足够的灵活性。特别是在处理复杂业务逻辑时,能够自由编写优化过的SQL语句,同时享受Spring的事务管理和依赖注入等特性。对于刚接触这套技术栈的开发者,建议从简单的注解方式开始,逐步过渡到XML配置,最后再探索高级特性如插件开发和多数据源管理等。