news 2026/8/13 5:32:14

MyBatis 全面解析 Spring Boot 集成实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 / 注解、缓存策略、分页方式),兼顾灵活性和性能。

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

问题:本地启动项目,想让其他人在他们自己的电脑也能访问该项目。需要怎么配置代理?

问题:本地启动项目&#xff0c;想让其他人在他们自己的电脑也能访问该项目。需要怎么配置代理?1、方案1.1 修改package.json配置1.2 创建配置文件(推荐)2、获取本机IP地址3、防火墙配置4、让其他人访问5、完整操作步骤6、注意事项7、高级需求:ngrok实现内网穿透1、方案 1.1 修…

作者头像 李华
网站建设 2026/8/12 4:40:01

儿童睡前故事:LobeChat每天讲不一样的童话

儿童睡前故事&#xff1a;LobeChat每天讲不一样的童话 在无数个夜晚&#xff0c;当孩子依偎在床头&#xff0c;轻声说“妈妈&#xff0c;再讲一个故事吧”的时候&#xff0c;很多家长心里既温暖又无奈——创意枯竭、疲惫不堪&#xff0c;翻来覆去还是那几个老掉牙的情节。而市面…

作者头像 李华
网站建设 2026/8/12 6:51:23

DownKyi终极使用指南:解锁B站视频下载的专业技巧

DownKyi终极使用指南&#xff1a;解锁B站视频下载的专业技巧 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xff09…

作者头像 李华
网站建设 2026/8/12 15:55:36

AI中文智能教学系统:让技术读懂你的学习需求

在传统中文教学里&#xff0c;“千人一面”的课堂、反馈滞后的作业批改、个性化需求难满足等问题&#xff0c;一直困扰着学习者和教师。而AI中文智能教学系统的出现&#xff0c;正凭借核心技术打破这些壁垒&#xff0c;让中文学习变得更高效、更精准。其实这套系统的核心逻辑很…

作者头像 李华
网站建设 2026/8/12 6:51:03

Linux CFS(完全公平调度器)原理与实现细节全解析(2)

接前一篇文章&#xff1a;Linux CFS&#xff08;完全公平调度器&#xff09;原理与实现细节全解析&#xff08;1&#xff09; 二、核心概念与关键抽象 2.2 调度实体&#xff08;sched_entity&#xff09; CFS的调度基本单位不是传统的task_struct&#xff0c;而是更通用的抽象…

作者头像 李华
网站建设 2026/8/12 23:25:42

LobeChat多模型切换功能实测:自由选择最佳输出

LobeChat多模型切换功能实测&#xff1a;自由选择最佳输出 在今天的大模型时代&#xff0c;你有没有遇到过这样的场景&#xff1f;写一份技术文档时希望用 GPT-4 保证质量&#xff0c;但日常闲聊又觉得成本太高&#xff1b;处理公司内部数据时想调用本地部署的 Llama3 模型确保…

作者头像 李华