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

MyBatis 源码解析:CachingExecutor 设计与实现

摘要

CachingExecutor 是 MyBatis 中用于管理二级缓存的核心组件,它通过包装其他 Executor 实现类,实现缓存机制的功能,从而提升查询性能并减少对数据库的直接访问。本文将通过自定义实现 CachingExecutor 来模拟 MyBatis 的缓存机制,并深入探讨其设计原理和实现细节。


前言

在大型系统中,频繁的数据库访问往往会导致性能瓶颈。MyBatis 通过提供二级缓存机制,减少了重复查询,提升了性能。而 CachingExecutor 是实现这一机制的关键。它通过包装 Executor 实现类,首先检查缓存,如果命中缓存则直接返回结果,如果未命中则继续通过底层 Executor 执行查询操作,并将结果存入缓存。本文将通过自定义实现一个简化版的 CachingExecutor,帮助你深入理解 MyBatis 的缓存机制。


自定义实现:简化版 CachingExecutor

目标与功能

我们将实现一个简化版的 CachingExecutor,该类通过包装 Executor,支持执行 SQL 语句、缓存查询结果以及缓存管理。通过该实现,我们可以更好地理解 MyBatis 的二级缓存工作机制。

核心流程

  1. 查询缓存:每次执行查询前,首先检查缓存,如果命中缓存则直接返回结果。
  2. 执行查询:如果缓存未命中,则通过底层 Executor 执行 SQL 语句,并将结果存入缓存。
  3. 缓存管理:支持缓存的清空、提交和回滚机制,确保数据一致性。

实现过程

1. 定义 Executor 接口

Executor 接口是 MyBatis 的核心接口,负责执行 SQL 操作和缓存管理。我们首先定义该接口,并为其添加核心方法。

/*** Executor 接口,定义了 MyBatis 中执行 SQL 和管理缓存的核心操作。*/
public interface Executor {<T> T query(String statement, Object parameter);int update(String statement, Object parameter);void commit();void rollback();void clearCache();void close();
}
  • query:执行查询操作。
  • update:执行更新、删除等操作。
  • commitrollback:处理事务管理。
  • clearCache:清除缓存,确保缓存数据一致性。
2. 实现 SimpleExecutor 类

