news 2026/2/12 22:16:08

SpringBoot+MyBatis实战:企业级开发指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+MyBatis实战:企业级开发指南

目录

Spring Boot 集成 MyBatis 实战:从配置到企业级优化

一、核心原理:Spring Boot 如何整合 MyBatis?

1. 整合核心依赖

2. 自动配置核心逻辑

二、环境搭建:从 0 到 1 创建集成项目

1. 步骤 1:初始化 Spring Boot 项目

Maven 依赖(pom.xml)

2. 步骤 2:创建数据库与表

3. 步骤 3:配置数据源与 MyBatis

三、核心实战:两种开发模式实现 CRUD

1. 通用准备:创建实体类(POJO)

2. 模式 1:注解式开发(无 XML)

步骤 1:创建 Mapper 接口

步骤 2:创建 Service 层(业务逻辑)

步骤 3:创建 Controller 层(接口测试)

步骤 4:测试接口

3. 模式 2:XML 式开发(SQL 与代码分离)

步骤 1:创建 Mapper 接口(仅定义方法)

步骤 2:创建 XML 映射文件

步骤 3:测试 XML 式开发

四、企业级高级特性

1. 分页查询:整合 MyBatis-Plus 分页插件(推荐)

步骤 1:引入 MyBatis-Plus 依赖

步骤 2:配置分页插件

步骤 3:实现分页查询

2. 事务管理:Spring 声明式事务

步骤 1:添加事务注解

步骤 2:测试事务

3. 多数据源:动态切换数据源

步骤 1:引入依赖

步骤 2:配置多数据源

步骤 3:切换数据源

五、常见问题与性能优化

1. 常见问题解决方案

(1)Mapper 接口注入失败(No qualifying bean of type ...)

(2)数据库字段与实体类字段映射失败(字段为 null)

(3)SQL 注入风险(如模糊查询直接拼接字符串)

2. 性能优化技巧

(1)使用连接池(默认 HikariCP)

(2)开启 MyBatis 二级缓存

(3)避免 N+1 查询问题

六、总结


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 核心组件(SqlSessionFactorySqlSessionTemplateMapper接口代理)注册为 Spring Bean,实现 “注入即使用”。

1. 整合核心依赖

Spring Boot 官方提供的mybatis-spring-boot-starter是整合的关键,它封装了三大核心依赖:

  • mybatis:MyBatis 核心包(提供 SQL 解析、映射等基础能力);
  • mybatis-spring:MyBatis 与 Spring 的桥接包(实现 MyBatis 组件与 Spring Bean 的适配);
  • spring-boot-starter-jdbc:Spring Boot JDBC starter(提供数据源自动配置)。

通过引入该 starter,无需手动管理 MyBatis 与 Spring 的版本兼容性,Spring Boot 会自动协调依赖版本。

2. 自动配置核心逻辑

Spring Boot 通过MyBatisAutoConfiguration类完成 MyBatis 的自动配置,核心流程如下:

  1. 数据源自动配置:基于spring-boot-starter-jdbc,自动创建DataSourceBean(默认使用 HikariCP 连接池);
  2. 创建 SqlSessionFactory:通过DataSource构建SqlSessionFactory(MyBatis 的核心工厂,负责生成SqlSession),并自动扫描@Mapper接口或 XML 映射文件;
  3. 创建 SqlSessionTemplate:封装SqlSession,作为线程安全的SqlSession代理(替代传统SqlSession的手动创建与关闭);
  4. Mapper 接口代理:通过MapperScannerConfigurer扫描指定包下的@Mapper接口,为每个接口生成动态代理对象(代理对象内部通过SqlSessionTemplate执行 SQL),并注册为 Spring Bean。

简言之:开发者只需定义Mapper接口与 SQL,Spring Boot 会自动完成 MyBatis 组件的创建与注入,无需手动编写SqlSessionFactorySqlSession等代码。

二、环境搭建:从 0 到 1 创建集成项目

