1. SpringBoot与MyBatis整合概述
在现代Java企业级应用开发中,SpringBoot和MyBatis的组合堪称黄金搭档。SpringBoot通过自动配置和约定优于配置的原则简化了项目搭建过程,而MyBatis作为一款优秀的持久层框架,以其灵活的SQL编写方式和良好的性能表现赢得了开发者的青睐。
我初次接触这个组合是在2016年参与一个电商后台系统开发时。当时团队从传统的Spring+JPA架构转向SpringBoot+MyBatis,最大的感受就是开发效率的提升和SQL可控性的增强。特别是对于复杂查询场景,MyBatis的XML配置方式比JPA的Criteria API更加直观和易于维护。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与项目配置
2.1 依赖引入
在SpringBoot项目中集成MyBatis,首先需要在pom.xml中添加必要的依赖。以下是基于SpringBoot 2.7.x和MyBatis 3.5.x的推荐配置:
xml复制<dependencies>
<!-- SpringBoot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 其他可选依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>
注意:在实际项目中,我强烈推荐使用Druid连接池而非默认的HikariCP。Druid提供了更完善的监控功能,对于生产环境的问题排查非常有帮助。
2.2 配置文件设置
application.yml中的基本配置示例:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/your_db?useSSL=false&serverTimezone=UTC
username: root
password: yourpassword
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo.entity
configuration:
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 30
这里有几个关键配置点需要注意:
mapper-locations:指定MyBatis的XML映射文件位置type-aliases-package:实体类所在包,可以简化XML中的类型声明map-underscore-to-camel-case:自动将下划线命名转为驼峰命名
3. MyBatis核心组件详解
3.1 Mapper接口与XML映射
MyBatis的核心思想是将SQL与Java代码分离。我们首先定义一个Mapper接口:
java复制@Mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(@Param("id") Long id);
@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
// 复杂查询可以使用XML配置
List<User> findUsersByCondition(UserQuery query);
}
对应的XML映射文件(resources/mapper/UserMapper.xml):
xml复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
</resultMap>
<select id="findUsersByCondition" resultMap="userResultMap">
SELECT * FROM users
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
ORDER BY id DESC
</select>
</mapper>
3.2 动态SQL实践
MyBatis强大的动态SQL能力是其核心优势之一。以下是几种常见用法:
xml复制<!-- if条件判断 -->
<select id="findActiveUsers" resultType="User">
SELECT * FROM users
<where>
<if test="active != null">
AND active = #{active}
</if>
</where>
</select>
<!-- choose/when/otherwise -->
<select id="findUsers" resultType="User">
SELECT * FROM users
<where>
<choose>
<when test="ids != null and ids.size() > 0">
AND id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</when>
<otherwise>
AND 1=0
</otherwise>
</choose>
</where>
</select>
<!-- set用于更新 -->
<update id="updateUser">
UPDATE users
<set>
<if test="name != null">name = #{name},</if>
<if test="email != null">email = #{email},</if>
</set>
WHERE id = #{id}
</update>
经验分享:在实际项目中,我建议将复杂的动态SQL拆分到XML中,简单的CRUD可以使用注解方式。这样既保持了代码的整洁性,又充分利用了MyBatis的特性。
4. 高级特性与最佳实践
4.1 分页查询实现
在SpringBoot中实现分页有多种方式,以下是结合MyBatis和PageHelper的推荐方案:
- 添加PageHelper依赖:
xml复制<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
- 配置application.yml:
yaml复制pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
- 使用示例:
java复制public PageInfo<User> findUsersByPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectAll();
return new PageInfo<>(users);
}
4.2 多数据源配置
对于需要连接多个数据库的场景,可以按以下方式配置:
- 主数据源配置:
java复制@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDataSourceConfig {
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
@Primary
public DataSource primaryDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "primarySqlSessionFactory")
@Primary
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/primary/*.xml"));
return bean.getObject();
}
@Bean(name = "primarySqlSessionTemplate")
@Primary
public SqlSessionTemplate primarySqlSessionTemplate(
@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
- 从数据源配置类似,去掉@Primary注解即可。
4.3 事务管理
SpringBoot中MyBatis的事务管理非常简单:
java复制@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void createUser(User user) {
userMapper.insert(user);
// 其他数据库操作
}
}
重要提示:默认情况下,Spring的事务管理是基于AOP实现的,因此只有public方法上的@Transactional注解才会生效。同时,自调用(即同一个类中的方法调用)也不会触发事务。
5. 性能优化与常见问题
5.1 缓存配置
MyBatis提供了一级缓存和二级缓存机制:
- 一级缓存:默认开启,作用域为SqlSession级别
- 二级缓存:需要手动开启,作用域为Mapper级别
开启二级缓存:
xml复制<!-- 在XML映射文件中添加 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
然后在application.yml中配置:
yaml复制mybatis:
configuration:
cache-enabled: true
5.2 常见问题排查
-
SQL注入风险:
- 永远不要使用字符串拼接方式构造SQL
- 使用#{}而非${}进行参数替换
-
N+1查询问题:
- 使用
和 进行关联查询 - 或者使用@Select注解配合@Results定义结果映射
- 使用
-
分页插件冲突:
- 确保只使用一种分页方案
- 检查是否有多个PageHelper依赖
-
事务不生效:
- 检查方法是否为public
- 确认是否在同一个类中调用
- 检查异常类型是否被捕获而未抛出
6. 实际项目中的经验分享
在多年的项目实践中,我总结了以下几点经验:
-
SQL评审制度:
对于复杂的SQL语句,建议建立团队评审机制。我曾经遇到过一个看似简单的查询,因为缺少索引导致全表扫描,在数据量增长后成为性能瓶颈。 -
MyBatis Generator的使用:
对于标准CRUD操作,可以使用MyBatis Generator自动生成代码。但要注意:- 生成的Example类在实际项目中往往不够灵活
- 建议只生成基础的CRUD方法,复杂查询手动编写
-
监控与调优:
- 配置Druid的监控界面(/druid)
- 定期检查慢SQL日志
- 使用EXPLAIN分析关键查询的执行计划
-
版本升级注意事项:
从MyBatis 3.4.x升级到3.5.x时,我们遇到了一个坑:@Param注解的行为发生了变化。原先可以不写@Param注解直接使用参数名,但在新版本中必须显式声明。 -
与SpringBoot的版本兼容性:
下表展示了常见的版本组合:
| SpringBoot版本 | MyBatis Starter版本 | MyBatis版本 |
|---|---|---|
| 2.4.x | 2.1.x | 3.5.x |
| 2.5.x | 2.2.x | 3.5.x |
| 2.7.x | 2.2.x | 3.5.x |
| 3.0.x | 3.0.x | 3.5.x |
最后,我想强调的是,虽然SpringBoot+MyBatis的组合非常强大,但也要根据项目特点选择合适的架构。对于简单的CRUD应用,JPA可能是更好的选择;而对于需要复杂SQL和精细控制的场景,MyBatis无疑更具优势。
