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

【MyBatis】——执行过程

MyBatis核心机制详解:执行流程、延迟加载与缓存

引言

MyBatis 作为一款优秀的持久层框架,以其灵活性、高性能和易于上手的特点,在Java开发中得到了广泛应用。要真正精通MyBatis,仅仅会编写SQL是远远不够的,深入理解其内部执行流程和核心机制至关重要。本文将详细解析MyBatis的执行流程延迟加载以及一级与二级缓存的原理和使用。

一、MyBatis 执行流程

配置文件:

MyBatis的执行流程可以看作是“如何通过接口方法最终执行SQL并返回结果”的故事。其核心过程如下图所示,我们将逐步分解:


详细步骤分解:

二、MyBatis 延迟加载(Lazy Loading)

1. 什么是延迟加载?

延迟加载,也称为懒加载,是一种对象关系映射(ORM)的优化策略。它的核心思想是:只有在真正使用到对象的关联数据时,才执行SQL语句去数据库查询加载,否则不加载。这可以有效减少不必要的数据库查询,提升性能。

例如:查询 Order(订单)对象时,其关联的 User(用户)信息可能并不需要立即显示。使用延迟加载,只有在调用 order.getUser() 方法时,MyBatis才会发起查询获取User数据。

2. 如何配置使用?
  1. 全局开启:在 mybatis-config.xml 核心配置文件中设置。

    <settings><!-- 开启延迟加载 --><setting name="lazyLoadingEnabled" value="true"/><!-- 禁止 aggressive lazy loading(按需加载) --><setting name="aggressiveLazyLoading" value="false"/></settings>

* `lazyLoadingEnabled`:延迟加载的全局开关。 * `aggressiveLazyLoading`:如果设置为 `true`,任何方法的调用都会加载所有延迟加载属性。设置为 `false` 则表示每个属性按需加载。

2. 局部配置:在具体的 associationcollection 标签上使用 fetchType 属性覆盖全局配置。


3. 实现原理

MyBatis的延迟加载是通过 动态代理 实现的。

开启了延迟加载后,在判断orderlist是否为空是,就会判断为是

  1. 创建代理对象:当主对象(如 Order)被查询出来后,MyBatis并不会直接填充其关联属性(如 user),而是为该关联属性创建一个代理对象(比如 User 的代理)。
  2. 拦截方法调用:这个代理对象实现了所有的目标对象方法,并持有一个用于执行延迟加载SQL的 Method 句柄。
  3. 触发加载:当应用程序第一次调用代理对象的任何方法时(如 order.getUser().getName()),拦截器(LazyLoader)会被触发。
  4. 执行查询:拦截器会通过之前保存的SQL信息和参数,执行一次真实的数据库查询,获取完整的关联对象数据。
  5. 替换代理:查询完成后,MyBatis会用真实的关联对象数据替换掉原来的代理对象。此后,对该属性的所有调用都将直接作用于这个真实对象,延迟加载过程结束。

注意事项:延迟加载在提高性能的同时,也带来了“N+1查询问题”的风险。如果遍历一个订单列表,并对每个订单都访问其延迟加载的用户信息,就会产生1次查询订单 + N次查询用户(N是订单数量)的性能灾难。需根据业务场景谨慎使用。

三、MyBatis 一级缓存与二级缓存

缓存是MyBatis提升性能的重要手段,它提供了两级缓存结构。

1. 一级缓存(Local Cache)

  • 作用范围SqlSession级别。默认开启,无法关闭。
  • 生命周期:与 SqlSession 相同。当 SqlSession 被 close()commit()rollback() 或执行了 update(增删改)操作时,该 SqlSession 下的一级缓存会被清空。
  • 工作原理
    • 在同一个 SqlSession 中执行两次相同的SQL查询(相同的SQL和参数),第一次会查询数据库并将结果放入缓存,第二次则直接从缓存中获取,不再访问数据库。
    • 它内部是一个简单的 HashMap,Key由 StatementId + SQL + 参数值 等构成。
  • 注意事项:在分布式或Web应用中,由于 SqlSession 的生命周期通常很短(例如在请求开始时创建,请求结束时关闭),一级缓存的作用非常有限,主要用于减少在一个业务事务内的重复查询。
2. 二级缓存(Second Level Cache)

  • 作用范围Mapper级别(Namespace级别)。多个 SqlSession 可以共享同一个二级缓存。
  • 生命周期:与应用生命周期基本相同。只有当数据被执行了 update 操作(增删改)时,MyBatis才会清空该 Mapper 对应的二级缓存,以确保数据一致性。
  • 如何开启
    1. 全局开关:在 mybatis-config.xml 中设置。
        <settings><setting name="cacheEnabled" value="true"/> <!-- 默认为true,通常不用改 --></settings>

2. **在Mapper.xml中开启**:在需要开启二级缓存的 `Mapper.xml` 文件中添加 `<cache/>` 标签。

        <?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.mapper.UserMapper"><cache/> <!-- 启用二级缓存 -->...</mapper>

3. **POJO序列化**:二级缓存中的数据可能被写入磁盘或通过网络传输,因此对应的实体类必须实现 `java.io.Serializable` 接口。

  • 工作流程

    1. 查询顺序:二级缓存 -> 一级缓存 -> 数据库
    2. 当 SqlSession 被关闭或提交时,它的一级缓存中的数据才会被转存到其对应的二级缓存中。这意味着,未提交的事务数据不会被其他 SqlSession 看到,避免了脏读。
  • 缓存策略与自定义

    • <cache> 标签可以配置多种属性,如 eviction(回收策略:LRU、FIFO等)、flushInterval(刷新间隔)、size(引用数目)等。
    • MyBatis还支持集成第三方缓存库,如 EhcacheRedis 等,只需实现 org.apache.ibatis.cache.Cache 接口,并在 <cache type="..."> 中指定类型即可。这使得二级缓存可以部署在分布式环境中,解决单机缓存的数据一致性问题。