本节以 “用户管理模块” 为例,基于 Spring Boot 2.7.x(稳定版),使用 MySQL 8.0 数据库,搭建 MyBatis 集成环境,覆盖两种开发模式:注解式(无 XML)XML 式(SQL 与代码分离)

1. 步骤 1:初始化 Spring Boot 项目

通过 Spring Initializr(start.spring.io)或 IDEA 手动创建项目,核心依赖如下:

Maven 依赖(pom.xml)
<!-- 继承Spring Boot父POM(统一版本管理) --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.10</version> <relativePath/> </parent> <dependencies> <!-- 1. Spring Boot Web(用于测试接口) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 2. MyBatis Spring Boot Starter(核心整合依赖) --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.1</version> <!-- 与Spring Boot 2.7.x兼容的稳定版 --> </dependency> <!-- 3. MySQL驱动(适配MySQL 8.0+) --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- 4. Lombok(简化POJO代码) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 5. 单元测试(可选) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <!-- 打包插件(生成可执行JAR) --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>

2. 步骤 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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

3. 步骤 3:配置数据源与 MyBatis

src/main/resources下创建application.yml(推荐使用 YAML 格式,比 Properties 更简洁),配置数据源、MyBatis 核心参数:

# 1. 数据源配置(默认使用HikariCP连接池) spring: datasource: url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true 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)的映射问题,无需手动写resultMap
  • mapper-locations:仅 XML 式开发需配置,指定 XML 映射文件的路径(支持通配符**,匹配多级目录);
  • log-impl:开发环境开启 SQL 打印,便于调试 SQL 语句与参数。

三、核心实战:两种开发模式实现 CRUD

Spring Boot 集成 MyBatis 支持两种主流开发模式:注解式(SQL 写在注解中)XML 式(SQL 写在独立 XML 文件中)。注解式适合简单 SQL,XML 式适合复杂 SQL(如多表关联、动态 SQL),可根据业务场景选择。

1. 通用准备:创建实体类(POJO)

首先定义与user表对应的实体类User,使用 Lombok 简化getter/setter

package 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 test='name != null'>name = #{name},</if>" + "<if test='age != null'>age = #{age},</if>" + "<if test='email != null'>email = #{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") List<User> selectAll(); // 6. 条件查询(根据姓名模糊查询) @Select("SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')") List<User> selectByName(String name); }

注解说明

  • @Options(useGeneratedKeys = true, keyProperty = "id"):新增用户时,自动获取 MySQL 自增主键id,并赋值给User对象的id字段(后续可直接通过user.getId()获取主键);
  • <script>标签:在注解中使用动态 SQL(如ifset)时,必须包裹在<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); // 查询所有 List<User> listAll(); // 模糊查询姓名 List<User> 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 List<User> listAll() { return userMapper.selectAll(); } @Override public List<User> 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 ResponseEntity<String> 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 ResponseEntity<String> 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 ResponseEntity<String> 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 ResponseEntity<User> 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 ResponseEntity<List<User>> listAll() { List<User> users = userService.listAll(); return ResponseEntity.ok(users); } // 6. 模糊查询姓名:GET /api/users/name/{name} @GetMapping("/name/{name}") public ResponseEntity<List<User>> listByName(@PathVariable String name) { List<User> users = userService.listByName(name); return ResponseEntity.ok(users); } }
步骤 4:测试接口

启动 Spring Boot 应用(运行DemoApplicationmain方法),使用 Postman 或 curl 测试接口:

  • 新增用户:POST http://localhost:8080/api/users,请求体{"name":"张三","age":25,"email":"zhangsan@example.com"}
  • 查询用户:GET http://localhost:8080/api/users/1(1 为新增用户的 ID);
  • 其他接口类似,控制台会打印 MyBatis 执行的 SQL 与参数,便于调试。

