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

官方网站开发需要几个技术人员会议网站

官方网站开发需要几个技术人员,会议网站,网站做编辑,网络布线文章目录 前言第六章 MyBatis缓存6.1 MyBatis缓存实现类6.2 MyBatis一级缓存实现原理6.2.1 一级缓存在查询时的使用6.2.2 一级缓存在更新时的清空 6.3 MyBatis二级缓存的实现原理6.3.1 实现的二级缓存的Executor类型6.3.2 二级缓存在查询时使用6.3.3 二级缓存在更新时清空 前言… 文章目录 前言第六章 MyBatis缓存6.1 MyBatis缓存实现类6.2 MyBatis一级缓存实现原理6.2.1 一级缓存在查询时的使用6.2.2 一级缓存在更新时的清空 6.3 MyBatis二级缓存的实现原理6.3.1 实现的二级缓存的Executor类型6.3.2 二级缓存在查询时使用6.3.3 二级缓存在更新时清空 前言 缓存是MyBatis中非常重要的特性。合理使用缓存可以减少数据库IO显著提升系统性能但在分布式环境下如果使用不当则会带来数据一致性问题。 在上一节【MyBatis3源码深度解析(十六)SqlSession的创建与执行(三)Mapper方法的调用过程】中提到MyBatis提供了一级缓存和二级缓存来提升查询效率一级缓存在BaseExecutor类中完成二级缓存在CachingExecutor类中完成。 第六章 MyBatis缓存 6.1 MyBatis缓存实现类 MyBatis缓存基于JVM堆内存实现即所有的缓存数据都存放在Java对象中。 MyBatis通过Cache接口定义缓存对象的行为其定义如下 源码1org.apache.ibatis.cache.Cachepublic interface Cache {// 获取缓存IDString getId();// 将一个Java对象添加到缓存中void putObject(Object key, Object value);// 根据key获取一个缓存对象Object getObject(Object key);// 根据key移除一个缓存对象Object removeObject(Object key);// 清空缓存void clear();// 获取缓存中存放的数据数量int getSize();// 3.2.6版本后不再使用default ReadWriteLock getReadWriteLock() {return null;} }Cache接口采用装饰器模式设计。它有一个基本的实现类PerpetualCache。 源码2org.apache.ibatis.cache.impl.PerpetualCachepublic class PerpetualCache implements Cache {private final String id;// 内部维护了一个HashMap容器以保存缓存数据private final MapObject, Object cache new HashMap();public PerpetualCache(String id) {this.id id;}Overridepublic String getId() {return id;}Overridepublic int getSize() {return cache.size();}Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}Overridepublic Object getObject(Object key) {return cache.get(key);}Overridepublic Object removeObject(Object key) {return cache.remove(key);}Overridepublic void clear() {cache.clear();}Overridepublic boolean equals(Object o) {if (getId() null) {throw new CacheException(Cache instances require an ID.);}if (this o) {return true;}if (!(o instanceof Cache)) {return false;}Cache otherCache (Cache) o;// 当两个缓存对象的ID相同时即认为缓存对象相同return getId().equals(otherCache.getId());}Overridepublic int hashCode() {if (getId() null) {throw new CacheException(Cache instances require an ID.);}// 仅以缓存对象的ID作为因子生成hashCodereturn getId().hashCode();} }由 源码2 可知PerpetualCache的实现非常简单内部仅仅维护了一个HashMap实例存放缓存对象。 需要注意的是PerpetualCache类重写了Object类的equals()方法和hashCode()方法。由equals()方法可知当两个缓存对象的ID相同时即认为缓存对象相同由hashCode()方法可知仅以缓存对象的ID作为因子生成hashCode。 除了基础的PerpetualCache实现类MyBatis还提供了许多其他的实现对PerpetualCache类的功能进行增强。借助IDE可以列出Cache的全部实现类 MyBatis的一级缓存使用的是PerpetualCache二级缓存使用的是TransactionalCache。 6.2 MyBatis一级缓存实现原理 MyBatis一级缓存默认是开启的而且不能关闭。 至于一级缓存不能关闭的原因MyBatis核心开发人员做出了解释MyBatis的一些关键特性例如通过association和collextion建立级联映射、避免循环引用circular references、加速重复嵌套查询等都是基于MyBatis一级缓存实现的而且MyBatis结果集映射相关代码重度依赖CacheKey所以MyBatis一级缓存不支持关闭。 在MyBatis主配置文件中有这样一个配置属性setting namelocalCacheScope valueSESSION/用于控制一级缓存的级别。该属性的取值为SESSION、STATEMENT。 当指定localCacheScope参数值为SESSION时缓存对整个SqlSession有效只有执行DML语句更新语句时缓存才会被清除当指定localCacheScope参数值为STATEMENT时缓存仅对当前执行的SQL语句有效当语句执行完毕后缓存就会被清除。 前面提到一级缓存在BaseExecutor类中完成 源码3org.apache.ibatis.executor.BaseExecutorpublic abstract class BaseExecutor implements Executor {// ...// 一级缓存对象protected PerpetualCache localCache;// 存储过程输出参数缓存protected PerpetualCache localOutputParameterCache;// ...protected BaseExecutor(Configuration configuration, Transaction transaction) {// ...this.localCache new PerpetualCache(LocalCache);this.localOutputParameterCache new PerpetualCache(LocalOutputParameterCache);// ...} }由 源码3 可知一级缓存使用PerpetualCache来实现BaseExecutor中维护了两个PerpetualCache属性localCache用于缓存MyBatis查询结果localOutputParameterCache用于缓存存储过程输出参数。 这两个属性均在BaseExecutor的构造方法中初始化并指定其ID。 MyBatis通过CacheKey对象来描述缓存的Key值。如果两次查询操作的CacheKey对象相同就认为这两次查询执行的是相同的SQL语句。 CacheKey对象通过BaseExecutor的createCacheKey()方法来创建。 源码4org.apache.ibatis.executor.BaseExecutorOverride public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {if (closed) {throw new ExecutorException(Executor was closed.);}CacheKey cacheKey new CacheKey();// Mapper的IDcacheKey.update(ms.getId());// 偏移量cacheKey.update(rowBounds.getOffset());// 查询条数cacheKey.update(rowBounds.getLimit());// SQL语句cacheKey.update(boundSql.getSql());// ......// SQL语句中的参数cacheKey.update(value);// ......// 配置文件中的environment标签的ID属性值if (configuration.getEnvironment() ! null) {cacheKey.update(configuration.getEnvironment().getId());}return cacheKey; }由 源码4 可知与CacheKey相关的因素包括Mapper的ID、偏移量、查询条数、SQL语句、SQL语句中的参数、配置文件中的environment标签的ID属性值。 执行两次查询时只有以上因素完全相同才会认为这两次查询执行的是相同的SQL语句才会直接从缓存中获取查询结果。 6.2.1 一级缓存在查询时的使用 解析来研究一下BaseExecutor的query()方法中是如何使用一级缓存的。 源码5org.apache.ibatis.executor.BaseExecutorOverride public E ListE 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()) {// 如果select标签的flushCache属性为true则直接清除缓存// 默认为falseclearLocalCache();}ListE list;try {queryStack;// 从一级缓存中根据CacheKey获取缓存结果list resultHandler null ? (ListE) 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) {// 如果localCacheScope参数值为STATEMENT缓存仅对当前执行的SQL语句有效当语句执行完毕后缓存就会被清除clearLocalCache();}}return list; }private E ListE queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ListE 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; }由 源码5 可知在BaseExecutor的query()方法中首先会判断select标签的flushCache属性值如果该属性为true说明任何SQL语句被调用都需要先清除缓存因此直接调用clearLocalCache()方法清除缓存。该属性在select标签中的默认值为false。 接着根据CacheKey从一级缓存中查找是否有缓存对象。如果查找不到则调用queryFromDatabase()方法从数据库查询数据并将查询结果保存到一级缓存中如果查找到了则直接返回。 最后该方法还会判断主配置文件中的localCacheScope参数的值是否为STATEMENT如果是STATEMENT缓存仅对当前执行的SQL语句有效因此当语句执行完毕后直接调用clearLocalCache()方法清除缓存。 6.2.2 一级缓存在更新时的清空 除了flushCache属性和localCacheScope属性可以控制一级缓存的清空MyBatis会在执行任意更新语句时清空缓存即BaseExecutor的update()方法 源码6org.apache.ibatis.executor.BaseExecutorOverride public 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); }由 源码6 可知MyBatis在调用doUpdate()方法执行更新语句之前会调用clearLocalCache()方法清除缓存。 下面做个简单的测试。有以下单元测试代码两次调用相同的selectAll()方法 Test public void testCache() throws IOException, NoSuchMethodException {Reader reader Resources.getResourceAsReader(mybatis-config.xml);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(reader);SqlSession sqlSession sqlSessionFactory.openSession();UserMapper userMapper sqlSession.getMapper(UserMapper.class);// 第一次调用selectAlluserMapper.selectAll();// 第二次调用selectAlluserMapper.selectAll(); }借助Debug工具可以发现第一次调用selectAll()方法是一级缓存中没有数据程序会调用queryFromDatabase()方法从数据库查询数据并存放到一级缓存中 第二次调用selectAll()方法时一级缓存中已经保存了第一次查询的数据这次直接从缓存中就可以取到数据而不需要再去数据库查询 6.3 MyBatis二级缓存的实现原理 6.3.1 实现的二级缓存的Executor类型 在 MyBatis的官方文档 中说默认情况下只启用了本地的会话缓存它仅仅对一个会话中的数据进行缓存。要启用全局的二级缓存只需要在你的SQL映射文件中添加一行cache/。 官方文档意思是默认情况下一级缓存是打开的二级缓存是关闭的。要开启二级缓存需要在SQL映射文件中添加一行cache/。 源码7org.apache.ibatis.builder.xml.XMLConfigBuilderprivate void settingsElement(Properties props) {// ......configuration.setCacheEnabled(booleanValueOf(props.getProperty(cacheEnabled), true));// ......configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty(defaultExecutorType, SIMPLE)));// ...... }由 源码7 可知在解析MyBatis主配置文件时cacheEnabled属性的默认值是true即默认使用二级缓存允许使用不代表开启defaultExecutorType属性的默认值是SIMPLE即默认创建的Executor类型是SimpleExecutor。 前面提到二级缓存是在CachingExecutor类中完成的。因此MyBatis在创建Executor时会根据主配置文件中的cacheEnabled属性和defaultExecutorType属性来判断创建哪种Executor。 该创建工作在Configuration对象的工厂方法newExecutor()中完成 源码8org.apache.ibatis.session.Configuration// 默认创建SimpleExecutor protected ExecutorType defaultExecutorType ExecutorType.SIMPLE; // 默认开启二级缓存 protected boolean cacheEnabled true;public Executor newExecutor(Transaction transaction) {return newExecutor(transaction, defaultExecutorType); }// 指定Executor类型 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType executorType null ? defaultExecutorType : executorType;Executor executor;if (ExecutorType.BATCH executorType) {executor new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE executorType) {executor new ReuseExecutor(this, transaction);} else {executor new SimpleExecutor(this, transaction);}// 如果cacheEnabled属性为true则创建CachingExecutorif (cacheEnabled) {executor new CachingExecutor(executor);}return (Executor) interceptorChain.pluginAll(executor); }由 源码8 可知MyBatis默认允许使用二级缓存其实现在CachingExecutor类中。 源码9org.apache.ibatis.executor.CachingExecutorpublic class CachingExecutor implements Executor {private final Executor delegate;private final TransactionalCacheManager tcm new TransactionalCacheManager();// ...... }由 源码9 可知CachingExecutor类中维护了一个TransactionalCacheManager实例用于管理所有的二级缓存对象。 源码10org.apache.ibatis.cache.TransactionalCacheManagerpublic class TransactionalCacheManager {// 通过HashMap对象维护二级缓存对应的TransactionalCache实例private final MapCache, TransactionalCache transactionalCaches new HashMap();// 清空缓存public void clear(Cache cache) {getTransactionalCache(cache).clear();}// 获取二级缓存对应的TransactionalCache对象// 根据根据缓存Key获取缓存对象public Object getObject(Cache cache, CacheKey key) {return getTransactionalCache(cache).getObject(key);}// 添加缓存public void putObject(Cache cache, CacheKey key, Object value) {getTransactionalCache(cache).putObject(key, value);}// 只有调用commit()方法后缓存对象才会真正添加到TransactionalCache中public void commit() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.commit();}}// 当调用rollback()方法时写入操作将被回滚public void rollback() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.rollback();}}// 如果二级缓存对应的TransactionalCache获取不到则创建一个新的private TransactionalCache getTransactionalCache(Cache cache) {return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);}}由 源码10 可知TransactionalCacheManager类中组合了一个HashMap对象用于维护二级缓存实例对应的TransactionalCache对象。getObject()和putObject()方法均要先调用getTransactionalCache()方法获取到TransactionalCache对象再对TransactionalCache对象进行操作。 源码11org.apache.ibatis.cache.decorators.TransactionalCachepublic class TransactionalCache implements Cache {// 缓存对象内部组合了一个HashMap实例private final Cache delegate;// 一个标志为true时表示提交数据时要清空缓存对象默认为falseprivate boolean clearOnCommit;// 保存即将存入二级缓存的数据private final MapObject, Object entriesToAddOnCommit;// 保存从二级缓存中没有取出的数据时的缓存Keyprivate final SetObject entriesMissedInCache;public TransactionalCache(Cache delegate) {this.delegate delegate;this.clearOnCommit false;this.entriesToAddOnCommit new HashMap();this.entriesMissedInCache new HashSet();}Overridepublic Object getObject(Object key) {// 从二级缓存Cache对象中获取缓存数据Object object delegate.getObject(key);if (object null) {// 从二级缓存中没有取出数据则将这个缓存Key保存下来entriesMissedInCache.add(key);}if (clearOnCommit) {return null;}return object;}Overridepublic void putObject(Object key, Object object) {// 将要缓存的数据保存到HashMap集合还没有真正加入到二级缓存中entriesToAddOnCommit.put(key, object);}// 提交数据public void commit() {if (clearOnCommit) {delegate.clear();}// 刷新待处理的数据flushPendingEntries();// 清空两个容器的数据reset();}// 回滚数据public void rollback() {unlockMissedEntries();reset();}private void flushPendingEntries() {// 将entriesToAddOnCommit容器中的数据一一添加到Cache对象中for (Map.EntryObject, Object entry : entriesToAddOnCommit.entrySet()) {delegate.putObject(entry.getKey(), entry.getValue());}// 遍历entriesMissedInCache容器中的缓存Key// 去重后将Value值置空添加到Cache对象中for (Object entry : entriesMissedInCache) {if (!entriesToAddOnCommit.containsKey(entry)) {delegate.putObject(entry, null);}}}private void reset() {clearOnCommit false;entriesToAddOnCommit.clear();entriesMissedInCache.clear();}private void unlockMissedEntries() {// 遍历entriesMissedInCache容器中的缓存Key// 逐一从Cache对象中移除for (Object entry : entriesMissedInCache) {try {delegate.removeObject(entry);} // catch ...}} }由 源码11 可知TransactionalCache类对Cache类进行了增强除了组合一个Cache对象以保存缓存对象还分别组合了一个HashMap容器和一个HashSet容器分别用于保存即将存入Cache对象的数据以及保存从二级缓存中没有取出的数据时的缓存Key。 1如果要将数据存入二级缓存则调用putObject()方法。 该方法将要缓存的数据保存到entriesToAddOnCommit容器。注意此时数据还没有真正保存到Cache对象中。 要想数据真正保存到Cache对象中还需要调用commit()方法该方法会将entriesToAddOnCommit容器中的数据一一添加到Cache对象中还会以entriesMissedInCache容器中的缓存Key去重后的也添加到Cache对象中只是它的Value值为null。 这样做的目的在于即使某个缓存Key的查询结果为null也要缓存下次相同的缓存Key查询时直接返回null即可。 2如果要将数据从二级缓存中取出来则调用getObject()方法该方法会根据缓存Key从Cache对象中取数据如果没有取到数据则将当前缓存Key保存到entriesMissedInCache容器中取到数据则直接返回。 6.3.2 二级缓存在查询时使用 执行查询SQL语句时会调用CachingExecutor类的query()方法中 源码12org.apache.ibatis.executor.CachingExecutorOverride public E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)throws SQLException {BoundSql boundSql ms.getBoundSql(parameterObject);// 构造缓存KeyCacheKey key createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }Override public E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,CacheKey key, BoundSql boundSql) throws SQLException {// 获取MappedStatement对象中维护的二级缓存对象Cache cache ms.getCache();if (cache ! null) {// 判断是否需要刷新二级缓存flushCacheIfRequired(ms);// 主配置文件中的cacheEnabled属性为true时使用二级缓存if (ms.isUseCache() resultHandler null) {ensureNoOutParams(ms, boundSql);// 从二级缓存中获取缓存数据SuppressWarnings(unchecked)ListE list (ListE) tcm.getObject(cache, key);if (list null) {// 二级缓存中没有获取到数据则从数据库中查询list delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);// 将数据库查询结果保存到二级缓存中tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}// 没有二级缓存时直接从数据库中查询return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }private void flushCacheIfRequired(MappedStatement ms) {Cache cache ms.getCache();if (cache ! null ms.isFlushCacheRequired()) {// 如果select标签的flushCache属性为true则直接清除缓存。默认为falsetcm.clear(cache);} }由 源码12 可知CachingExecutor类的query()方法的逻辑如下 1调用createCacheKey()方法创建缓存Key对象 2调用MappedStatement对象的getCache()方法获取维护的二级缓存对象。如果有则进入使用二级缓存的逻辑如果没有则直接从数据库中查询。 3判断是否需要刷新二级缓存。如果select标签的flushCache属性为trueselect标签中默认为false则直接调用TransactionalCacheManager的clear()方法清除缓存。 4判断主配置文件中的cacheEnabled属性为true时真正使用二级缓存。 5根据缓存Key从二级缓存中获取缓存数据如果获取到了则直接返回如果二级缓存中没有获取到数据则从数据库中查询再将数据库查询结果保存到二级缓存中。 6.3.3 二级缓存在更新时清空 和一级缓存一样二级缓存也会在执行更新语句时被清空即CachingExecutor类的update()方法 源码13org.apache.ibatis.executor.CachingExecutorOverride public int update(MappedStatement ms, Object parameterObject) throws SQLException {// 必要时清空二级缓存flushCacheIfRequired(ms);// 执行更新语句return delegate.update(ms, parameterObject); }由 源码13 可知在执行更新SQL语句之前会根据select|insert|update|delete标签的flushCache属性来判断是否需要清空二级缓存。 而select标签的flushCache属性值默认为falseinsert|update|delete标签的flushCache属性值默认为true因此在执行更新语句时会清空二级缓存。 … 下面做个简单的测试。沿用上面的单元测试代码两次调用相同的selectAll()方法不一样的是查询后需要手动调用commit()方法SELECT语句不会自动调用commit()方法 Test public void testCache() throws IOException, NoSuchMethodException {Reader reader Resources.getResourceAsReader(mybatis-config.xml);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(reader);SqlSession sqlSession sqlSessionFactory.openSession();UserMapper userMapper sqlSession.getMapper(UserMapper.class);// 第一次调用selectAll并手动提交userMapper.selectAll();sqlSession.commit();// 第二次调用selectAll并手动提交userMapper.selectAll();sqlSession.commit(); }另外还需要在SQL映射文件中添加一行cache/。 借助Debug工具可以发现第一次调用selectAll()方法时二级缓存中没有数据程序会调用query()方法从数据库查询数据并存放到二级缓存中 第二次调用selectAll()方法时二级缓存中已经保存了第一次查询的数据这次直接从缓存中就可以取到数据而不需要再去数据库查询 注意如果单元测试中没有sqlSession.commit();这一行代码会发现数据不会保存到二级缓存中。 … 本节完更多内容请查阅分类专栏MyBatis3源码深度解析
http://www.dnsts.com.cn/news/76847.html

相关文章:

  • 题库网站建设的绩效指标汕头市住监局官网
  • 桐城市美丽乡村建设专题网站网站建设开题报告书
  • 公司想建个网站怎么弄wordpress管理面板忘记密码
  • 绍兴手机网站建设网站开发的论文
  • 5000做网站网站定制开发 广州
  • 响应式网站是做多大尺寸wordpress 仪表盘美化
  • wordpress网站邀请码自己制作的网站如何发布
  • 都江堰市网站建设招标信息网哪个比较好
  • 网站标题分隔符原阳网站建设
  • 备案号怎么添加到网站太原网站建设价格
  • 一流的低价网站建设网页游戏大厅在线玩
  • 盈利网站网站上传用什么软件做视频教程
  • 5g对网站建设的影响企业查询软件排行榜
  • 怎样免费网站建设为什么不能安装wordpress
  • 做网站建设的有哪些分销系统网站
  • 手机网站首页怎么查一个网站是什么程序做的
  • 网站建设步骤及推广方法oa系统软件
  • 爱站网关键词排名旅游app页面设计图
  • iis 默认网站 删除名师工作室网站建设建议
  • 网站移动窗口代码四川人防工程建设网站
  • 一起做陶艺搬上网站中国的网站做欧美风
  • 做网站视频下载商品的销售网站总体设计
  • 网站开发模板下载网站搜索引擎友好性分析
  • 学校期末评语网站开发电子商务系统建设网站策划书
  • 做网站较好的框架深圳公司注册资金最低多少
  • 网站制作培训班dede 手机站 怎么获取跳转网站
  • 机构网站建设需要交费吗扑克直播软件app开发
  • 企业做网站的方案现在视频做网站晚了吗
  • 网站做rss+wordpress网站程序上传完
  • 怎么做视频解析网站吗wordpress 安全防护