首页 > 编程笔记 > Java笔记

模板方法模式在MyBatis源码中的应用

在 MyBatis 源码中,有很多模板方法模式的经典应用场景。

本节来介绍模板方法模式在 BaseExecutor 类中的应用。BaseExecutor 是一个基础的 SQL 执行类,实现了大部分 SQL 执行逻辑,然后把几个方法交给子类定制化完成,主要提供了缓存管理和事务管理的基本功能。源码如下。
  1. public abstract class BaseExecutor implements Executor {
  2. protected Transaction transaction;
  3. protected Executor wrapper;
  4.  
  5. protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  6. protected PerpetualCache localCache;
  7. protected PerpetualCache localOutputParameterCache;
  8. protected Configuration configuration;
  9.  
  10. protected int queryStack = 0;
  11. private boolean closed;
  12.  
  13. @Override
  14. public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  15. ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  16. if (closed) {
  17. throw new ExecutorException("Executor was closed.");
  18. }
  19. if (queryStack == 0 && ms.isFlushCacheRequired()) {
  20. clearLocalCache();
  21. }
  22. List<E> list;
  23. try {
  24. queryStack++;
  25. list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
  26. if (list != null) {
  27. handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
  28. } else {
  29. list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
  30. }
  31. } finally {
  32. queryStack--;
  33. }
  34. if (queryStack == 0) {
  35. for (DeferredLoad deferredLoad : deferredLoads) {
  36. deferredLoad.load();
  37. }
  38. // issue #601
  39. deferredLoads.clear();
  40. if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
  41. // issue #482
  42. clearLocalCache();
  43. }
  44. }
  45. return list;
  46. }
  47.  
  48. protected abstract int doUpdate(MappedStatement ms, Object parameter)
  49. throws SQLException;
  50.  
  51. protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
  52. throws SQLException;
  53.  
  54. protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
  55. throws SQLException;
  56.  
  57. protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
  58. throws SQLException;
  59.  
  60. // 省略....
Executor 是 Mybatis 的核心接口之一,定义了数据库操作的基本方法。BaseExecutor 类中的 query() 方法会先创建 CacheKey 对象,并根据 CacheKey 对象查找一级缓存,如果缓存命中则返回缓存中记录的结果对象,如果未命中则查询数据库得到结果集,之后将结果集映射成结果对象并保存到一级缓存中,同时返回结果对象。

doUpdate、doFlushStatements、doQuery 和 doQueryCursor 这几个方法就是交由子类来实现的,也就是说继承 BaseExecutor 的子类只需要实现这 4 个基本方法来完成数据库的相关操作即可。

BaseExecutor 的子类有 ReuseExecutor、SimpleExecutor、BatchExecutor 和 ClosedExecutor,其类图如下。


这里对这 4 个子类的功能简单介绍一下:
下面是 SimpleExecutor 的 doUpdate() 方法实现。
  1. public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  2. Statement stmt = null;
  3.  
  4. int var6;
  5. try {
  6. Configuration configuration = ms.getConfiguration();
  7. StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
  8. stmt = this.prepareStatement(handler, ms.getStatementLog());
  9. var6 = handler.update(stmt);
  10. } finally {
  11. this.closeStatement(stmt);
  12. }
  13.  
  14. return var6;
  15. }
再来对比一下 BatchExecutor 的 doUpdate() 方法实现。
  1. public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
  2. Configuration configuration = ms.getConfiguration();
  3. StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
  4. BoundSql boundSql = handler.getBoundSql();
  5. String sql = boundSql.getSql();
  6. Statement stmt;
  7. if (sql.equals(this.currentSql) && ms.equals(this.currentStatement)) {
  8. int last = this.statementList.size() - 1;
  9. stmt = (Statement)this.statementList.get(last);
  10. handler.parameterize(stmt);
  11. BatchResult batchResult = (BatchResult)this.batchResultList.get(last);
  12. batchResult.addParameterObject(parameterObject);
  13. } else {
  14. Connection connection = this.getConnection(ms.getStatementLog());
  15. stmt = handler.prepare(connection);
  16. handler.parameterize(stmt);
  17. this.currentSql = sql;
  18. this.currentStatement = ms;
  19. this.statementList.add(stmt);
  20. this.batchResultList.add(new BatchResult(ms, sql, parameterObject));
  21. }
  22.  
  23. handler.batch(stmt);
  24. return -2147482646;
  25. }
细心的小伙伴一定看出了差异,BatchExecutor 的处理逻辑比 SimpleExecutor 更为复杂,调用的核心 API 也有区别,SimpleExecutor 调用的核心方法是 handler.update() 方法,BatchExecutor 调用的核心方法是 handler.batch() 方法。这里暂时不对 MyBatis 源码进行深入分析,感兴趣的小伙伴可以自行继续深入研究。

所有教程

优秀文章