Mybatis总结

Mybatis 是Java生态中主流的ORM框架。

基础概念

1、Mybatis、和Jdbc、Hibernate的区别:
定义 :Mybatis是一款半自动化的ORM框架,基于XML文件或注解配置SQL语句,将Java对象与数据库表进行映射,简化JDBC代码编写(不需要手动处理connention、ResultSet等),同时保留SQL的灵活性。

对比JDBC和HIbernate:

维度 JDBC MyBatis Hibernate
ORM 类型 无(手动映射) 半自动(需手动写 SQL) 全自动(无需手动写 SQL)
SQL 控制度 完全手动编写,灵活但冗余 手动编写 SQL,灵活且简化代码 自动生成 SQL,灵活性低
学习成本 低(原生 API) 中(需理解配置与映射规则) 高(需掌握 HQL、缓存、级联等)
适用场景 简单项目、需极致控制 SQL 中小型项目、需灵活调优 SQL 大型项目、SQL 无需频繁调整
代码冗余度 高(重复处理连接、结果集) 低(框架封装通用操作) 极低(几乎无 SQL 代码)

2、Mybatis的主要组成,各自功能

Mybatis各个组件主要围绕:SQL执行流程开展。主要有:

  • SqlSessionFactoryBuilder:构建器,基于XML或Java配置创建SqlSessionFactory。创建后即可销毁(生命周期最短)。
  • SqlSessionFactory:工厂,创建SqlSession的单例对象(整个应用生命周期内唯一),负责初始化Mybatis核心配置(如数据源。映射文件)。
  • SqlSession:会话,代表与数据库的一次交互(生命周期为一次请求/事务),提供增删改查的API(如:selectOne()、insert()),可直接执行SQL或调用Mapper接口。
  • Mapper接口:映射器 ,无实现类的接口,Mybatis通过动态代理生成 其实现类。将接口方法与XML/注解中的SQL语句绑定(原理:接口+SQL映射)。
  • Excutor:执行器 ,Mybatis核心执行组件(由SqlSession内部调用),负责解析SQL,管理缓存,执行JDBC操作。分为3类型:
    • SimpleExecutor:默认,每次执行SQL都创建一个新的Statement;
    • ReuseExcutor:复用Statement(根据SQL语句缓存);
    • BatchExecutor:批量执行SQL(使用批量插入/更新);
  • MapperStatement:映射语句,封装XML/注解中的SQL配置(如SQL语句、参数类型、结果类型),是Mybatis解析SQL的核心载体。

配置与映射规则

1、Mybatis的核心配置文件(mybatis-config.xml)包含的关键节点。

核心配置文件定义 MyBatis 全局参数,节点需按固定顺序排列(否则报错),关键节点及作用:

  • properties:引入外部属性文件(如 db.properties),避免硬编码数据库连接信息;
  • settings:配置 MyBatis 全局开关(如缓存开启、驼峰命名映射、日志实现);
  • typeAliases:为 Java 类型定义别名(如 com.example.User 别名 User),简化映射文件中的类型配置;
  • typeHandlers:自定义类型处理器(如将 Java 枚举与数据库 VARCHAR 类型映射);
  • objectFactory:自定义对象工厂(默认使用 DefaultObjectFactory,用于创建结果集映射的 Java 对象);
  • plugins:配置插件(如分页插件 PageHelper、逻辑删除插件),通过拦截器增强 MyBatis 功能;
  • environments:配置数据库环境(支持多环境,如开发、测试、生产),包含transactionManager(事务管理器)和 dataSource(数据源);
  • mappers:注册 SQL 映射文件或 Mapper 接口(核心,告诉 MyBatis 去哪里找 SQL)。

2、Mybati中如何实现【表字段名与Java对象属性名不一致问题】的映射。

解决方式:

  • SQL别名(简单直接):在SQL中给字段起别名,与Java属性对应。
  • resultMap映射(推荐,可复用):在映射文件中定义resultMap,指定子u但与属性映射关系
  • 开启驼峰命名自动映射(全局配置)。
  • 自定义TypeHandler(特殊场景):针对复杂场景(如JSON字段与Java对象,自定义Type Handler处理映射逻辑)。

3、Mybatis中#{}和${}区别,推荐使用#{}

两者都是 用于向SQL中注入参数 的,但是底层实现和安全性不同

