当前位置: 首页 > news >正文

MyBatis 全面解析 Spring Boot 集成实战

目录

一、MyBatis 核心原理

1. 核心架构

2. 核心特性

二、Spring Boot 集成 MyBatis 实战

1. 环境准备

(1)依赖引入(Maven)

(2)核心配置(application.yml)

2. 代码实现

(1)数据库表设计

(2)实体类

(3)Mapper 接口

(4)Mapper XML 文件(resources/mapper/UserMapper.xml)

(5)Service 层(业务逻辑)

(6)启动类

3. 测试验证

三、MyBatis 高级用法

1. 分页插件(PageHelper)

(1)引入依赖

(2)配置(application.yml)

(3)使用示例

2. 二级缓存

(1)开启全局缓存(application.yml)

(2)在 Mapper XML 中配置缓存

3. 自定义类型处理器

(1)自定义类型处理器

(2)配置类型处理器

(3)使用示例(XML)

四、常见问题与最佳实践

1. 常见问题

2. 最佳实践

五、总结


MyBatis 是一款轻量级、高性能的持久层框架,核心优势是灵活控制 SQL+简化 JDBC 操作,既保留了手写 SQL 的灵活性,又解决了传统 JDBC 繁琐的资源管理、参数设置和结果映射问题。本文将从核心原理、核心特性、Spring Boot 集成实战、高级用法四个维度全面解析 MyBatis。

一、MyBatis 核心原理

1. 核心架构

MyBatis 的核心执行流程可概括为:

plaintext

配置加载 → SqlSessionFactory 创建 → SqlSession 获取 → Mapper 代理生成 → SQL 执行 → 结果映射

关键组件说明:

组件作用
SqlSessionFactory会话工厂(单例),负责创建 SqlSession,通过 SqlSessionFactoryBuilder 构建
SqlSession数据库会话(非线程安全),封装增删改查 API,每次请求 / 事务独立创建
Mapper 接口数据操作接口,MyBatis 通过动态代理生成实现类,接口方法与 SQL 绑定
Mapper XML / 注解存储 SQL 语句、参数映射、结果映射规则
Configuration核心配置类,存储全局配置(数据源、事务、别名、映射器等)
Executor执行器,负责 SQL 执行(SimpleExecutor/BatchExecutor/ReuseExecutor)
StatementHandler处理 Statement(PreparedStatement/Statement),设置参数、执行 SQL
ResultSetHandler将 ResultSet 映射为 Java 对象(结果映射核心)

2. 核心特性

  • 动态 SQL:通过<if>/<where>/<foreach>等标签动态拼接 SQL,适配复杂条件
  • 结果映射:支持一对一、一对多、多对多关联映射,解决字段名与属性名不一致问题
  • 参数解析:支持多种参数类型(基本类型、实体、Map、注解参数),防止 SQL 注入
  • 缓存机制:一级缓存(SqlSession 级别)+ 二级缓存(Mapper 级别),提升查询性能
  • 插件扩展:支持自定义插件(如分页、拦截 SQL),扩展执行流程

二、Spring Boot 集成 MyBatis 实战

1. 环境准备

(1)依赖引入(Maven)

xml

<!-- Spring Boot 父工程 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> <relativePath/> </parent> <dependencies> <!-- MyBatis + Spring Boot 整合启动器 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> <!-- MySQL 驱动 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- Spring Boot 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Lombok(简化实体类) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
(2)核心配置(application.yml)

yaml

# 数据源配置 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 username: root password: 123456 # 连接池配置(默认 HikariCP) hikari: maximum-pool-size: 10 # 最大连接数 minimum-idle: 5 # 最小空闲连接 idle-timeout: 300000 # 空闲连接超时时间 # MyBatis 配置 mybatis: # Mapper XML 文件位置 mapper-locations: classpath:mapper/**/*.xml # 实体类别名包(简化 XML 中 type 配置) type-aliases-package: com.example.mybatis.entity # 全局配置 configuration: map-underscore-to-camel-case: true # 自动下划线转驼峰(如 user_name → userName) cache-enabled: false # 关闭二级缓存(默认开启,按需开启) log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志 default-statement-timeout: 30 # SQL 执行超时时间

2. 代码实现

(1)数据库表设计

sql

