Mybatis基础

警告
本文最后更新于 2023-05-10,文中内容可能已过时。

一、MyBatis 核心基础 🔴

1、MyBatis 是什么

MyBatis 是一款轻量级、半自动ORM持久层框架。自定义SQL、灵活度高、性能优秀,支持高级映射、延迟加载、缓存、插件扩展,是互联网行业主流持久层框架。

半自动ORM:手写SQL(灵活)+ 自动封装结果集(简化代码)。

2、核心九大组件 🔴

  1. Configuration:全局配置类,加载所有xml、注解、环境配置。

  2. SqlSessionFactory:会话工厂,生产SqlSession,全局单例。

  3. SqlSession:数据库会话,负责CRUD,非线程安全,用完关闭。

  4. Executor:执行器,SQL调度核心,处理缓存、事务。

  5. MappedStatement:封装一条SQL语句、ID、参数、返回值。

  6. ParameterHandler:参数预处理、赋值、防止注入。

  7. StatementHandler:封装JDBC Statement,执行SQL。

  8. ResultSetHandler:结果集封装,映射为Java对象。

  9. TypeHandler:类型转换器,Java类型 <=> 数据库类型。

3、完整执行流程 🔴

  1. 加载核心配置文件,初始化Configuration全局配置;

  2. 构建SqlSessionFactory工厂对象;

  3. 获取SqlSession会话,默认开启事务、不自动提交;

  4. 通过动态代理生成Mapper接口代理对象;

  5. Executor执行器获取MappedStatement,封装SQL;

  6. ParameterHandler完成参数预编译赋值;

  7. StatementHandler执行JDBC SQL;

  8. ResultSetHandler反射封装结果集;

  9. 关闭会话、释放资源,事务手动提交。

二、语法核心考点 🔴

1、#{} 和 ${} 硬核区别 🔴

对比项 #{} 预编译占位符 ${} 字符串直接拼接
编译方式 预编译、生成?占位符 直接字符串拼接SQL
安全性能 防SQL注入(底层PreparedStatement) 存在严重注入风险
数据类型 自动加单引号 原样拼接,不加引号
使用场景 普通条件查询(99%场景) 动态表名、排序字段

生产强制规范:优先使用#{},禁止随意使用${}。

2、动态SQL标签(生产常用)

  1. if:非空判断,动态拼接条件。

  2. where:自动去除多余and/or,避免语法报错。

  3. set:更新语句,自动去除尾部逗号。

  4. foreach:批量遍历,in查询、批量插入。

  5. trim:自定义截取前后多余字符,万能标签。

  6. bind:绑定变量,拼接模糊查询,防止数据库兼容问题。

3、resultType 和 ResultMap 区别

  1. resultType:简单类型、实体类,自动映射,要求字段名和属性名一致。

  2. resultMap:自定义映射,解决字段不一致、复杂关联、嵌套查询、类型转换。

三、关联查询 & 懒加载 🔴

1、三种关联关系

  • 一对一:用户-用户详情,association标签。

  • 一对多:订单-订单项,collection标签。

  • 多对多:用户-角色,中间表拆分,转化为一对多。

2、两种查询方式

(1)嵌套结果查询(联表查询)

一条SQL联表查询,一次性查出所有数据,性能高,适合数据量小场景。

(2)嵌套查询(分步查询)

先查主表,再查关联表,SQL多条,配合懒加载优化性能。

3、N+1 查询问题 🟠

3.1 现象

查询10条用户,额外执行10条SQL查询关联订单,一共11次SQL查询。

3.2 解决方案

  1. 使用联表嵌套结果,一条SQL搞定;

  2. 开启懒加载,按需加载关联数据;

  3. 禁止循环中单独查询数据库。

4、懒加载机制

  • 核心配置:lazyLoadingEnabled=true,默认关闭。

  • 原理:分步查询时,默认不加载关联数据,使用属性时才触发查询。

  • 优点:减少无效SQL,提升查询性能。

四、缓存机制 🔴

1、一级缓存(本地缓存)

1.1 特性

  • 默认开启,无需手动配置。

  • 作用域:同一个SqlSession

  • 缓存位置:内存,底层HashMap。