注意事项:

拓展:serializable接口有哪些作用

java.io.Serializable 是 Java 提供的一个标记接口(Marker Interface)。它内部没有任何需要实现的方法(public abstract 方法)。它的作用仅仅是为一个类“打上标记”,向 Java 虚拟机(JVM)声明:“这个类的对象可以被序列化。”

核心作用:对象序列化与反序列化

Serializable 接口的核心作用围绕着两个过程:

  1. 序列化(Serialization)

    • 将内存中的 Java 对象(Object) 的状态(即其成员变量的值)转换成一个字节序列(byte sequence) 的过程。
    • 这个字节序列可以被轻松地写入到文件(.ser 等)、存入数据库的 BLOB 字段、或者通过网络发送到另一台主机。
  2. 反序列化(Deserialization)

    • 与序列化相反。将之前序列化得到的字节序列,重新构造成一个与原始对象状态完全相同的 Java 对象 的过程。

简单比喻:

  • 序列化就像把一辆汽车拆解成零件清单(字节序列),以便装箱运输或存入仓库。
  • 反序列化就像在目的地根据零件清单重新组装出一辆一模一样的汽车(对象)。

为什么需要序列化?

序列化机制解决了几个关键问题:

  1. 持久化(Persistence)

    • 将对象的状态永久保存到存储设备(如硬盘)上,即使程序关闭后,下次启动仍然可以通过反序列化恢复这个对象。例如:保存游戏进度、应用程序的配置信息等。
  2. 网络通信(Network Communication)

    • 在分布式系统或远程方法调用(如 RMI、Web Services)中,需要将对象从一个JVM(一台机器)传输到另一个JVM(另一台机器)。序列化将对象转换为字节流,可以通过网络Socket进行传输,接收方再反序列化获得对象。
  3. 缓存(Caching)

    • 这正是MyBatis二级缓存要求 Serializable 的原因!
    • 缓存系统(如Redis、Memcached,甚至MyBatis自己的二级缓存)通常是独立于应用程序进程的。它们存储的是数据,而不是活的Java对象。
    • 为了将一个Java对象放入缓存,必须先将它序列化成字节数组形式进行存储。
    • 当从缓存中读取数据时,再将字节数组反序列化回Java对象供程序使用。
    • 如果对象不可序列化,缓存系统就无法正确地存储和重建它。
3. 一级缓存与二级缓存对比总结
特性一级缓存二级缓存
作用范围SqlSession 内部Mapper(Namespace) 级别,跨 SqlSession
默认状态开启,且不可关闭需在Mapper.xml中手动配置 <cache/> 开启
生命周期随 SqlSession 的销毁而销毁随应用程序的生命周期,除非被更新操作清空
缓存数据存储在内存中可配置存储介质(内存、磁盘、第三方缓存服务器)
共享性不能共享可以共享
应用场景避免在一个会话内重复查询避免在不同会话间对读多写少的相同数据进行重复查询

总结

深入理解MyBatis的执行流程是理解其所有特性的基础,它揭示了SQL从方法调用到结果返回的完整生命周期。延迟加载是一种重要的优化手段,通过动态代理实现按需加载,但需警惕N+1问题。一级缓存二级缓存构成了MyBatis的性能加速器,一级缓存作用于会话内部,二级缓存则服务于全局应用,正确配置和使用缓存能极大提升系统性能,尤其是在读多写少的场景下。

掌握这三部分内容,意味着你对MyBatis的核心运行机制有了深刻的认识,能够更好地在实际开发中进行性能调优和问题排查。

http://www.dtcms.com/a/453707.html

相关文章:

  • 修改配置文件之后,重启edge浏览器收藏夹消失怎么办?
  • 网站推广优化外包公司个人网站设计开题报告
  • 组播实验-IGMP、IGMP Snooping及PIM-DM协议
  • 企业seo网站推广公司wordpress 下载受限
  • 第十二篇:std::shared_ptr和std::weak_ptr:共享所有权与解决循环引用
  • 第十九章:千变万化,随心而动——State的状态艺术
  • Harmony中EventHub实现发布订阅
  • 高效学习方法——知识关联性
  • 教育类网站素材总部在上海的世界500强企业
  • CCF编程能力等级认证GESP—C++6级—20250927
  • libopenssl-1_0_0-devel-1.0.2p RPM 包安装教程(openSUSE/SLES x86_64)​
  • 网站开发栏目需求1仪征网站建设公司哪家好
  • FK 外键上需要创建index 避免 主表update时 的lock
  • 三剑合璧:C++11 lambda、variadic template 与 wrapper 的协奏
  • 空间智能找文献方向
  • 儒枫网网站建设惠州 企业网站建设
  • 基于 GEE 平台用 Sentinel-1 SAR 数据实现山区潜在滑坡检测
  • CSS是什么?—— 网页的“化妆师”
  • cygwin + redis
  • 我也来做外国网站购物苏州新区建网站
  • #智能电饭煲技术开发原理与源代码实现
  • 无人机系统耗电,低功耗管理问题解决方法(chatgpt)
  • 设计网站国外公共建设工程中心网站
  • 第二十章:遍历万象,操作随心——Visitor的访问艺术
  • 找不到或无法加载主类
  • XMedia Recode:音频格式转换
  • Linux 内核态和用户态
  • 145、【OS】【Nuttx】【周边】效果呈现方案解析:VSCode Remote Server
  • 【C++实战(78)】解锁C++ 大数据处理:从并行到分布式实战
  • 农安县住房和城乡建设厅网站青岛网站建设开发