模板方法模式在MyBatis源码中的应用
在 MyBatis 源码中,有很多模板方法模式的经典应用场景。
本节来介绍模板方法模式在 BaseExecutor 类中的应用。BaseExecutor 是一个基础的 SQL 执行类,实现了大部分 SQL 执行逻辑,然后把几个方法交给子类定制化完成,主要提供了缓存管理和事务管理的基本功能。源码如下。
Executor 是 Mybatis 的核心接口之一,定义了数据库操作的基本方法。BaseExecutor 类中的 query() 方法会先创建 CacheKey 对象,并根据 CacheKey 对象查找一级缓存,如果缓存命中则返回缓存中记录的结果对象,如果未命中则查询数据库得到结果集,之后将结果集映射成结果对象并保存到一级缓存中,同时返回结果对象。
doUpdate、doFlushStatements、doQuery 和 doQueryCursor 这几个方法就是交由子类来实现的,也就是说继承 BaseExecutor 的子类只需要实现这 4 个基本方法来完成数据库的相关操作即可。
BaseExecutor 的子类有 ReuseExecutor、SimpleExecutor、BatchExecutor 和 ClosedExecutor,其类图如下。
这里对这 4 个子类的功能简单介绍一下:
下面是 SimpleExecutor 的 doUpdate() 方法实现。
再来对比一下 BatchExecutor 的 doUpdate() 方法实现。
细心的小伙伴一定看出了差异,BatchExecutor 的处理逻辑比 SimpleExecutor 更为复杂,调用的核心 API 也有区别,SimpleExecutor 调用的核心方法是 handler.update() 方法,BatchExecutor 调用的核心方法是 handler.batch() 方法。这里暂时不对 MyBatis 源码进行深入分析,感兴趣的小伙伴可以自行继续深入研究。
本节来介绍模板方法模式在 BaseExecutor 类中的应用。BaseExecutor 是一个基础的 SQL 执行类,实现了大部分 SQL 执行逻辑,然后把几个方法交给子类定制化完成,主要提供了缓存管理和事务管理的基本功能。源码如下。
- public abstract class BaseExecutor implements Executor {
- protected Transaction transaction;
- protected Executor wrapper;
- protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
- protected PerpetualCache localCache;
- protected PerpetualCache localOutputParameterCache;
- protected Configuration configuration;
- protected int queryStack = 0;
- private boolean closed;
- @Override
- public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
- ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
- if (closed) {
- throw new ExecutorException("Executor was closed.");
- }
- if (queryStack == 0 && ms.isFlushCacheRequired()) {
- clearLocalCache();
- }
- List<E> list;
- try {
- queryStack++;
- list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
- if (list != null) {
- handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
- } else {
- list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
- }
- } finally {
- queryStack--;
- }
- if (queryStack == 0) {
- for (DeferredLoad deferredLoad : deferredLoads) {
- deferredLoad.load();
- }
- // issue #601
- deferredLoads.clear();
- if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
- // issue #482
- clearLocalCache();
- }
- }
- return list;
- }
- protected abstract int doUpdate(MappedStatement ms, Object parameter)
- throws SQLException;
- protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
- throws SQLException;
- protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
- throws SQLException;
- protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
- throws SQLException;
- // 省略....
doUpdate、doFlushStatements、doQuery 和 doQueryCursor 这几个方法就是交由子类来实现的,也就是说继承 BaseExecutor 的子类只需要实现这 4 个基本方法来完成数据库的相关操作即可。
BaseExecutor 的子类有 ReuseExecutor、SimpleExecutor、BatchExecutor 和 ClosedExecutor,其类图如下。

这里对这 4 个子类的功能简单介绍一下:
- SimpleExecutor 是 Mybatis 执行 Mapper 语句时默认使用的 Executor,提供最基本的 Mapper 语句执行功能,没有过多的封装。
- ReuseExecutor 提供了 Statement 重用的功能,通过 statementMap 字段缓存使用过的 Statement 对象进行重用,可以减少 SQL 预编译以及创建和销毁 Statement 对象的开销,从而提高性能。
- BatchExecutor 实现了批处理多条 SQL 语句的功能,在客户端缓存多条 SQL 并在合适的时机将多条 SQL 打包发送给数据库执行,从而减少网络方面的开销,提升系统的性能。
- ClosedExecutor 只是某个类的一个内部类。
下面是 SimpleExecutor 的 doUpdate() 方法实现。
- public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
- Statement stmt = null;
- int var6;
- try {
- Configuration configuration = ms.getConfiguration();
- StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
- stmt = this.prepareStatement(handler, ms.getStatementLog());
- var6 = handler.update(stmt);
- } finally {
- this.closeStatement(stmt);
- }
- return var6;
- }
- public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
- Configuration configuration = ms.getConfiguration();
- StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
- BoundSql boundSql = handler.getBoundSql();
- String sql = boundSql.getSql();
- Statement stmt;
- if (sql.equals(this.currentSql) && ms.equals(this.currentStatement)) {
- int last = this.statementList.size() - 1;
- stmt = (Statement)this.statementList.get(last);
- handler.parameterize(stmt);
- BatchResult batchResult = (BatchResult)this.batchResultList.get(last);
- batchResult.addParameterObject(parameterObject);
- } else {
- Connection connection = this.getConnection(ms.getStatementLog());
- stmt = handler.prepare(connection);
- handler.parameterize(stmt);
- this.currentSql = sql;
- this.currentStatement = ms;
- this.statementList.add(stmt);
- this.batchResultList.add(new BatchResult(ms, sql, parameterObject));
- }
- handler.batch(stmt);
- return -2147482646;
- }
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算