维度 #{} ${}
实现原理 预编译SQL(使用?占位符),参数通过PreparedStatement注入字符串拼接(直接将参数替换到SQL中
安全性 防止SQL注入风险(参数当作值,不解析SQL语法) 存在SQL注入风险(参数可能拼接为SQL关键字)
适用场景 普通参数注入(条件查询、新增数据的字段值) 动态SQL片段(如表名、排序字段,需保证参数安全)

:除非需要动态拼接 表名/字段名(且参数需要严格验证),否则优先使用#{}避免SQL注入。

动态SQL和关联查询

1、Mybatis 提供的动态SQL标签:

  • if 条件判断,满足条件则拼接
  • where:自动处理条件中的AND/OR(若条件都不满足则不拼接where,若第一个条件带AND则自动删除)
  • choose when otherwise:类似于if else if-else,只执行第一个满足条件的where,否则执行otherwise
  • foreach:遍历集合(List、Array),常用于In条件或批量插入;
  • set:用于更新语句。
  • trim:自定义 拼接规则,可替代where和if
  • sql和include:抽取重复SQL片段,通过include复用,减少冗余。

2、Mybatis 处理一对一和 一对多问题。
关联查询的核心:通过ResultMap中的 <association/ >(一对一)和<collection/ >(一对多)标签映射关系。

1)一对一映射:

核心标签:<association/ > ,映射「单个关联对象」,属性如下:

  • property:Java 对象中关联属性名(如 userCard);
  • javaType:关联属性的类型(如 com.example.UserCard);
  • column:关联查询的外键字段(如 user_id,用于子查询);
  • select:子查询的 ID(用于「分步查询」),或直接在 resultMap 中配置字段映射(用于「关联查询」)。
xml 复制代码
<resultMap id="UserWithCardMap" type="User">
  <id column="user_id" property="userId"/>
  <result column="user_name" property="userName"/>
  <!-- 一对一关联 UserCard -->
  <association property="userCard" javaType="UserCard">
    <id column="card_id" property="cardId"/>
    <result column="card_no" property="cardNo"/>
  </association>
</resultMap>

<select id="getUserWithCard" resultMap="UserWithCardMap">
  SELECT u.user_id, u.user_name, c.card_id, c.card_no
  FROM user u
  LEFT JOIN user_card c ON u.user_id = c.user_id
  WHERE u.user_id = #{userId}
</select>

2)一对多

核心标签:,映射「关联对象集合」,属性如下:

  • property:Java 对象中集合属性名(如 orders);
  • ofType:集合中元素的类型(如 com.example.Order,注意区别于 javaType);
  • column:外键字段,其他属性与 类似。
xml 复制代码
<resultMap id="UserWithOrdersMap" type="User">
  <id column="user_id" property="userId"/>
  <result column="user_name" property="userName"/>
  <!-- 一对多关联 Order 集合 -->
  <collection property="orders" ofType="Order">
    <id column="order_id" property="orderId"/>
    <result column="order_no" property="orderNo"/>
    <result column="create_time" property="createTime"/>
  </collection>
</resultMap>

<select id="getUserWithOrders" resultMap="UserWithOrdersMap">
  SELECT u.user_id, u.user_name, o.order_id, o.order_no, o.create_time
  FROM user u
  LEFT JOIN `order` o ON u.user_id = o.user_id
  WHERE u.user_id = #{userId}
</select>

缓存机制

Mybatis提供两级缓存

1、Mybatis提供两级缓存,各自的特点:

一级缓存(本地缓存)、二级缓存(全局缓存)。核心是减少数据库查询次数。

缓存级别 作用范围 生命周期 开启方式 核心特点
一级缓存 SqlSession内部(会话级) 随着SQLSession关闭而销毁 默认开启,无需配置 基于Hash Map实现,同一SqlSession内重复查询同一SQL会命中缓存
二级缓存 SqlSessionFactory全局(应用级) 随着应用关闭而销毁 需要手动开启(映射文件附加<cache/ >) 基于HashMap或三方缓存(Redis)实现, 不同SQL session可共享缓存

2、一级缓存失效场景:(一级默认开启,但可能失效)

  • 同一Sqlsession中执行更新操作(insert/update/delete),Mybatis会清空当前sqlsession的一级缓存(避免缓存与数据库数据不一致);
  • 手动调用SqlSession.clearCache()方法;主动清理缓存;
  • 同一sqlsession中查询不同的SQL或参数;缓存的可以组成:SQL语句+参数+环境+映射ID。不同key导致未命中。
  • SqlSession关闭或提交,SQLSession生命周期结束,缓存随即销毁、

3、开启二级缓存,以及二级缓存的注意事项

开启步骤:

  1. 全局配置开启二级缓存(默认已开启,可省略)

在mybatis.xml文件中配置

xml 复制代码
<settings>
  <setting name="cacheEnabled" value="true"/>
</settings>
  1. 在映射文件中声明二级缓存
    在需要开启二级缓存的Mapper.xml文件中添加<cache/ >标签;