CREATE DATABASE IF NOT EXISTS mybatis_demo; USE mybatis_demo; -- 用户表 CREATE TABLE `user` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `user_name` VARCHAR(50) NOT NULL COMMENT '用户名', `age` INT COMMENT '年龄', `email` VARCHAR(100) COMMENT '邮箱', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) COMMENT '用户表'; -- 订单表(一对多关联:一个用户多个订单) CREATE TABLE `order` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `order_no` VARCHAR(32) NOT NULL COMMENT '订单号', `user_id` BIGINT NOT NULL COMMENT '用户ID', `amount` DECIMAL(10,2) NOT NULL COMMENT '订单金额', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ) COMMENT '订单表';
(2)实体类

java

运行

// User.java package com.example.mybatis.entity; import lombok.Data; import java.time.LocalDateTime; import java.util.List; @Data public class User { private Long id; private String userName; // 对应数据库 user_name(下划线转驼峰) private Integer age; private String email; private LocalDateTime createTime; // 一对多关联:用户的订单列表 private List<Order> orderList; } // Order.java package com.example.mybatis.entity; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; @Data public class Order { private Long id; private String orderNo; private Long userId; private BigDecimal amount; private LocalDateTime createTime; }
(3)Mapper 接口

java

运行

// UserMapper.java package com.example.mybatis.mapper; import com.example.mybatis.entity.User; import org.apache.ibatis.annotations.*; import java.util.List; // 方式1:注解扫描(推荐),也可在启动类加 @MapperScan("com.example.mybatis.mapper") @Mapper public interface UserMapper { /** * 根据ID查询用户(含订单列表) */ User selectById(Long id); /** * 条件查询用户(动态SQL) */ List<User> selectByCondition(@Param("userName") String userName, @Param("age") Integer age); /** * 新增用户(主键回填) */ int insert(User user); /** * 更新用户(动态更新) */ int update(User user); /** * 删除用户 */ int delete(Long id); }
(4)Mapper 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"> <!-- namespace 必须与 Mapper 接口全限定名一致 --> <mapper namespace="com.example.mybatis.mapper.UserMapper"> <!-- 基础结果映射:解决字段名与属性名映射 --> <resultMap id="BaseResultMap" type="User"> <id column="id" property="id"/> <!-- 主键映射 --> <result column="user_name" property="userName"/> <result column="age" property="age"/> <result column="email" property="email"/> <result column="create_time" property="createTime"/> </resultMap> <!-- 关联结果映射:用户 + 订单列表(一对多) --> <resultMap id="UserWithOrderResultMap" type="User" extends="BaseResultMap"> <!-- collection 映射一对多关系 --> <collection property="orderList" ofType="Order" column="id" select="com.example.mybatis.mapper.OrderMapper.selectByUserId"/> </resultMap> <!-- 根据ID查询用户(关联订单) --> <select id="selectById" resultMap="UserWithOrderResultMap"> SELECT id, user_name, age, email, create_time FROM user WHERE id = #{id} </select> <!-- 条件查询(动态SQL) --> <select id="selectByCondition" resultMap="BaseResultMap"> SELECT id, user_name, age, email, create_time FROM user <where> <if test="userName != null and userName != ''"> AND user_name LIKE CONCAT('%', #{userName}, '%') </if> <if test="age != null"> AND age = #{age} </if> </where> </select> <!-- 新增用户:useGeneratedKeys 开启主键回填,keyProperty 绑定实体主键 --> <insert id="insert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user (user_name, age, email) VALUES (#{userName}, #{age}, #{email}) </insert> <!-- 动态更新:set 标签自动处理逗号 --> <update id="update"> UPDATE user <set> <if test="userName != null and userName != ''"> user_name = #{userName}, </if> <if test="age != null"> age = #{age}, </if> <if test="email != null and email != ''"> email = #{email} </if> </set> WHERE id = #{id} </update> <!-- 删除用户 --> <delete id="delete"> DELETE FROM user WHERE id = #{id} </delete> </mapper>

xml

<!-- OrderMapper.xml(resources/mapper/OrderMapper.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.mybatis.mapper.OrderMapper"> <resultMap id="BaseResultMap" type="Order"> <id column="id" property="id"/> <result column="order_no" property="orderNo"/> <result column="user_id" property="userId"/> <result column="amount" property="amount"/> <result column="create_time" property="createTime"/> </resultMap> <select id="selectByUserId" resultMap="BaseResultMap"> SELECT id, order_no, user_id, amount, create_time FROM `order` WHERE user_id = #{userId} </select> </mapper>
(5)Service 层(业务逻辑)

java

运行

// UserService.java package com.example.mybatis.service; import com.example.mybatis.entity.User; import com.example.mybatis.mapper.UserMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service public class UserService { @Resource private UserMapper userMapper; /** * 根据ID查询用户 */ public User selectById(Long id) { return userMapper.selectById(id); } /** * 条件查询 */ public List<User> selectByCondition(String userName, Integer age) { return userMapper.selectByCondition(userName, age); } /** * 新增用户(事务控制) */ @Transactional(rollbackFor = Exception.class) public int insert(User user) { return userMapper.insert(user); } /** * 更新用户 */ @Transactional(rollbackFor = Exception.class) public int update(User user) { return userMapper.update(user); } /** * 删除用户 */ @Transactional(rollbackFor = Exception.class) public int delete(Long id) { return userMapper.delete(id); } }
(6)启动类

java

运行

package com.example.mybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // 方式2:批量扫描 Mapper 接口(替代每个接口加 @Mapper) // @MapperScan("com.example.mybatis.mapper") @SpringBootApplication public class MybatisDemoApplication { public static void main(String[] args) { SpringApplication.run(MybatisDemoApplication.class, args); } }

3. 测试验证

java

运行

package com.example.mybatis; import com.example.mybatis.entity.User; import com.example.mybatis.service.UserService; import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest public class UserServiceTest { @Resource private UserService userService; @Test public void testInsert() { User user = new User(); user.setUserName("张三"); user.setAge(25); user.setEmail("zhangsan@example.com"); int rows = userService.insert(user); System.out.println("新增行数:" + rows + ",用户ID:" + user.getId()); } @Test public void testSelectById() { User user = userService.selectById(1L); System.out.println("用户信息:" + user); System.out.println("用户订单:" + user.getOrderList()); } @Test public void testSelectByCondition() { List<User> userList = userService.selectByCondition("张", 25); System.out.println("条件查询结果:" + userList); } @Test public void testUpdate() { User user = new User(); user.setId(1L); user.setAge(26); int rows = userService.update(user); System.out.println("更新行数:" + rows); } @Test public void testDelete() { int rows = userService.delete(1L); System.out.println("删除行数:" + rows); } }

三、MyBatis 高级用法

1. 分页插件(PageHelper)

(1)引入依赖

xml

<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.7</version> </dependency>
(2)配置(application.yml)

yaml

pagehelper: helper-dialect: mysql # 数据库方言 reasonable: true # 页码合理化(页码<=0 查第1页,页码>总页数查最后一页) support-methods-arguments: true # 支持通过参数传递分页参数
(3)使用示例

java

运行

@Test public void testPage() { // 第1页,每页10条 PageHelper.startPage(1, 10); List<User> userList = userService.selectByCondition(null, null); // 分页结果封装 PageInfo<User> pageInfo = new PageInfo<>(userList); System.out.println("总条数:" + pageInfo.getTotal()); System.out.println("总页数:" + pageInfo.getPages()); System.out.println("当前页数据:" + pageInfo.getList()); }

2. 二级缓存

(1)开启全局缓存(application.yml)

yaml

mybatis: configuration: cache-enabled: true # 开启二级缓存(默认 true)
(2)在 Mapper XML 中配置缓存

xml

<!-- UserMapper.xml 中添加 --> <cache eviction="LRU" # 缓存淘汰策略(LRU/FIFO/SOFT/WEAK) flushInterval="60000" # 自动刷新时间(毫秒) size="1024" # 缓存最大条目数 readOnly="true"/> # 只读缓存(性能更高)

注意:二级缓存基于 Mapper 级别,仅适用于查询频繁、修改较少的场景;实体类需实现Serializable接口。

3. 自定义类型处理器

解决特殊类型映射(如 JSON 字段、枚举),示例:将List<String>映射为数据库 JSON 字段。

(1)自定义类型处理器

java

运行

package com.example.mybatis.typehandler; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import com.alibaba.fastjson2.JSON; import java.sql.*; import java.util.List; public class ListTypeHandler extends BaseTypeHandler<List<String>> { @Override public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, JSON.toJSONString(parameter)); } @Override public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException { return JSON.parseArray(rs.getString(columnName), String.class); } @Override public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return JSON.parseArray(rs.getString(columnIndex), String.class); } @Override public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return JSON.parseArray(cs.getString(columnIndex), String.class); } }
(2)配置类型处理器