3. 模式 2:XML 式开发(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中引用(若参数是单个简单类型,可省略;若多个参数,必须加) List<User> selectByCondition(@Param("name") String name, @Param("minAge") Integer minAge, @Param("maxAge") Integer maxAge); // 6. 多表关联查询(示例:假设用户关联订单表,查询用户及订单数量) List<Map<String, Object>> selectUserWithOrderCount(); }

关键说明

  • @Param注解:当方法有多个参数时,必须用@Param给参数命名(如@Param("name")),否则 XML 中无法通过#{name}引用参数;
  • 返回值为Map<String, Object>:适合多表关联查询,键为数据库字段名,值为字段值(无需定义专门的实体类)。
步骤 2:创建 XML 映射文件

根据application.ymlmybatis.mapper-locations的配置(classpath:mybatis/mappers/**/*.xml),在src/main/resources下创建目录mybatis/mappers,并创建UserXmlMapper.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"> <!-- namespace:必须与Mapper接口的全路径一致 --> <mapper namespace="com.example.demo.mapper.UserXmlMapper"> <!-- 1. 新增用户(与注解式一致,支持自增主键) --> <insert id="insert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user(name, age, email) VALUES(#{name}, #{age}, #{email}) </insert> <!-- 2. 根据ID删除 --> <delete id="deleteById"> DELETE FROM user WHERE id = #{id} </delete> <!-- 3. 根据ID动态更新(仅更新非null字段) --> <update id="updateById"> UPDATE user <set> <if test="name != null">name = #{name},</if> <if test="age != null">age = #{age},</if> <if test="email != null">email = #{email},</if> </set> WHERE id = #{id} </update> <!-- 4. 根据ID查询(可省略resultMap,依赖全局下划线转驼峰配置) --> <select id="selectById" resultType="User"> <!-- resultType:返回实体类(别名,需配置type-aliases-package) --> SELECT * FROM user WHERE id = #{id} </select> <!-- 5. 多条件动态查询(姓名模糊、年龄范围) --> <select id="selectByCondition" resultType="User"> SELECT * FROM user <where> <!-- <where>标签自动处理AND/OR,避免SQL语法错误 --> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="minAge != null"> AND age >= #{minAge} </if> <if test="maxAge != null"> AND age <= #{maxAge} </if> </where> ORDER BY create_time DESC <!-- 按创建时间倒序 --> </select> <!-- 6. 多表关联查询(用户表关联订单表,查询用户及订单数量) --> <select id="selectUserWithOrderCount" resultType="java.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> </mapper>

XML 标签说明

  • namespace:必须与Mapper接口的全路径完全一致(如com.example.demo.mapper.UserXmlMapper),否则 MyBatis 无法关联接口与 XML;
  • resultType:指定查询结果的类型(如User是实体类别名,java.util.Map是 Map 类型);
  • <where>标签:替代 SQL 中的WHERE关键字,自动移除条件前多余的AND/OR(如第一个条件不满足时,第二个条件的AND会被删除);
  • 多表关联:通过LEFT JOIN关联订单表,使用COUNT(o.id)统计订单数量,适合复杂业务场景。
步骤 3:测试 XML 式开发

与注解式类似,创建UserXmlServiceUserXmlController(或复用原有 Service/Controller),注入UserXmlMapper即可测试。例如测试多条件查询:

// Service层方法 public List<User> listByCondition(String name, Integer minAge, Integer maxAge) { return userXmlMapper.selectByCondition(name, minAge, maxAge); } // Controller层接口 @GetMapping("/condition") public ResponseEntity<List<User>> listByCondition( @RequestParam(required = false) String name, // 非必传参数 @RequestParam(required = false) Integer minAge, @RequestParam(required = false) Integer maxAge) { List<User> users = userService.listByCondition(name, minAge, maxAge); return ResponseEntity.ok(users); }

测试接口:GET http://localhost:8080/api/users/condition?name=张&minAge=20&maxAge=30,会返回 “姓名包含张、年龄 20-30” 的用户列表。

四、企业级高级特性

实际项目中,除了基础 CRUD,还需解决分页查询事务管理多数据源等企业级需求,本节介绍这些特性的整合方案。

1. 分页查询:整合 MyBatis-Plus 分页插件(推荐)

MyBatis 原生不支持分页,需手动写LIMIT语句,效率低且易出错。推荐使用MyBatis-Plus 分页插件(轻量级、无侵入),支持物理分页(通过LIMIT实现)。

步骤 1:引入 MyBatis-Plus 依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.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接口方法(参数添加IPage<User>,返回IPage<User>) IPage<User> selectPage(IPage<User> page, @Param("name") String name); // XML映射文件(无需写LIMIT,分页插件自动添加) <select id="selectPage" resultType="User"> SELECT * FROM user <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> </where> ORDER BY create_time DESC </select> // Service层方法 public IPage<User> pageUser(Integer pageNum, Integer pageSize, String name) { // 创建分页对象(pageNum:页码,pageSize:每页条数) IPage<User> page = new Page<>(pageNum, pageSize); // 调用Mapper方法,分页插件自动处理分页 return userXmlMapper.selectPage(page, name); } // Controller层接口 @GetMapping("/page") public ResponseEntity<IPage<User>> pageUser( @RequestParam(defaultValue = "1") Integer pageNum, // 默认第1页 @RequestParam(defaultValue = "10") Integer pageSize, // 默认每页10条 @RequestParam(required = false) String name) { IPage<User> userPage = userService.pageUser(pageNum, pageSize, name); return ResponseEntity.ok(userPage); }

测试接口:GET http://localhost:8080/api/users/page?pageNum=1&pageSize=5&name=张,返回结果包含分页信息(总条数、总页数、当前页数据):

{ "records": [{"id":1,"name":"张三","age":25,"email":"zhangsan@example.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(List<User> 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 事务仅在抛出RuntimeExceptionError时回滚,添加此属性后,所有Exception(包括受检异常)都会触发回滚;
  • propagation:事务传播行为,常用REQUIRED(默认,适合大多数场景)、SUPPORTS(无事务则以非事务方式执行)、REQUIRES_NEW(创建新事务,与当前事务独立)。
步骤 2:测试事务

调用batchSave方法时,若中间抛出异常,之前插入的用户会被回滚(数据库中不会留下部分数据),确保数据一致性。

3. 多数据源:动态切换数据源

实际项目中可能需要操作多个数据库(如主库写、从库读),Spring Boot 可通过dynamic-datasource-spring-boot-starter(MyBatis-Plus 团队提供)实现动态数据源切换。

步骤 1:引入依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.2</version> </dependency>
步骤 2:配置多数据源

修改application.yml,配置主从数据源:

spring: datasource: dynamic: primary: master # 默认数据源(主库) datasource: # 主库(写操作) master: url: jdbc:mysql://localhost:3306/test_master?useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # 从库(读操作) slave1: url: jdbc:mysql://localhost:3306/test_slave1?useSSL=false&serverTimezone=Asia/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 List<User> listAll() { return userMapper.selectAll(); } }

说明@DS注解优先级:方法级 > 类级,若类上添加@DS("slave1"),则类中所有方法默认使用从库,可通过方法级@DS("master")覆盖。

五、常见问题与性能优化

1. 常见问题解决方案

(1)Mapper 接口注入失败(No qualifying bean of type ...)
  • 原因 1:Mapper 接口未添加@Mapper注解,且未配置@MapperScan
  • 解决方案:在配置类上添加@MapperScan("com.example.demo.mapper"),统一扫描 Mapper 接口。
(2)数据库字段与实体类字段映射失败(字段为 null)
  • 原因 1:未开启下划线转驼峰(如create_time无法映射到createTime);
  • 解决方案:在application.yml中配置mybatis.configuration.map-underscore-to-camel-case: true
  • 原因 2:XML 中resultMap配置错误,字段名与实体类属性不匹配;
  • 解决方案:检查resultMapcolumn(数据库字段)与property(实体类属性)是否一致。
(3)SQL 注入风险(如模糊查询直接拼接字符串)
  • 原因:使用'%${name}%'${}是字符串拼接,会导致 SQL 注入);
  • 解决方案:使用CONCAT('%', #{name}, '%')#{}是预编译参数,防止 SQL 注入)。

2. 性能优化技巧

(1)使用连接池(默认 HikariCP)

Spring 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)避免 N+1 查询问题

N+1 查询是指 “查询 1 条主数据,触发 N 条从数据查询”(如查询 10 个用户,每个用户查询其订单,共触发 1+10=11 次查询),解决方案:

  • 使用关联查询:通过LEFT JOIN一次性查询主从数据(如 XML 式开发中的多表关联);
  • 使用 MyBatis-Plus 的association/collection标签:在resultMap中配置关联查询,避免 N+1。

六、总结

Spring Boot 集成 MyBatis 的核心是 “自动配置”,通过mybatis-spring-boot-starter消除了传统 MyBatis 的繁琐配置,让开发者专注于 SQL 与业务逻辑。本文覆盖了两种开发模式(注解式适合简单 SQL,XML 式适合复杂 SQL),以及分页、事务、多数据源等企业级特性,满足从中小项目到大型系统的需求。

掌握 Spring Boot 集成 MyBatis 的关键在于:

  1. 理解自动配置逻辑,熟悉application.yml中的核心参数(数据源、XML 路径、下划线转驼峰);
  2. 根据 SQL 复杂度选择开发模式(注解式 / XML 式),避免 SQL 与代码过度耦合;
  3. 合理使用分页插件、事务管理、多数据源等高级特性,解决企业级需求;
  4. 关注性能优化(连接池、缓存、避免 N+1),确保应用高效稳定运行。

随着 MyBatis-Plus 等增强工具的普及,Spring Boot 与 MyBatis 的整合会更加高效,未来在云原生、微服务场景中仍将是数据访问层的主流方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/5 11:21:08

前端设计模式:轻量级实战指南

目录 1.简介 一. 什么是设计模式&#xff1f; 二、前端设计模式的“不一样” 1. 语言特性&#xff1a;弱类型、原型继承&#xff0c;让“类式模式”变“轻量” 2. 场景核心&#xff1a;DOM、异步、组件化&#xff0c;让模式“靶向落地” &#xff08;1&#xff09;DOM操作…

作者头像 李华
网站建设 2026/2/10 2:25:51

26、全功能应用:拼写检查与索引生成

全功能应用:拼写检查与索引生成 在文本处理领域,拼写检查和索引生成是两项重要的任务。下面将详细介绍如何使用相关工具和脚本完成这些任务。 拼写检查脚本 拼写检查脚本通过设置一个 shell 变量 AWKLIB 来指定 spellcheck.awk 脚本的位置。符号 “$*” 会展开为脚本名…

作者头像 李华
网站建设 2026/2/8 6:19:15

4.1 AI代码研究方法:快速掌握大型开源项目核心代码库

4.1 AI代码研究方法:快速掌握大型开源项目核心代码库 在现代软件开发中,理解和掌握大型开源项目的代码库是一项关键技能。传统的代码阅读方法往往效率低下且容易迷失在复杂的代码结构中。本节将介绍如何利用AI工具(如Cursor、Claude Code等)来快速、系统地解构和理解大型开…

作者头像 李华
网站建设 2026/2/11 20:11:17

4.1 AI赋能代码研究:快速解构大型开源项目

4.1 AI赋能代码研究:快速解构大型开源项目 在软件开发过程中,我们经常需要理解和学习大型开源项目的代码结构和实现原理。传统的代码阅读方式往往效率低下,难以快速把握项目的整体架构和核心逻辑。本节课将介绍如何利用AI工具(特别是Cursor和Claude Code)来快速解构和理解…

作者头像 李华
网站建设 2026/2/3 10:41:09

魔改YOLO13高阶版改进之结合C3k2与DySnakeConv电信天线设备检测

1. 魔改YOLO13高阶版改进之结合C3k2与DySnakeConv电信天线设备检测 1.1. &#x1f680; 前言 在当今5G和物联网快速发展的时代&#xff0c;电信天线设备的检测与维护变得越来越重要&#xff01;&#x1f50d; 传统的检测方法效率低下且容易出错&#xff0c;而基于深度学习的目…

作者头像 李华