SimpleExecutor 类负责执行 SQL 语句,不涉及缓存管理,是 CachingExecutor 的底层执行器。

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** SimpleExecutor 是 Executor 接口的一个简化实现。* 它负责直接执行 SQL 操作,不涉及缓存机制。*/
public class SimpleExecutor implements Executor {private final Connection connection;public SimpleExecutor(Connection connection) {this.connection = connection;}@Overridepublic <T> T query(String statement, Object parameter) {try (PreparedStatement pstmt = connection.prepareStatement(statement)) {pstmt.setObject(1, parameter);try (ResultSet rs = pstmt.executeQuery()) {if (rs.next()) {// 返回查询结果,简化处理,仅获取第一列数据return (T) rs.getObject(1);}}} catch (SQLException e) {throw new RuntimeException("Error executing query", e);}return null;}@Overridepublic int update(String statement, Object parameter) {try (PreparedStatement pstmt = connection.prepareStatement(statement)) {pstmt.setObject(1, parameter);return pstmt.executeUpdate();} catch (SQLException e) {throw new RuntimeException("Error executing update", e);}}@Overridepublic void commit() {try {connection.commit();} catch (SQLException e) {throw new RuntimeException("Failed to commit transaction", e);}}@Overridepublic void rollback() {try {connection.rollback();} catch (SQLException e) {throw new RuntimeException("Failed to rollback transaction", e);}}@Overridepublic void clearCache() {// SimpleExecutor 不涉及缓存管理,留空}@Overridepublic void close() {try {connection.close();} catch (SQLException e) {throw new RuntimeException("Failed to close connection", e);}}
}
  • query 方法:直接执行 SQL 查询操作,返回结果。
  • update 方法:执行更新操作。
  • commitrollback 方法:事务管理。
  • clearCache 方法:此类不涉及缓存,因此该方法为空实现。
3. 实现 CachingExecutor 类

CachingExecutor 是 MyBatis 的核心缓存执行器,负责管理查询结果的缓存,并包装底层 Executor

import java.util.HashMap;
import java.util.Map;/*** CachingExecutor 是 Executor 的一个实现类,它通过包装底层 Executor 实现缓存机制。* 查询操作会首先检查缓存,未命中则通过底层 Executor 执行查询。*/
public class CachingExecutor implements Executor {private final Executor delegate;  // 底层 Executorprivate final Map<String, Object> cache = new HashMap<>();  // 二级缓存public CachingExecutor(Executor delegate) {this.delegate = delegate;}@Overridepublic <T> T query(String statement, Object parameter) {String cacheKey = statement + ":" + parameter;if (cache.containsKey(cacheKey)) {// 缓存命中,直接返回结果System.out.println("Cache hit for key: " + cacheKey);return (T) cache.get(cacheKey);}// 缓存未命中,调用底层 Executor 执行查询T result = delegate.query(statement, parameter);if (result != null) {cache.put(cacheKey, result);  // 将结果存入缓存}return result;}@Overridepublic int update(String statement, Object parameter) {// 调用底层 Executor 执行更新int rows = delegate.update(statement, parameter);// 更新操作后清空缓存,避免缓存中的脏数据clearCache();return rows;}@Overridepublic void commit() {delegate.commit();}@Overridepublic void rollback() {delegate.rollback();}@Overridepublic void clearCache() {cache.clear();  // 清空缓存}@Overridepublic void close() {delegate.close();}
}
  • query 方法:先检查缓存,命中则返回缓存数据;未命中则通过底层 Executor 执行查询,并将结果缓存。
  • update 方法:执行更新操作,并在更新后清空缓存,以防缓存脏数据。
  • clearCache 方法:清空缓存,确保数据一致性。
  • delegate:包装底层 Executor(如 SimpleExecutor)。
4. 测试 CachingExecutor

我们编写一个简单的测试类,验证 CachingExecutor 的功能。

import java.sql.Connection;
import java.sql.DriverManager;public class CachingExecutorTest {public static void main(String[] args) {try {// 模拟数据库连接Connection connection = DriverManager.getConnection("jdbc:h2:mem:testdb", "sa", "");// 初始化底层 Executor 和 CachingExecutorSimpleExecutor simpleExecutor = new SimpleExecutor(connection);CachingExecutor cachingExecutor = new CachingExecutor(simpleExecutor);// 执行 SQL 操作cachingExecutor.update("INSERT INTO users (name, age) VALUES ('John', 30)", null);Object result = cachingExecutor.query("SELECT age FROM users WHERE name = 'John'", null);System.out.println("Query result: " + result);// 再次查询,验证缓存result = cachingExecutor.query("SELECT age FROM users WHERE name = 'John'", null);System.out.println("Query result from cache: " + result);// 提交事务cachingExecutor.commit();// 关闭资源cachingExecutor.close();} catch (Exception e) {e.printStackTrace();}}
}

自定义实现类图

«interface»
Executor
+query(String statement, Object parameter)
+int update(String statement, Object parameter)
+commit()
+rollback()
+clearCache()
+close()
CachingExecutor
- Executor delegate
- Map cache
+query(String statement, Object parameter)
+int update(String statement, Object parameter)
+commit()
+rollback()
+clearCache()
+close()
SimpleExecutor
Connection

代码解析流程图

开始
初始化 CachingExecutor
执行 SQL 查询操作
缓存命中?
返回缓存结果
调用底层 Executor 查询
将查询结果存入缓存
返回结果
执行 SQL 更新操作
清空缓存
提交事务或回滚
关闭 Executor 并释放资源
结束

源码解析:MyBatis 中的 CachingExecutor

1. CachingExecutor 的设计与实现

在 MyBatis 中,CachingExecutor 通过包装其他 Executor 实现类,为查询操作提供了缓存机制。每次查询时,CachingExecutor 首先会检查缓存,命中则返回缓存结果;如果未命中,则调用底层 Executor 执行查询,并将结果缓存起来。CachingExecutor 还提供了缓存的清空功能,以保证在执行更新操作或事务提交时,缓存中的数据不会过期或不一致。

public class CachingExecutor implements Executor {private final Executor delegate;  // 底层 Executorprivate final TransactionalCacheManager tcm = new TransactionalCacheManager();  // 缓存管理器@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {CacheKey cacheKey = createCacheKey(ms, parameterObject, rowBounds);// 检查缓存List<E> list = tcm.getObject(cacheKey);if (list == null) {// 缓存未命中,执行查询list = delegate.query(ms, parameterObject, rowBounds, resultHandler);tcm.putObject(cacheKey, list);  // 将结果存入缓存}return list;}@Overridepublic int update(MappedStatement ms, Object parameter) throws SQLException {tcm.clear(ms.getCache());  // 清空缓存return delegate.update(ms, parameter);}// 其他方法...
}
  • query 方法:检查缓存,命中则返回缓存数据;未命中则执行查询并将结果存入缓存。
  • update 方法:在执行更新操作时,首先清空缓存,避免缓存中存有脏数据。

2. 二级缓存的管理

MyBatis 的二级缓存是 CachingExecutor 中的核心功能之一。它允许跨多个 SqlSession 共享缓存数据,从而减少数据库的访问次数。MyBatis 的二级缓存可以通过配置开启,并允许开发者自定义缓存实现。

CachingExecutor 通过 TransactionalCacheManager 管理缓存操作,并确保在事务提交或回滚时处理缓存的一致性。

public class TransactionalCacheManager {private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();public void clear(Cache cache) {getTransactionalCache(cache).clear();}public Object getObject(CacheKey cacheKey) {TransactionalCache txCache = transactionalCaches.get(cacheKey);return (txCache != null) ? txCache.getObject(cacheKey) : null;}public void putObject(Cache cache, Object key, Object value) {getTransactionalCache(cache).putObject(key, value);}private TransactionalCache getTransactionalCache(Cache cache) {return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);}// 其他方法...
}

3. 提交与回滚时的缓存处理

在 MyBatis 中,事务提交或回滚时,CachingExecutor 会调用 TransactionalCacheManager 清空或保存缓存,以确保缓存与数据库的一致性。


总结与互动

通过本文,我们详细探讨了 MyBatis 中 CachingExecutor 的设计与实现,并通过自定义实现加深了对二级缓存机制的理解。掌握这些知识有助于在实际开发中更好地利用缓存机制,提升系统性能并确保数据的一致性。

如果您觉得这篇文章对您有帮助,请点赞、收藏并关注!此外,欢迎在评论区留言,与我们分享您的见解或提出疑问!


相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 虚拟机【linux】配置无线网络
  • nefu暑假集训5 KMP 个人模板+例题汇总
  • PCM转PCMA(pcm_alaw,G711.A率)转换表 PCM转PCMU(pcm_ulaw,G711.U率)转换表
  • day-49 让所有学生保持开心的分组方法数
  • gitee 简单使用
  • 【护网相关知识】
  • org.apache.commons.lang.math.NumberUtils#isNumber 解释
  • Python实践:多种方式实现数字前补零
  • uniapp壁纸项目笔记
  • 前端原生Js批量修改页面元素属性的2个方法
  • SprinBoot+Vue在线商城微信小程序的设计与实现
  • 数据库系统 第36节 数据库镜像
  • 【网络安全】XSS(新)+Amazon账户劫持复现
  • 【软件设计】常用设计模式--概述
  • 无人机+应用综合实训室解决方案
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • java第三方包学习之lombok
  • js中的正则表达式入门
  • Material Design
  • Redis学习笔记 - pipline(流水线、管道)
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • 番外篇1:在Windows环境下安装JDK
  • 分享一份非常强势的Android面试题
  • 关于 Cirru Editor 存储格式
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 小程序 setData 学问多
  •  一套莫尔斯电报听写、翻译系统
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • $.proxy和$.extend
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (rabbitmq的高级特性)消息可靠性
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (分享)自己整理的一些简单awk实用语句
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (十六)一篇文章学会Java的常用API
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • (杂交版)植物大战僵尸
  • (转)setTimeout 和 setInterval 的区别
  • ****三次握手和四次挥手
  • .gitattributes 文件
  • .NET COER+CONSUL微服务项目在CENTOS环境下的部署实践
  • .NET CORE Aws S3 使用
  • .NET Core中的去虚
  • .net framework 4.8 开发windows系统服务
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .NET3.5下用Lambda简化跨线程访问窗体控件,避免繁复的delegate,Invoke(转)
  • /usr/bin/env: node: No such file or directory