1. 项目概述
在Java企业级开发领域,SpringBoot和MyBatis的组合堪称黄金搭档。最近我在一个新项目中采用了SpringBoot3+MyBatis的方案,整个过程踩过不少坑也积累了不少实战经验。本文将完整呈现从零开始整合这两个框架的全过程,包含那些官方文档里不会告诉你的配置技巧和性能优化手段。
SpringBoot3作为Spring家族的最新成员,带来了诸多改进:支持Java17+、更快的启动速度、更好的GraalVM原生镜像支持。而MyBatis作为老牌ORM框架,在复杂SQL处理方面依然保持着不可替代的优势。两者的结合既能享受SpringBoot的便利,又能保留MyBatis的灵活性。
2. 环境准备与项目搭建
2.1 开发环境配置
推荐使用以下环境组合:
- JDK17+(SpringBoot3最低要求)
- Maven3.6+或Gradle7.x
- IDE选择IntelliJ IDEA(对SpringBoot支持最好)
- 数据库选用MySQL8.x或PostgreSQL14+
注意:SpringBoot3不再支持JDK8,如果必须使用JDK8可以考虑SpringBoot2.7.x版本
2.2 初始化SpringBoot项目
使用Spring Initializr创建项目时关键依赖选择:
- Spring Web(如果要做Web应用)
- MyBatis Framework
- 对应数据库驱动(如MySQL Driver)
pom.xml中需要显式声明MyBatis-Spring-Boot-Starter版本:
xml复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
2.3 数据库连接配置
application.yml典型配置示例:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 15
minimum-idle: 5
idle-timeout: 30000
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo.entity
configuration:
map-underscore-to-camel-case: true
3. MyBatis核心组件整合
3.1 实体类与Mapper接口设计
实体类示例(使用Lombok简化代码):
java复制@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
}
Mapper接口设计要点:
java复制@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User findById(Long id);
@Insert("INSERT INTO user(username,email) VALUES(#{username},#{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Update("UPDATE user SET username=#{username}, email=#{email} WHERE id=#{id}")
int update(User user);
@Delete("DELETE FROM user WHERE id=#{id}")
int delete(Long id);
}
3.2 XML映射文件编写
对于复杂SQL,推荐使用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="username" column="username"/>
<result property="email" column="email"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="selectByCondition" resultMap="userResultMap">
SELECT * FROM user
<where>
<if test="username != null">
AND username LIKE CONCAT('%',#{username},'%')
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>
3.3 事务管理配置
SpringBoot默认已经配置了事务管理,只需在Service层添加注解:
java复制@Service
@RequiredArgsConstructor
public class UserService {
private final UserMapper userMapper;
@Transactional
public void createUser(User user) {
userMapper.insert(user);
// 其他业务操作...
}
}
4. 高级特性与性能优化
4.1 动态SQL实践
MyBatis强大的动态SQL能力:
xml复制<update id="updateSelective" parameterType="User">
UPDATE user
<set>
<if test="username != null">username=#{username},</if>
<if test="email != null">email=#{email},</if>
</set>
WHERE id=#{id}
</update>
4.2 分页查询实现
使用PageHelper实现物理分页:
- 添加依赖:
xml复制<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
- 使用示例:
java复制public PageInfo<User> queryByPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectAll();
return new PageInfo<>(users);
}
4.3 二级缓存配置
启用MyBatis二级缓存:
- 在配置文件中开启:
yaml复制mybatis:
configuration:
cache-enabled: true
- 在Mapper.xml中配置:
xml复制<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
5. 常见问题排查
5.1 映射问题排查
常见错误现象:
- 查询结果字段为null
- 插入操作主键未回填
解决方案:
- 检查实体类字段名与数据库列名是否匹配
- 确认是否开启了驼峰命名转换
- 检查@Options注解配置是否正确
5.2 事务失效场景
事务不生效的常见原因:
- 方法不是public
- 自调用问题(调用同类中的其他方法)
- 异常类型不是RuntimeException且未指定rollbackFor
- 数据库引擎不支持事务(如MyISAM)
5.3 性能优化建议
- 批量操作使用
<foreach>标签:
xml复制<insert id="batchInsert">
INSERT INTO user(username, email) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.username}, #{item.email})
</foreach>
</insert>
- 复杂查询使用
<sql>片段复用:
xml复制<sql id="baseColumn">id,username,email,create_time</sql>
<select id="selectAll" resultMap="userResultMap">
SELECT <include refid="baseColumn"/> FROM user
</select>
- 连接池配置建议:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: ${DB_POOL_SIZE:10}
connection-timeout: 30000
leak-detection-threshold: 60000
6. 测试与验证
6.1 单元测试编写
使用SpringBootTest进行集成测试:
java复制@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
@Transactional
@Rollback
void testInsert() {
User user = new User(null, "test", "test@example.com", null);
userMapper.insert(user);
assertNotNull(user.getId());
}
}
6.2 接口测试要点
测试重点验证:
- 基本CRUD功能
- 事务回滚情况
- 边界条件处理(如空值、超长字符串等)
- 并发情况下的表现
7. 生产环境建议
7.1 监控与指标
建议配置的监控项:
- 数据库连接池使用情况
- SQL执行时间统计
- 慢查询日志记录
7.2 安全注意事项
- SQL注入防护:
- 永远不要拼接SQL
- 使用#{}而不是${}(除非必要)
- XML中特殊字符使用CDATA包裹
- 敏感信息保护:
- 数据库密码加密存储
- 生产环境禁用druid的监控页面
- 限制数据库账号权限
在实际项目开发中,我发现MyBatis的@Provider注解对于动态SQL也非常有用,特别是当SQL需要根据运行时条件动态生成时。比如:
java复制@SelectProvider(type = UserSqlProvider.class, method = "getQuerySql")
List<User> queryByExample(UserExample example);
对应的SqlProvider类:
java复制public class UserSqlProvider {
public String getQuerySql(UserExample example) {
return new SQL() {{
SELECT("*");
FROM("user");
if (example.getUsername() != null) {
WHERE("username like #{username} || '%'");
}
if (example.getCreateStart() != null) {
WHERE("create_time >= #{createStart}");
}
}}.toString();
}
}
这种写法既保持了类型安全,又能实现灵活的SQL构建,特别适合复杂查询场景。