1.2 失效场景 🔴

  1. SqlSession手动关闭、清空;

  2. 执行增删改操作(自动清空缓存);

  3. 手动调用clearCache()。

2、二级缓存(全局缓存)

2.1 特性

  • 默认关闭,需要手动开启。

  • 作用域:同一个Mapper映射文件,跨SqlSession共享。

  • 底层:默认PerpetualCache,内存存储。

2.2 开启条件(全部满足)

  1. 全局配置开启:cacheEnabled=true;

  2. Mapper文件添加 <cache/> 标签;

  3. 实体类实现序列化接口Serializable;

  4. SqlSession正常关闭,缓存才写入二级缓存。

2.3 执行顺序

二级缓存 → 一级缓存 → 数据库

3、缓存穿透、击穿、雪崩(Mybatis层面)

  • 穿透:查询不存在数据,缓存不存储,直连数据库 → 空值缓存。

  • 击穿:热点数据缓存失效,大量请求打库 → 延长缓存时间。

  • 雪崩:大量缓存同时失效 → 过期时间随机偏移。

五、插件机制(高级工程师必懂)

1、四大拦截对象(拦截顺序)

Mybatis仅允许拦截四大核心对象,无法拦截其他组件:

  1. Executor:执行器,拦截增删改查、事务(最先拦截)。

  2. ParameterHandler:参数赋值拦截。

  3. StatementHandler:SQL语句修改、分页改写(PageHelper原理)。

  4. ResultSetHandler:结果集封装、后置处理。

2、插件开发原理

  1. 实现Interceptor拦截器接口;

  2. 注解指定拦截签名、拦截方法;

  3. 生成JDK动态代理,对目标对象增强;

  4. 插件链逐层执行,责任链模式。

3、生产常用插件

  • PageHelper:分页插件,改写SQL拼接limit;

  • 通用Mapper:简化CRUD;

  • 自定义数据脱敏、字段加密插件。

六、事务 & 批量操作(生产重点)

1、事务管理

  • Mybatis默认开启手动事务,不自动提交;

  • 整合Spring后,交由Spring事务管理器控制;

  • 传播行为、隔离级别完全依赖Spring。

2、批量操作优化

2.1 普通foreach批量(不推荐)

拼接多条insert语句,SQL过长、数据库解析压力大。

2.2 SqlSession批量模式(生产推荐)

开启批量Executor,底层攒SQL,一次性提交数据库,减少IO次数。

1
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);

七、生产高频坑点 🔴

  1. 驼峰命名失效:未开启mapUnderscoreToCamelCase,数据库下划线无法映射Java驼峰属性。

  2. 一级缓存脏数据:同会话查询、中间无修改,读取缓存旧数据,分布式环境建议关闭一级缓存。

  3. 二级缓存脏数据:多Mapper修改同一张表,缓存不互通,出现脏数据。

  4. foreach批量过多:in查询参数过长,超出数据库max_allowed_packet限制。

  5. 时间类型异常:未配置时区、类型处理器,日期时差8小时。

  6. 空字符串判断:if标签判断''空字符串,引发查询逻辑异常。

八、高级优化方案 🔴

  1. 禁止select *:明确查询字段,减少IO、减少内存占用。

  2. 合理使用分页:禁止一次性查询全表大数据。

  3. 关闭不必要缓存:分布式环境关闭一级、二级缓存,避免脏数据,改用Redis缓存。

  4. 批量操作使用Batch模式:减少数据库连接次数。

  5. 开启预编译:防止SQL注入,复用执行计划。

  6. 避免大事务:拆分长事务,减少数据库锁等待。

  7. SQL优化:联表代替子查询,避免嵌套循环查询。

九、总结

MyBatis是半自动ORM持久层框架,核心包含九大组件,执行流程分为配置加载、会话创建、SQL解析、参数处理、执行封装;

#{}预编译防注入,${}仅用于动态表名;拥有一级、二级两级缓存,分布式环境建议关闭原生缓存改用Redis;

支持动态SQL、关联查询与懒加载,需规避N+1查询问题;

基于四大拦截对象实现插件扩展,生产常用批量执行器优化批量插入,同时规避缓存脏数据、SQL过长、映射异常等坑点,配合Spring事务完成数据库持久化操作。