欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

Mybatis源码:Executor 模板模式

发布时间:2023/12/15 编程问答 48 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Mybatis源码:Executor 模板模式 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

by yan 20220301

模板模式

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。

创建Executor

在openSqlSession中创建Executor

Mybatis insert

时序图: insert 过程

Mybatis Executor 相关类图

BaseExecutor :实现了Executor的全部方法,包括对缓存,事务,连接提供了一系列的模板方法, 这些模板方法中留出来了四个抽象的方法等待子类去实现如下

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; //模板方法@Overridepublic int update(MappedStatement ms, Object parameter) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}clearLocalCache();return doUpdate(ms, parameter);}@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}@SuppressWarnings("unchecked")@Overridepublic <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 #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}@Overridepublic <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);return doQueryCursor(ms, parameter, rowBounds, boundSql);}

SimpleExecutor: 特点是每次执行完毕后都会将创建出来的statement关闭掉,他也是默认的执行器类型
ReuseExecutor: 在它在本地维护了一个容器,用来存放针对每条sql创建出来的statement,下次执行相同的sql时,会先检查容器中是否存在相同的sql,如果存在就使用现成的,不再重复获取
BatchExecutor: 特点是进行批量修改,她会将修改操作记录在本地,等待程序触发提交事务,或者是触发下一次查询时,批量执行修改

BaseExecutor与一级缓存

BaseExecutor维护的一级缓存。
一级缓存是sqlsession级的:
sqlsession->excutor->localcache

public abstract class BaseExecutor implements Executor {private static final Log log = LogFactory.getLog(BaseExecutor.class);protected Transaction transaction;protected Executor wrapper;protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;protected PerpetualCache localCache;protected PerpetualCache localOutputParameterCache;protected Configuration configuration;protected int queryStack;private boolean closed;protected BaseExecutor(Configuration configuration, Transaction transaction) {this.transaction = transaction;this.deferredLoads = new ConcurrentLinkedQueue<>();this.localCache = new PerpetualCache("LocalCache");this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");this.closed = false;this.configuration = configuration;this.wrapper = this;} ...private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;localCache.putObject(key, EXECUTION_PLACEHOLDER);try {list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}}

什么是这个一级缓存呢? **一级缓存就是上面代码中的localCache。
一级缓存叫localCache,它的封装类叫PerpetualCache。
perpetual意味永不间断,一级缓存默认存在,也关不了。
但是在与Spring整合时,Spring把这个缓存给关了,这并不奇怪,因为spring 直接干掉了这个sqlSession。

一级缓存什么时候被填充的值呢?填充值的操作在一个叫做queryFromDataBase()的方法里面。
清空一级缓存的方法就是BaseExecutor的update()模板方法里。

CachingExecutor与二级缓存

二级缓存是共享级,在configuration里的。在mapper builder 加载配置 build时创建的。

//Configuration.javaprotected final Map<String, Cache> caches = new StrictMap<>("Caches collection");

CachingExecutor通过TransactionalCacheManager可能获取二级缓存里的数据。
二级缓存中存放的不是对象,而是被序列化后存储的数据,需要反序列化出来。

//MapperBuilderAssistant.javapublic void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods = type.getMethods();for (Method method : methods) {try {// issue #237if (!method.isBridge()) {parseStatement(method);}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}private void parseCache() {CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);if (cacheDomain != null) {Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();Properties props = convertToProperties(cacheDomain.properties());assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);}} public Cache useNewCache(Class<? extends Cache> typeClass,Class<? extends Cache> evictionClass,Long flushInterval,Integer size,boolean readWrite,boolean blocking,Properties props) {Cache cache = new CacheBuilder(currentNamespace).implementation(valueOrDefault(typeClass, PerpetualCache.class)).addDecorator(valueOrDefault(evictionClass, LruCache.class)).clearInterval(flushInterval).size(size).readWrite(readWrite).blocking(blocking).properties(props).build();configuration.addCache(cache);currentCache = cache;return cache;}

参考

https://
www.runoob.com/design-pattern/template-pattern.html
https://www.cnblogs.com/ZhuChangwu/p/11745562.html

总结

以上是生活随笔为你收集整理的Mybatis源码:Executor 模板模式的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。