xml 复制代码
<!-- UserMapper.xml -->
<cache 
  eviction="LRU"        <!-- 缓存淘汰策略:LRU(最近最少使用)、FIFO 等 -->
  flushInterval="60000" <!-- 缓存自动刷新时间(毫秒) -->
  size="1024"           <!-- 缓存最大条目数 -->
  readOnly="true"/>     <!-- 是否只读(true:返回缓存对象的引用,性能高;false:返回拷贝,安全) -->
  1. 实体类实现序列化接口
    二级缓存可能需要将对象写入磁盘(如使用Ehcache时),需保证实体类implement Serializable。

注意事项:

  • 二级缓存按Mapper划分:不同Mapper的缓存应相互独立,若需要跨Mapper共享缓存,需要配置cache-ref(引用其他Mapper的缓存);
  • 避免缓存频繁更新的数据
  • 复杂关联查询(如一对多),需要考虑是否缓存。优先使用一级缓存或使用三方缓存

性能优化

1、Mybatis常见的性能优化

  • SQL优化
    • 避免Select *,只查询需要的字段
    • 合理利用索引
    • 减少关联查询
  • 缓存优化
    • 开启二级缓存(针对高频,低频更新的数据)
    • 集成三方缓存,替代默认缓存。
  • 执行器优化:
    • 批量操作时,使用BatchExecutor,需要手动设置SqlSessiond 执行器类型
  • 分页优化:
    • 使用分页插件(如Page Helper),避免手动在这里插入代码片拼接limit语句。
    • 避免全表查询后分页。会导致内存溢出
  • 参数优化
    • 避免大字段查询
  • 延迟加载(懒加载)
    • 开启全局懒加载,避免关联查询一次性查出所有数据(按需加载)。
xml 复制代码
<settings>
  <setting name="lazyLoadingEnabled" value="true"/> <!-- 开启懒加载 -->
  <setting name="aggressiveLazyLoading" value="false"/> <!-- 关闭积极加载(按需加载单个属性) -->
</settings>

2、Mybatis的延时加载,
定义 :延迟加载(懒加载)是指 **关联对象在需要时才会加载,**而不是在主对象加载时,一次性加载。如:加载用户订单时,先加载用户信息,只有调用user.getOrder()时,才查询订单数据。
原理 :通过动态代理实现延迟加载.

  1. 开启懒加载,Mybatis会为实体类(如:User)生成动态代理对象;
  2. 初始查询只加载主对象User的基本属性,关联属性如order会被设置为【代理对象]
  3. 当调用关联查询属性的getter方法时,代理对象会触发Mybatis的懒加载机制逻辑。执行关联SQL并填充数据。
  4. 若未调用关联属性,则不会执行关联查询,减少数据库压力。

MyBatis的事务:Mybatis本身不提供事务管理,而是依赖底层数据源和Spring管理。

  • 原生Mybatis事务管理:通过SqlSession控制事务,默认关闭自动提交,需要手动commit()或rollback();
java 复制代码
SqlSession sqlSession = sqlSessionFactory.openSession(false); // false:关闭自动提交
try {
  UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  mapper.insertUser(user1);
  mapper.insertUser(user2);
  sqlSession.commit(); // 手动提交
} catch (Exception e) {
  sqlSession.rollback(); // 异常回滚
} finally {
  sqlSession.close();
}
  • Spring整合Mybatis事务管理
    通过Spring的@Transactional注解或者xml配置文件声明事务,无需手动控制SqlSession ;
java 复制代码
@Service
public class UserService {
  @Autowired
  private UserMapper userMapper;

  @Transactional(rollbackFor = Exception.class) // 声明事务,异常回滚
  public void batchInsert(List<User> userList) {
    for (User user : userList) {
      userMapper.insertUser(user);
    }
  }
}
相关推荐
渣哥1 分钟前
如果没有双亲委派,Java 会乱成什么样?
java
Java水解9 分钟前
深入剖析Spring IOC容器——原理、源码与实践全解析
后端·spring
jokr_9 分钟前
C++ STL 顶层设计与安全:迭代器、失效与线程安全
java·c++·安全
Code_Artist14 分钟前
[Java并发编程]6.并发集合类:ConcurrentHashMap、CopyOnWriteArrayList
java·后端·源码阅读
爬虫程序猿17 分钟前
利用 Java 爬虫按关键字搜索 1688 商品详情 API 返回值说明实战指南
java·开发语言·爬虫
前端赵哈哈1 小时前
初学者入门:Android 实现 Tab 点击切换(TabLayout + ViewPager2)
android·java·android studio
jokr_1 小时前
C++ 指针与引用面试深度解析
java·c++·面试
杨杨杨大侠1 小时前
第6篇:链路追踪系统 - 分布式环境下的请求跟踪
java·后端·apache log4j
乘风归趣1 小时前
spire.doc在word中生成公式
java·开发语言·word
kong@react1 小时前
docker部署spring boot,安装jdk17、maven3.8.8详细步骤
java·spring boot·docker