yaml

mybatis: type-handlers-package: com.example.mybatis.typehandler # 扫描自定义类型处理器
(3)使用示例(XML)

xml

<resultMap id="BaseResultMap" type="User"> <result column="tags" property="tags" typeHandler="com.example.mybatis.typehandler.ListTypeHandler"/> </resultMap>

四、常见问题与最佳实践

1. 常见问题

问题场景解决方案
字段名与属性名不一致开启map-underscore-to-camel-case或自定义resultMap
SQL 注入风险使用#{}(预编译),避免${}(直接拼接);参数校验
关联查询性能差按需使用association/collection,避免 N+1 查询(可通过fetchType="lazy"懒加载)
分页查询繁琐集成 PageHelper 插件
主键回填失败确保useGeneratedKeys="true"+keyProperty绑定正确,数据库主键自增

2. 最佳实践

  1. SQL 管理:复杂 SQL 写 XML,简单 SQL 用注解(如@Select
  2. 参数传递:多参数使用@Param注解,避免 Map 传递(可读性差)
  3. 结果映射:统一使用resultMap,避免重复配置
  4. 事务控制:在 Service 层加@Transactional,指定rollbackFor = Exception.class
  5. 日志调试:开启 MyBatis SQL 日志,便于定位问题
  6. 性能优化
    • 避免全表扫描,给查询字段加索引
    • 合理使用缓存(一级缓存默认开启,二级缓存按需开启)
    • 批量操作使用foreach+BatchExecutor

五、总结

MyBatis 与 Spring Boot 的整合核心是自动配置+简化开发

  • Spring Boot 自动创建SqlSessionFactorySqlSession,无需手动管理
  • 通过@Mapper@MapperScan扫描 Mapper 接口,动态代理生成实现类
  • 结合 XML / 注解灵活编写 SQL,适配各类业务场景

掌握 MyBatis 的核心是理解映射规则动态 SQL,而 Spring Boot 集成则重点关注配置简化、插件扩展和事务管理。在实际开发中,需结合业务场景选择合适的用法(XML / 注解、缓存策略、分页方式),兼顾灵活性和性能。

http://www.cnnetsun.cn/news/92460.html

相关文章:

  • Zipkin 深度解析:核心原理、集成实战与最佳实践
  • Windows右键菜单管理终极指南:让你的桌面操作效率提升300%
  • 驾驶员分心疲劳驾驶打电话打瞌睡喝水检测数据集VOC+YOLO格式8864张12类别
  • 彼得林奇的“长期价值创造“在网络效应企业中的衡量
  • 使用pytorch进行batch_size分批训练,并使用adam+lbfgs算法——波士顿房价预测
  • 如何快速实现Unity游戏多语言支持:新手完整指南
  • [漫画]《软件方法》逃避思考的伪创新舒适区
  • [漫画]喜欢自编图形的遮羞布
  • LobeChat能否支持星际语言翻译?外星文明假说沟通模型构建
  • 软考 系统架构设计师系列知识点之面向服务架构设计理论与实践(16)
  • J2EE技术及应用实验及报告(黑龙江大学)
  • AI农情数据要素服务平台:让种地靠数据,丰产不盲目
  • JavaScript学习笔记:14.类型数组
  • LobeChat能否定制品牌LOGO?白标解决方案
  • navigatetominiprogram实战应用案例分享
  • 1小时验证创意:右键菜单管理工具原型开发
  • LobeChat能否对接土卫六湖泊数据?甲烷循环与外星地貌研究
  • Axolotl推理缓存:5倍性能提升与70%成本优化的架构革命
  • vue3事件处理详解
  • YOLO目标检测入门:手把手教你跑通第一个demo
  • 1小时搭建:VSCode远程开发环境原型
  • 电商项目实战:Vue3父子组件传值最佳实践
  • 【LLM基础教程】从序列切分到上下文窗口01_为什么序列建模必须切分数据
  • 备赛三--
  • 高并发时代的“确定性”挑战——为何稳定性正在成为 JVM 的下一场核心竞争?
  • C语言之最大公约数和最小公倍数问题
  • LobeChat能否对接Telegram Bot?跨平台消息同步实现
  • AI如何用博图加速工业自动化开发
  • C++:二叉搜索树(BST)完全指南(从概念原理、核心操作到底层实现)
  • Splashtop AEM 在 G2冬季报告中斩获“最佳预估 ROI”殊荣