湛江市网站建设,树形菜单的网站代码,微信公众号运营,公司网站与营销网站在栏目上的不同目录
Spring Boot 集成 MyBatis 实战#xff1a;从配置到企业级优化
一、核心原理#xff1a;Spring Boot 如何整合 MyBatis#xff1f;
1. 整合核心依赖
2. 自动配置核心逻辑
二、环境搭建#xff1a;从 0 到 1 创建集成项目
1. 步骤 1#xff1a;初始化 Spring Bo…目录Spring Boot 集成 MyBatis 实战从配置到企业级优化一、核心原理Spring Boot 如何整合 MyBatis1. 整合核心依赖2. 自动配置核心逻辑二、环境搭建从 0 到 1 创建集成项目1. 步骤 1初始化 Spring Boot 项目Maven 依赖pom.xml2. 步骤 2创建数据库与表3. 步骤 3配置数据源与 MyBatis三、核心实战两种开发模式实现 CRUD1. 通用准备创建实体类POJO2. 模式 1注解式开发无 XML步骤 1创建 Mapper 接口步骤 2创建 Service 层业务逻辑步骤 3创建 Controller 层接口测试步骤 4测试接口3. 模式 2XML 式开发SQL 与代码分离步骤 1创建 Mapper 接口仅定义方法步骤 2创建 XML 映射文件步骤 3测试 XML 式开发四、企业级高级特性1. 分页查询整合 MyBatis-Plus 分页插件推荐步骤 1引入 MyBatis-Plus 依赖步骤 2配置分页插件步骤 3实现分页查询2. 事务管理Spring 声明式事务步骤 1添加事务注解步骤 2测试事务3. 多数据源动态切换数据源步骤 1引入依赖步骤 2配置多数据源步骤 3切换数据源五、常见问题与性能优化1. 常见问题解决方案1Mapper 接口注入失败No qualifying bean of type ...2数据库字段与实体类字段映射失败字段为 null3SQL 注入风险如模糊查询直接拼接字符串2. 性能优化技巧1使用连接池默认 HikariCP2开启 MyBatis 二级缓存3避免 N1 查询问题六、总结Spring Boot 集成 MyBatis 实战从配置到企业级优化MyBatis 作为 Java 领域轻量级 ORM 框架以其 “半自动化” 特性SQL 与代码分离、灵活控制查询逻辑成为企业级应用的首选数据访问方案。而 Spring Boot 通过 “约定优于配置” 的设计可快速整合 MyBatis消除传统 MyBatis 繁琐的 XML 配置与工厂类管理。本文将从 “环境搭建→核心配置→CRUD 实战→高级特性→性能优化” 五个维度带您掌握 Spring Boot 集成 MyBatis 的完整流程覆盖注解式、XML 式两种开发模式以及分页、事务、多数据源等企业级需求。一、核心原理Spring Boot 如何整合 MyBatis在动手实践前需先理解 Spring Boot 与 MyBatis 的整合逻辑 —— 本质是通过自动配置替代传统 MyBatis 的手动配置将 MyBatis 核心组件SqlSessionFactory、SqlSessionTemplate、Mapper接口代理注册为 Spring Bean实现 “注入即使用”。1. 整合核心依赖Spring Boot 官方提供的mybatis-spring-boot-starter是整合的关键它封装了三大核心依赖mybatisMyBatis 核心包提供 SQL 解析、映射等基础能力mybatis-springMyBatis 与 Spring 的桥接包实现 MyBatis 组件与 Spring Bean 的适配spring-boot-starter-jdbcSpring Boot JDBC starter提供数据源自动配置。通过引入该 starter无需手动管理 MyBatis 与 Spring 的版本兼容性Spring Boot 会自动协调依赖版本。2. 自动配置核心逻辑Spring Boot 通过MyBatisAutoConfiguration类完成 MyBatis 的自动配置核心流程如下数据源自动配置基于spring-boot-starter-jdbc自动创建DataSourceBean默认使用 HikariCP 连接池创建 SqlSessionFactory通过DataSource构建SqlSessionFactoryMyBatis 的核心工厂负责生成SqlSession并自动扫描Mapper接口或 XML 映射文件创建 SqlSessionTemplate封装SqlSession作为线程安全的SqlSession代理替代传统SqlSession的手动创建与关闭Mapper 接口代理通过MapperScannerConfigurer扫描指定包下的Mapper接口为每个接口生成动态代理对象代理对象内部通过SqlSessionTemplate执行 SQL并注册为 Spring Bean。简言之开发者只需定义Mapper接口与 SQLSpring Boot 会自动完成 MyBatis 组件的创建与注入无需手动编写SqlSessionFactory、SqlSession等代码。二、环境搭建从 0 到 1 创建集成项目本节以 “用户管理模块” 为例基于 Spring Boot 2.7.x稳定版使用 MySQL 8.0 数据库搭建 MyBatis 集成环境覆盖两种开发模式注解式无 XML与XML 式SQL 与代码分离。1. 步骤 1初始化 Spring Boot 项目通过 Spring Initializrstart.spring.io或 IDEA 手动创建项目核心依赖如下Maven 依赖pom.xml!-- 继承Spring Boot父POM统一版本管理 -- parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.10/version relativePath/ /parent dependencies !-- 1. Spring Boot Web用于测试接口 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 2. MyBatis Spring Boot Starter核心整合依赖 -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version !-- 与Spring Boot 2.7.x兼容的稳定版 -- /dependency !-- 3. MySQL驱动适配MySQL 8.0 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- 4. Lombok简化POJO代码 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- 5. 单元测试可选 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies !-- 打包插件生成可执行JAR -- build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration /plugin /plugins /build2. 步骤 2创建数据库与表在 MySQL 中创建test数据库并执行以下 SQL 创建user表CREATE TABLE user ( id int NOT NULL AUTO_INCREMENT COMMENT 用户ID主键, name varchar(50) NOT NULL COMMENT 用户名, age int DEFAULT NULL COMMENT 年龄, email varchar(100) DEFAULT NULL COMMENT 邮箱, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;3. 步骤 3配置数据源与 MyBatis在src/main/resources下创建application.yml推荐使用 YAML 格式比 Properties 更简洁配置数据源、MyBatis 核心参数# 1. 数据源配置默认使用HikariCP连接池 spring: datasource: url: jdbc:mysql://localhost:3306/test?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root # 你的MySQL用户名 password: 123456 # 你的MySQL密码 driver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8.0驱动类 # 2. MyBatis配置 mybatis: # 2.1 扫描XML映射文件若使用XML式开发需指定路径 mapper-locations: classpath:mybatis/mappers/**/*.xml # 2.2 配置别名简化POJO类全路径如User替代com.example.demo.entity.User type-aliases-package: com.example.demo.entity # 2.3 配置MyBatis全局参数可选 configuration: map-underscore-to-camel-case: true # 开启下划线转驼峰如数据库create_time → 实体类createTime log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL开发环境调试用 cache-enabled: false # 关闭全局缓存默认开启可根据需求调整关键配置说明map-underscore-to-camel-case: true解决数据库字段下划线命名如create_time与 Java 实体类驼峰命名如createTime的映射问题无需手动写resultMapmapper-locations仅 XML 式开发需配置指定 XML 映射文件的路径支持通配符**匹配多级目录log-impl开发环境开启 SQL 打印便于调试 SQL 语句与参数。三、核心实战两种开发模式实现 CRUDSpring Boot 集成 MyBatis 支持两种主流开发模式注解式SQL 写在注解中与XML 式SQL 写在独立 XML 文件中。注解式适合简单 SQLXML 式适合复杂 SQL如多表关联、动态 SQL可根据业务场景选择。1. 通用准备创建实体类POJO首先定义与user表对应的实体类User使用 Lombok 简化getter/setterpackage com.example.demo.entity; import lombok.Data; import java.time.LocalDateTime; /** * 用户实体类与数据库user表对应 */ Data // 等同于Getter Setter ToString EqualsAndHashCode NoArgsConstructor public class User { private Integer id; // 主键ID private String name; // 用户名 private Integer age; // 年龄 private String email; // 邮箱 private LocalDateTime createTime; // 创建时间对应数据库create_time }2. 模式 1注解式开发无 XML注解式开发通过 MyBatis 提供的Select、Insert、Update、Delete等注解将 SQL 直接写在Mapper接口方法上无需创建 XML 文件适合简单 CRUD 场景。步骤 1创建 Mapper 接口定义UserMapper接口添加Mapper注解告诉 Spring Boot 这是 MyBatis 的 Mapper 接口需生成代理对象package com.example.demo.mapper; import com.example.demo.entity.User; import org.apache.ibatis.annotations.*; import java.util.List; /** * 用户Mapper接口注解式开发 */ Mapper // 标记为MyBatis Mapper接口Spring Boot会自动扫描并生成代理对象 public interface UserMapper { // 1. 新增用户Options获取自增主键值赋值给实体类的id字段 Insert(INSERT INTO user(name, age, email) VALUES(#{name}, #{age}, #{email})) Options(useGeneratedKeys true, keyProperty id) // 开启自增主键绑定实体类id字段 int insert(User user); // 2. 根据ID删除用户 Delete(DELETE FROM user WHERE id #{id}) int deleteById(Integer id); // 3. 根据ID更新用户动态更新仅更新非null字段 Update(script // script标签支持动态SQL语法 UPDATE user set // set标签自动处理逗号避免SQL语法错误 if testname ! nullname #{name},/if if testage ! nullage #{age},/if if testemail ! nullemail #{email},/if /set WHERE id #{id} /script) int updateById(User user); // 4. 根据ID查询用户 Select(SELECT id, name, age, email, create_time AS createTime FROM user WHERE id #{id}) User selectById(Integer id); // 5. 查询所有用户配合全局配置map-underscore-to-camel-case无需手动映射create_time→createTime Select(SELECT * FROM user) ListUser selectAll(); // 6. 条件查询根据姓名模糊查询 Select(SELECT * FROM user WHERE name LIKE CONCAT(%, #{name}, %)) ListUser selectByName(String name); }注解说明Options(useGeneratedKeys true, keyProperty id)新增用户时自动获取 MySQL 自增主键id并赋值给User对象的id字段后续可直接通过user.getId()获取主键script标签在注解中使用动态 SQL如if、set时必须包裹在script标签内否则 MyBatis 无法解析动态 SQL 语法CONCAT(%, #{name}, %)MySQL 模糊查询语法避免直接写%#{name}%会导致 SQL 注入风险。步骤 2创建 Service 层业务逻辑定义UserService接口与实现类注入UserMapper封装业务逻辑// UserService接口 package com.example.demo.service; import com.example.demo.entity.User; import java.util.List; public interface UserService { // 新增用户 boolean save(User user); // 根据ID删除 boolean removeById(Integer id); // 根据ID更新动态更新非null字段 boolean update(User user); // 根据ID查询 User getById(Integer id); // 查询所有 ListUser listAll(); // 模糊查询姓名 ListUser listByName(String name); } // UserService实现类 package com.example.demo.service.impl; import com.example.demo.entity.User; import com.example.demo.mapper.UserMapper; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; Service // 标记为Spring Service组件 public class UserServiceImpl implements UserService { // 注入MyBatis Mapper代理对象Spring Boot自动生成 Autowired private UserMapper userMapper; Override public boolean save(User user) { // insert方法返回影响行数0表示成功 return userMapper.insert(user) 0; } Override public boolean removeById(Integer id) { return userMapper.deleteById(id) 0; } Override public boolean update(User user) { // 确保更新时ID不为null否则SQL会更新所有记录风险极高 if (user.getId() null) { throw new IllegalArgumentException(更新失败用户ID不能为空); } return userMapper.updateById(user) 0; } Override public User getById(Integer id) { return userMapper.selectById(id); } Override public ListUser listAll() { return userMapper.selectAll(); } Override public ListUser listByName(String name) { return userMapper.selectByName(name); } }步骤 3创建 Controller 层接口测试定义UserController提供 RESTful 接口测试 CRUD 功能package com.example.demo.controller; import com.example.demo.entity.User; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; /** * 用户管理REST接口 * 接口前缀/api/users */ RestController RequestMapping(/api/users) public class UserController { Autowired private UserService userService; // 1. 新增用户POST /api/users PostMapping public ResponseEntityString save(RequestBody User user) { boolean success userService.save(user); if (success) { // 新增成功返回201状态码与用户ID return new ResponseEntity(新增用户成功ID user.getId(), HttpStatus.CREATED); } return new ResponseEntity(新增用户失败, HttpStatus.INTERNAL_SERVER_ERROR); } // 2. 删除用户DELETE /api/users/{id} DeleteMapping(/{id}) public ResponseEntityString removeById(PathVariable Integer id) { boolean success userService.removeById(id); if (success) { return ResponseEntity.ok(删除用户成功); } return new ResponseEntity(删除用户失败用户不存在, HttpStatus.NOT_FOUND); } // 3. 更新用户PUT /api/users PutMapping public ResponseEntityString update(RequestBody User user) { try { boolean success userService.update(user); if (success) { return ResponseEntity.ok(更新用户成功); } return new ResponseEntity(更新用户失败用户不存在, HttpStatus.NOT_FOUND); } catch (IllegalArgumentException e) { return new ResponseEntity(e.getMessage(), HttpStatus.BAD_REQUEST); } } // 4. 根据ID查询GET /api/users/{id} GetMapping(/{id}) public ResponseEntityUser getById(PathVariable Integer id) { User user userService.getById(id); if (user ! null) { return ResponseEntity.ok(user); } return ResponseEntity.notFound().build(); // 用户不存在返回404 } // 5. 查询所有GET /api/users GetMapping public ResponseEntityListUser listAll() { ListUser users userService.listAll(); return ResponseEntity.ok(users); } // 6. 模糊查询姓名GET /api/users/name/{name} GetMapping(/name/{name}) public ResponseEntityListUser listByName(PathVariable String name) { ListUser users userService.listByName(name); return ResponseEntity.ok(users); } }步骤 4测试接口启动 Spring Boot 应用运行DemoApplication的main方法使用 Postman 或 curl 测试接口新增用户POST http://localhost:8080/api/users请求体{name:张三,age:25,email:zhangsanexample.com}查询用户GET http://localhost:8080/api/users/11 为新增用户的 ID其他接口类似控制台会打印 MyBatis 执行的 SQL 与参数便于调试。3. 模式 2XML 式开发SQL 与代码分离XML 式开发将 SQL 写在独立的 XML 映射文件中Mapper接口仅定义方法签名适合复杂 SQL 场景如多表关联查询、复杂动态 SQL。步骤 1创建 Mapper 接口仅定义方法与注解式不同XML 式的Mapper接口无需添加 SQL 注解仅需定义方法签名package com.example.demo.mapper; import com.example.demo.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; /** * 用户Mapper接口XML式开发 */ Mapper public interface UserXmlMapper { // 1. 新增用户与注解式功能一致 int insert(User user); // 2. 根据ID删除 int deleteById(Integer id); // 3. 根据ID更新动态更新 int updateById(User user); // 4. 根据ID查询 User selectById(Integer id); // 5. 多条件动态查询支持姓名模糊、年龄范围、邮箱非空 // Param给参数命名便于XML中引用若参数是单个简单类型可省略若多个参数必须加 ListUser selectByCondition(Param(name) String name, Param(minAge) Integer minAge, Param(maxAge) Integer maxAge); // 6. 多表关联查询示例假设用户关联订单表查询用户及订单数量 ListMapString, Object selectUserWithOrderCount(); }关键说明Param注解当方法有多个参数时必须用Param给参数命名如Param(name)否则 XML 中无法通过#{name}引用参数返回值为MapString, Object适合多表关联查询键为数据库字段名值为字段值无需定义专门的实体类。步骤 2创建 XML 映射文件根据application.yml中mybatis.mapper-locations的配置classpath:mybatis/mappers/**/*.xml在src/main/resources下创建目录mybatis/mappers并创建UserXmlMapper.xml文件?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd !-- namespace必须与Mapper接口的全路径一致 -- mapper namespacecom.example.demo.mapper.UserXmlMapper !-- 1. 新增用户与注解式一致支持自增主键 -- insert idinsert useGeneratedKeystrue keyPropertyid INSERT INTO user(name, age, email) VALUES(#{name}, #{age}, #{email}) /insert !-- 2. 根据ID删除 -- delete iddeleteById DELETE FROM user WHERE id #{id} /delete !-- 3. 根据ID动态更新仅更新非null字段 -- update idupdateById UPDATE user set if testname ! nullname #{name},/if if testage ! nullage #{age},/if if testemail ! nullemail #{email},/if /set WHERE id #{id} /update !-- 4. 根据ID查询可省略resultMap依赖全局下划线转驼峰配置 -- select idselectById resultTypeUser !-- resultType返回实体类别名需配置type-aliases-package -- SELECT * FROM user WHERE id #{id} /select !-- 5. 多条件动态查询姓名模糊、年龄范围 -- select idselectByCondition resultTypeUser SELECT * FROM user where !-- where标签自动处理AND/OR避免SQL语法错误 -- if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if if testminAge ! null AND age #{minAge} /if if testmaxAge ! null AND age #{maxAge} /if /where ORDER BY create_time DESC !-- 按创建时间倒序 -- /select !-- 6. 多表关联查询用户表关联订单表查询用户及订单数量 -- select idselectUserWithOrderCount resultTypejava.util.Map SELECT u.id, u.name, u.age, COUNT(o.id) AS order_count !-- 订单数量 -- FROM user u LEFT JOIN order o ON u.id o.user_id !-- 假设订单表为order需用反引号转义关键字 -- GROUP BY u.id, u.name, u.age !-- GROUP BY需包含非聚合字段 -- HAVING COUNT(o.id) 0 !-- 仅查询有订单的用户 -- /select /mapperXML 标签说明namespace必须与Mapper接口的全路径完全一致如com.example.demo.mapper.UserXmlMapper否则 MyBatis 无法关联接口与 XMLresultType指定查询结果的类型如User是实体类别名java.util.Map是 Map 类型where标签替代 SQL 中的WHERE关键字自动移除条件前多余的AND/OR如第一个条件不满足时第二个条件的AND会被删除多表关联通过LEFT JOIN关联订单表使用COUNT(o.id)统计订单数量适合复杂业务场景。步骤 3测试 XML 式开发与注解式类似创建UserXmlService、UserXmlController或复用原有 Service/Controller注入UserXmlMapper即可测试。例如测试多条件查询// Service层方法 public ListUser listByCondition(String name, Integer minAge, Integer maxAge) { return userXmlMapper.selectByCondition(name, minAge, maxAge); } // Controller层接口 GetMapping(/condition) public ResponseEntityListUser listByCondition( RequestParam(required false) String name, // 非必传参数 RequestParam(required false) Integer minAge, RequestParam(required false) Integer maxAge) { ListUser users userService.listByCondition(name, minAge, maxAge); return ResponseEntity.ok(users); }测试接口GET http://localhost:8080/api/users/condition?name张minAge20maxAge30会返回 “姓名包含张、年龄 20-30” 的用户列表。四、企业级高级特性实际项目中除了基础 CRUD还需解决分页查询、事务管理、多数据源等企业级需求本节介绍这些特性的整合方案。1. 分页查询整合 MyBatis-Plus 分页插件推荐MyBatis 原生不支持分页需手动写LIMIT语句效率低且易出错。推荐使用MyBatis-Plus 分页插件轻量级、无侵入支持物理分页通过LIMIT实现。步骤 1引入 MyBatis-Plus 依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version !-- 与Spring Boot 2.7.x兼容 -- /dependency步骤 2配置分页插件创建 MyBatis 配置类注册分页插件package com.example.demo.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * MyBatis配置类注册分页插件、扫描Mapper接口 */ Configuration MapperScan(com.example.demo.mapper) // 扫描所有Mapper接口可替代每个接口的Mapper注解 public class MyBatisConfig { /** * 注册MyBatis-Plus分页插件 */ Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 添加分页插件指定数据库类型MySQL interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }说明MapperScan(com.example.demo.mapper)在配置类上统一扫描 Mapper 接口无需在每个 Mapper 接口添加Mapper注解简化代码。步骤 3实现分页查询以 XML 式开发为例修改UserXmlMapper接口与 XML实现分页// Mapper接口方法参数添加IPageUser返回IPageUser IPageUser selectPage(IPageUser page, Param(name) String name); // XML映射文件无需写LIMIT分页插件自动添加 select idselectPage resultTypeUser SELECT * FROM user where if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if /where ORDER BY create_time DESC /select // Service层方法 public IPageUser pageUser(Integer pageNum, Integer pageSize, String name) { // 创建分页对象pageNum页码pageSize每页条数 IPageUser page new Page(pageNum, pageSize); // 调用Mapper方法分页插件自动处理分页 return userXmlMapper.selectPage(page, name); } // Controller层接口 GetMapping(/page) public ResponseEntityIPageUser pageUser( RequestParam(defaultValue 1) Integer pageNum, // 默认第1页 RequestParam(defaultValue 10) Integer pageSize, // 默认每页10条 RequestParam(required false) String name) { IPageUser userPage userService.pageUser(pageNum, pageSize, name); return ResponseEntity.ok(userPage); }测试接口GET http://localhost:8080/api/users/page?pageNum1pageSize5name张返回结果包含分页信息总条数、总页数、当前页数据{ records: [{id:1,name:张三,age:25,email:zhangsanexample.com,createTime:2024-05-20T10:00:00}], total: 3, // 总条数 size: 5, // 每页条数 current: 1, // 当前页码 pages: 1 // 总页数 }2. 事务管理Spring 声明式事务Spring Boot 整合 MyBatis 后可直接使用 Spring 的声明式事务Transactional注解确保多 SQL 操作的原子性要么全成功要么全回滚。步骤 1添加事务注解在 Service 层的方法上添加Transactional注解指定事务属性如隔离级别、传播行为Service public class UserServiceImpl implements UserService { Autowired private UserMapper userMapper; /** * 示例批量新增用户事务管理确保所有用户要么全成功要么全失败 */ Transactional( rollbackFor Exception.class, // 所有异常都回滚默认仅RuntimeException回滚 propagation Propagation.REQUIRED // 事务传播行为默认若当前无事务则创建新事务 ) Override public boolean batchSave(ListUser userList) { int count 0; for (User user : userList) { count userMapper.insert(user); // 模拟异常测试事务回滚 if (user.getName().equals(测试回滚)) { throw new RuntimeException(批量新增失败触发事务回滚); } } return count userList.size(); } }关键属性说明rollbackFor Exception.class默认情况下Spring 事务仅在抛出RuntimeException或Error时回滚添加此属性后所有Exception包括受检异常都会触发回滚propagation事务传播行为常用REQUIRED默认适合大多数场景、SUPPORTS无事务则以非事务方式执行、REQUIRES_NEW创建新事务与当前事务独立。步骤 2测试事务调用batchSave方法时若中间抛出异常之前插入的用户会被回滚数据库中不会留下部分数据确保数据一致性。3. 多数据源动态切换数据源实际项目中可能需要操作多个数据库如主库写、从库读Spring Boot 可通过dynamic-datasource-spring-boot-starterMyBatis-Plus 团队提供实现动态数据源切换。步骤 1引入依赖dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot-starter/artifactId version3.5.2/version /dependency步骤 2配置多数据源修改application.yml配置主从数据源spring: datasource: dynamic: primary: master # 默认数据源主库 datasource: # 主库写操作 master: url: jdbc:mysql://localhost:3306/test_master?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # 从库读操作 slave1: url: jdbc:mysql://localhost:3306/test_slave1?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver步骤 3切换数据源通过DS注解指定数据源添加在 Service 或方法上Service public class UserServiceImpl implements UserService { Autowired private UserMapper userMapper; // 写操作使用主库默认可省略DS(master) Override public boolean save(User user) { return userMapper.insert(user) 0; } // 读操作使用从库通过DS指定数据源 DS(slave1) Override public User getById(Integer id) { return userMapper.selectById(id); } // 读操作使用从库 DS(slave1) Override public ListUser listAll() { return userMapper.selectAll(); } }说明DS注解优先级方法级 类级若类上添加DS(slave1)则类中所有方法默认使用从库可通过方法级DS(master)覆盖。五、常见问题与性能优化1. 常见问题解决方案1Mapper 接口注入失败No qualifying bean of type ...原因 1Mapper 接口未添加Mapper注解且未配置MapperScan解决方案在配置类上添加MapperScan(com.example.demo.mapper)统一扫描 Mapper 接口。2数据库字段与实体类字段映射失败字段为 null原因 1未开启下划线转驼峰如create_time无法映射到createTime解决方案在application.yml中配置mybatis.configuration.map-underscore-to-camel-case: true。原因 2XML 中resultMap配置错误字段名与实体类属性不匹配解决方案检查resultMap的column数据库字段与property实体类属性是否一致。3SQL 注入风险如模糊查询直接拼接字符串原因使用%${name}%${}是字符串拼接会导致 SQL 注入解决方案使用CONCAT(%, #{name}, %)#{}是预编译参数防止 SQL 注入。2. 性能优化技巧1使用连接池默认 HikariCPSpring Boot 默认使用 HikariCP 连接池性能最优无需额外配置可通过以下参数优化连接池spring: datasource: hikari: maximum-pool-size: 10 # 最大连接数根据CPU核心数调整建议10-20 minimum-idle: 5 # 最小空闲连接数 idle-timeout: 300000 # 空闲连接超时时间5分钟 connection-timeout: 30000 # 连接超时时间30秒2开启 MyBatis 二级缓存MyBatis 二级缓存是 “namespace 级” 缓存同一 Mapper 接口的方法共享缓存适合查询频繁、修改少的数据# 开启全局二级缓存 mybatis: configuration: cache-enabled: true在 Mapper 接口上添加CacheNamespace注解或在 XML 中添加cache标签Mapper CacheNamespace(eviction FifoCache.class, flushInterval 60000) // FIFO缓存60秒刷新 public interface UserMapper { ... }3避免 N1 查询问题N1 查询是指 “查询 1 条主数据触发 N 条从数据查询”如查询 10 个用户每个用户查询其订单共触发 11011 次查询解决方案使用关联查询通过LEFT JOIN一次性查询主从数据如 XML 式开发中的多表关联使用 MyBatis-Plus 的association/collection标签在resultMap中配置关联查询避免 N1。六、总结Spring Boot 集成 MyBatis 的核心是 “自动配置”通过mybatis-spring-boot-starter消除了传统 MyBatis 的繁琐配置让开发者专注于 SQL 与业务逻辑。本文覆盖了两种开发模式注解式适合简单 SQLXML 式适合复杂 SQL以及分页、事务、多数据源等企业级特性满足从中小项目到大型系统的需求。掌握 Spring Boot 集成 MyBatis 的关键在于理解自动配置逻辑熟悉application.yml中的核心参数数据源、XML 路径、下划线转驼峰根据 SQL 复杂度选择开发模式注解式 / XML 式避免 SQL 与代码过度耦合合理使用分页插件、事务管理、多数据源等高级特性解决企业级需求关注性能优化连接池、缓存、避免 N1确保应用高效稳定运行。随着 MyBatis-Plus 等增强工具的普及Spring Boot 与 MyBatis 的整合会更加高效未来在云原生、微服务场景中仍将是数据访问层的主流方案。