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

站长工具网站查询拓客软件

站长工具网站查询,拓客软件,深圳网站设计 公司价格,wordpress赞的代码文章目录 一、目标#xff1a;一级缓存二、设计#xff1a;一级缓存三、实现#xff1a;一级缓存3.1 工程结构3.2 一级缓存类图3.3 一级缓存实现3.3.1 定义缓存接口3.3.2 实现缓存接口3.3.3 创建缓存KEY3.3.4 NULL值缓存key 3.4 定义缓存机制、占位符和修改配置文件3.4.1 定… 文章目录 一、目标一级缓存二、设计一级缓存三、实现一级缓存3.1 工程结构3.2 一级缓存类图3.3 一级缓存实现3.3.1 定义缓存接口3.3.2 实现缓存接口3.3.3 创建缓存KEY3.3.4 NULL值缓存key 3.4 定义缓存机制、占位符和修改配置文件3.4.1 定义缓存机制3.4.2 定义占位符符3.4.3 修改映射器语句类3.4.4 修改配置项 3.5 在会话中缓存使用3.5.1 创建缓存KEY3.5.2 抽象执行器实现 3.6 解析缓存机制 四、测试一级缓存4.1 修改XML配置文件4.2 单元测试4.2.1 两次查询4.2.2 提交会话4.2.3 关闭会话 五、总结一级缓存 一、目标一级缓存 什么是一级缓存 在数据库的一次会话中有时候我们可能需要反复的执行完全相同查询语句。 如果不采取一些优化的手段那么每一次查询都会查询一次数据库而在极短的会话周期内几乎反复查询出来的结果也是完全相同的 与内存获取相比相同的数据再查询数据库的代价是很大的如果系统的调用量较大那么这可能造成很大的资源浪费。、 所以结合我们实现的 ORM 框架在一个会话周期内添加缓存操作当会话结束 commit/close/clear 时则进行清空缓存。 二、设计一级缓存 一级缓存如何设计 mybatis的一级缓存主要在于一次 session 会话周期内将相同的执行语句结果缓存起来避免重复执行数据操作。当发生一切影响 session 会话的操作时都会清空缓存避免发生胀肚。 https://article-images.zsxq.com/Fq6qNr4inPGf3qav-TzvEntASm4n 在 Mybatis 的 XML 配置文件中可以设置本地缓存的机制如果不设置则是默认 SESSION 级别也就是使用一级缓存保存会话生命周期内的数据。 如果设置为 STATEMENT 则不使用一级缓存。 SqlSession 的工作主要交给 Executor 执行器完成负责数据库的各种操作。 当创建一个 SqlSession 对象时Mybatis 会为这个 SqlSession 创建一个新的 Executor 执行器。而缓存的工具包也是在执行器的创建时构建出来的。 基于缓存的创建在会话周期内保存查询结果数据当一个 session 会话内发生了改变数据的行为包括insert/delete/update 则清空缓存。另外当主动执行 close、commit、clear 操作时也要顺便把缓存数据清空。这样才能尽最大可能的提高查询效率的同时降低发生脏读的可能。 三、实现一级缓存 3.1 工程结构 mybatis-step-17 |-src|-main| |-java| |-com.lino.mybatis| |-annotations| | |-Delete.java| | |-Insert.java| | |-Select.java| | |-Update.java| |-binding| | |-MapperMethod.java| | |-MapperProxy.java| | |-MapperProxyFactory.java| | |-MapperRegistry.java| |-builder| | |-annotations| | | |-MapperAnnotationBuilder.java| | |-xml| | | |-XMLConfigBuilder.java| | | |-XMLMapperBuilder.java| | | |-XMLStatementBuilder.java| | |-BaseBuilder.java| | |-MapperBuilderAssistant.java| | |-ParameterExpression.java| | |-ResultMapResolver.java| | |-SqlSourceBuilder.java| | |-StaticSqlSource.java| |-cache| | |-impl| | | |-PrepetualCache.java| | |-Cache.java| | |-CacheKey.java| | |-NullCacheKey.java| |-datasource| | |-druid| | | |-DruidDataSourceFacroty.java| | |-pooled| | | |-PooledConnection.java| | | |-PooledDataSource.java| | | |-PooledDataSourceFacroty.java| | | |-PoolState.java| | |-unpooled| | | |-UnpooledDataSource.java| | | |-UnpooledDataSourceFacroty.java| | |-DataSourceFactory.java| |-executor| | |-keygen| | | |-Jdbc3KeyGenerator.java| | | |-KeyGenerator.java| | | |-NoKeyGenerator.java| | | |-SelectKeyGenerator.java| | |-parameter| | | |-ParameterHandler.java| | |-result| | | |-DefaultResultContext.java| | | |-DefaultResultHandler.java| | |-resultset| | | |-DefaultResultSetHandler.java| | | |-ResultSetHandler.java| | | |-ResultSetWrapper.java| | |-statement| | | |-BaseStatementHandler.java| | | |-PreparedStatementHandler.java| | | |-SimpleStatementHandler.java| | | |-StatementHandler.java| | |-BaseExecutor.java| | |-ExecutionPlaceholder.java| | |-Executor.java| | |-SimpleExecutor.java| |-io| | |-Resources.java| |-mapping| | |-BoundSql.java| | |-Environment.java| | |-MappedStatement.java| | |-ParameterMapping.java| | |-ResultFlag.java| | |-ResultMap.java| | |-ResultMapping.java| | |-SqlCommandType.java| | |-SqlSource.java| |-parsing| | |-GenericTokenParser.java| | |-TokenHandler.java| |-plugin| | |-Interceptor.java| | |-InterceptorChain.java| | |-Intercepts.java| | |-Invocation.java| | |-Plugin.java| | |-Signature.java| |-reflection| | |-factory| | | |-DefaultObjectFactory.java| | | |-ObjectFactory.java| | |-invoker| | | |-GetFieldInvoker.java| | | |-Invoker.java| | | |-MethodInvoker.java| | | |-SetFieldInvoker.java| | |-property| | | |-PropertyNamer.java| | | |-PropertyTokenizer.java| | |-wrapper| | | |-BaseWrapper.java| | | |-BeanWrapper.java| | | |-CollectionWrapper.java| | | |-DefaultObjectWrapperFactory.java| | | |-MapWrapper.java| | | |-ObjectWrapper.java| | | |-ObjectWrapperFactory.java| | |-MetaClass.java| | |-MetaObject.java| | |-Reflector.java| | |-SystemMetaObject.java| |-scripting| | |-defaults| | | |-DefaultParameterHandler.java| | | |-RawSqlSource.java| | |-xmltags| | | |-DynamicContext.java| | | |-DynamicSqlSource.java| | | |-ExpressionEvaluator.java| | | |-IfSqlNode.java| | | |-MixedSqlNode.java| | | |-OgnlCache.java| | | |-OgnlClassResolver.java| | | |-SqlNode.java| | | |-StaticTextSqlNode.java| | | |-TextSqlNode.java| | | |-TrimSqlNode.java| | | |-XMLLanguageDriver.java| | | |-XMLScriptBuilder.java| | |-LanguageDriver.java| | |-LanguageDriverRegistry.java| |-session| | |-defaults| | | |-DefaultSqlSession.java| | | |-DefaultSqlSessionFactory.java| | |-Configuration.java| | |-LocalCacheScope.java| | |-ResultContext.java| | |-ResultHandler.java| | |-RowBounds.java| | |-SqlSession.java| | |-SqlSessionFactory.java| | |-SqlSessionFactoryBuilder.java| | |-TransactionIsolationLevel.java| |-transaction| | |-jdbc| | | |-JdbcTransaction.java| | | |-JdbcTransactionFactory.java| | |-Transaction.java| | |-TransactionFactory.java| |-type| | |-BaseTypeHandler.java| | |-DateTypeHandler.java| | |-IntegerTypeHandler.java| | |-JdbcType.java| | |-LongTypeHandler.java| | |-SimpleTypeRegistry.java| | |-StringTypeHandler.java| | |-TypeAliasRegistry.java| | |-TypeHandler.java| | |-TypeHandlerRegistry.java|-test|-java| |-com.lino.mybatis.test| |-dao| | |-IActivityDao.java| |-plugin| | |-TestPlugin.java| |-po| | |-Activity.java| |-ApiTest.java|-resources|-mapper| |-Activity_Mapper.xml|-mybatis-config-datasource.xml3.2 一级缓存类图 以 XMLConfigBuilder 配置构建器为入口解析 XML 配置中关于缓存机制的配置。 关闭一级缓存可以通过 LocalCacheScope 配置的方式进行处理。 接下来就是在会话周期内以创建 SqlSession 构建出 Executor 执行器初始化缓存组件在执行查询操作时存放数据。 当会话周期内有执行 insert/update/delete 以及关闭、提交、清空等操作时在缓存组件中删除相关的缓存数据。 具体的缓存操作通过 PerpetualCache 以及永久缓存实现类实现 Cache 缓存接口来完成其中关于缓存的 ID 字段使用 CacheKey 为查询操作创建索引。 注意一般缓存框架的数据结构基本上都是 Key-Value 方式存储Mybatis 对于其 Key 的生成采取规则为[mappedStatementId offset limit SQL queryParams environment] 生成一个哈希码作为 Key 使用。 3.3 一级缓存实现 在 Mybatis 框架中有一个单独提供的 cache 包用于处理数据的缓存操作一级缓存、二级缓存的操作都是这个包下提供的服务。 3.3.1 定义缓存接口 Cache.java package com.lino.mybatis.cache;/*** description: 缓存接口*/ public interface Cache {/*** 获取ID每个缓存都有唯一的ID标识** return ID*/String getId();/*** 存入值** param key 键* param value 值*/void putObject(Object key, Object value);/*** 获取值** param key 键* return 值*/Object getObject(Object key);/*** 删除值** param key 键* return 值*/Object removeObject(Object key);/*** 清空*/void clear();/*** 获取缓存大小** return 缓存大小*/int getSize(); }缓存接口主要提供了数据的存放、获取、删除、清空以及数量大小的获取。 3.3.2 实现缓存接口 PerpetualCache.java package com.lino.mybatis.cache.impl;import com.alibaba.fastjson.JSON; import com.lino.mybatis.cache.Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map;/*** description: 一级缓存在 Session 生命周期内一直保持每创建新的 OpenSession 都会创建一个缓存器 PerpetualCache*/ public class PerpetualCache implements Cache {private Logger logger LoggerFactory.getLogger(PerpetualCache.class);private String id;/*** 使用HashMap存放一级缓存数据session 生命周期较短正常情况下数据不会一直在缓存存放*/private MapObject, Object cache new HashMap(16);public PerpetualCache(String id) {this.id id;}Overridepublic String getId() {return id;}Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}Overridepublic Object getObject(Object key) {Object obj cache.get(key);if (null ! obj) {logger.info(一级缓存 \r\nkey{} \r\nval{}, key, JSON.toJSONString(obj));}return obj;}Overridepublic Object removeObject(Object key) {return cache.remove(key);}Overridepublic void clear() {cache.clear();}Overridepublic int getSize() {return cache.size();} }一般缓存的实现类也叫永远缓存使用 HashMap 存放数据因为这个缓存是配合整个会话周期内使用和销毁。 所以使用 HashMap 比较简单不需要太多的容量和大小限制。 基本这个类里的操作都是对 HashMap 的存放、删除和清空的基本操作。 3.3.3 创建缓存KEY 通常使用 HashMap 的时候 Key 都是一个 String 的值那么这里因为需要对查询的信息以及 SQL 做一个 ID 使用但这样都拼装下来就太长了。所以在缓存 Key 的实现中基于这些信息创建了一个新的 HashCode 作为 KEY 使用。 CacheKey.java package com.lino.mybatis.cache;import java.io.Serializable; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List;/*** description: 缓存key一般缓存框架的数据结构基本上都是 key-value 方式存储* Mybatis 对于其 key 的生成采取规则为[mappedStatementId offset limit SQL queryParams environment]生成一个哈希码*/ public class CacheKey implements Cloneable, Serializable {private static final long serialVersionUID 1146682552656046210L;public static final CacheKey NULL_CACHE_KEY new NullCacheKey();private static final int DEFAULT_MULTIPLYEP 37;private static final int DEFAULT_HASHCODE 17;private int multiplier;private int hashcode;private long checksum;private int count;private ListObject updateList;public CacheKey() {this.hashcode DEFAULT_HASHCODE;this.multiplier DEFAULT_MULTIPLYEP;this.count 0;this.updateList new ArrayList();}public CacheKey(Object[] objects) {this();updateAll(objects);}public int getUpdateCount() {return updateList.size();}public void update(Object object) {if (object ! null object.getClass().isArray()) {int length Array.getLength(object);for (int i 0; i length; i) {Object element Array.get(object, i);doUpdate(element);}} else {doUpdate(object);}}private void doUpdate(Object object) {// 计算hash值校验码int baseHashCode object null ? 1 : object.hashCode();count;checksum baseHashCode;baseHashCode * count;hashcode multiplier hashcode baseHashCode;updateList.add(object);}public void updateAll(Object[] objects) {for (Object o : objects) {update(o);}}Overridepublic boolean equals(Object object) {if (this object) {return true;}if (!(object instanceof CacheKey)) {return false;}final CacheKey cachekey (CacheKey) object;if (hashcode ! cachekey.hashcode) {return false;}if (checksum ! cachekey.checksum) {return false;}if (count ! cachekey.count) {return false;}for (int i 0; i updateList.size(); i) {Object thisObject updateList.get(i);Object thatObject cachekey.updateList.get(i);if (thisObject null) {if (thatObject ! null) {return false;}} else {if (!thisObject.equals(thatObject)) {return false;}}}return true;}Overridepublic int hashCode() {return hashcode;}Overridepublic String toString() {StringBuilder returnValue new StringBuilder().append(hashcode).append(:).append(checksum);for (Object obj : updateList) {returnValue.append(:).append(obj);}return returnValue.toString();}Overridepublic CacheKey clone() throws CloneNotSupportedException {CacheKey clonedCacheKey (CacheKey) super.clone();clonedCacheKey.updateList new ArrayList(updateList);return clonedCacheKey;}}doUpdate哈希计算 Mybatis 对于其 Key 的生成规则采取规则为 [mappedStatementId offset limit SQL queryParams environment] 生成一个哈希码。而 doUpdate 方法的 object 入参对象就是用于拼装哈希值的具体操作。 equals哈希 equal 如果遇到相同哈希值避免对象重复那么 CacheKey 缓存 Key 重写了 equals 对比方法。这也是为什么在 doUpdate 计算哈希方法时把对象添加到 updateList.add(object) 集合中就是用于这里的 equals 判断使用 3.3.4 NULL值缓存key NullCacheKey.java package com.lino.mybatis.cache;/*** description: NULL值缓存key*/ public class NullCacheKey extends CacheKey {private static final long serialVersionUID 3704229911977019465L;public NullCacheKey() {super();} }3.4 定义缓存机制、占位符和修改配置文件 在 Mybatis 框架中默认情况下以及缓存是开启使用的但是也支持用户可以自主关闭以及缓存。 settings!--缓存级别SESSION/STATEMENT--setting namelocalCacheScope valueSESSION/ /settingslocalCacheScope 缓存机制的属性值有两个SESSION、STATEMENT。 STATEMENT关闭一级缓存。 3.4.1 定义缓存机制 LocalCacheScope.java package com.lino.mybatis.session;/*** description: 本地缓存机制*/ public enum LocalCacheScope {/*** 本地缓存*/SESSION, STATEMENT }LocalCacheScope 缓存机制是个枚举值配置分别为SESSION, STATEMENT SESSION为默认值支持使用一级缓存。STATEMENT不支持使用一级缓存。 3.4.2 定义占位符符 ExecutionPlaceholder.java package com.lino.mybatis.executor;/*** description: 占位符*/ public enum ExecutionPlaceholder {/*** 占位符*/EXECUTION_PLACEHOLDER }3.4.3 修改映射器语句类 MappedStatement.java package com.lino.mybatis.mapping;import com.lino.mybatis.executor.keygen.Jdbc3KeyGenerator; import com.lino.mybatis.executor.keygen.KeyGenerator; import com.lino.mybatis.executor.keygen.NoKeyGenerator; import com.lino.mybatis.scripting.LanguageDriver; import com.lino.mybatis.session.Configuration; import java.util.Collections; import java.util.List;/*** description: 映射器语句类*/ public class MappedStatement {private String resource;private Configuration configuration;private String id;private SqlCommandType sqlCommandType;private SqlSource sqlSource;Class? resultType;private LanguageDriver lang;private ListResultMap resultMaps;private boolean flushCacheRequired;...public MappedStatement() {}...public boolean isFlushCacheRequired() {return flushCacheRequired;} }3.4.4 修改配置项 Configuration.java package com.lino.mybatis.session;import com.lino.mybatis.binding.MapperRegistry; import com.lino.mybatis.datasource.druid.DruidDataSourceFactory; import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory; import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory; import com.lino.mybatis.executor.Executor; import com.lino.mybatis.executor.SimpleExecutor; import com.lino.mybatis.executor.keygen.KeyGenerator; import com.lino.mybatis.executor.parameter.ParameterHandler; import com.lino.mybatis.executor.resultset.DefaultResultSetHandler; import com.lino.mybatis.executor.resultset.ResultSetHandler; import com.lino.mybatis.executor.statement.PreparedStatementHandler; import com.lino.mybatis.executor.statement.StatementHandler; import com.lino.mybatis.mapping.BoundSql; import com.lino.mybatis.mapping.Environment; import com.lino.mybatis.mapping.MappedStatement; import com.lino.mybatis.mapping.ResultMap; import com.lino.mybatis.plugin.Interceptor; import com.lino.mybatis.plugin.InterceptorChain; import com.lino.mybatis.reflection.MetaObject; import com.lino.mybatis.reflection.factory.DefaultObjectFactory; import com.lino.mybatis.reflection.factory.ObjectFactory; import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory; import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory; import com.lino.mybatis.scripting.LanguageDriver; import com.lino.mybatis.scripting.LanguageDriverRegistry; import com.lino.mybatis.scripting.xmltags.XMLLanguageDriver; import com.lino.mybatis.transaction.Transaction; import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory; import com.lino.mybatis.type.TypeAliasRegistry; import com.lino.mybatis.type.TypeHandlerRegistry; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set;/*** description: 配置项*/ public class Configuration {/*** 环境*/protected Environment environment;/*** 是否使用自动生成键值对*/protected boolean useGeneratedKeys false;/*** 缓存机制默认不配置的情况是 SESSION*/protected LocalCacheScope localCacheScope LocalCacheScope.SESSION;...public LocalCacheScope getLocalCacheScope() {return localCacheScope;}public void setLocalCacheScope(LocalCacheScope localCacheScope) {this.localCacheScope localCacheScope;}...}添加 LocalCacheScope 缓存机制。 3.5 在会话中缓存使用 Mybatis 的会话创建 SqlSession 其实主要操作都集中在 Executor 执行器的抽象类中通过抽象类的创建实例化 new PerpetualCache(LocalCache) 一级缓存并在执行器中完成缓存存放、使用、删除等操作。 3.5.1 创建缓存KEY Executor.java package com.lino.mybatis.executor;import com.lino.mybatis.cache.CacheKey; import com.lino.mybatis.mapping.BoundSql; import com.lino.mybatis.mapping.MappedStatement; import com.lino.mybatis.session.ResultHandler; import com.lino.mybatis.session.RowBounds; import com.lino.mybatis.transaction.Transaction; import java.sql.SQLException; import java.util.List;/*** description: 执行器*/ public interface Executor {/*** 结果处理器*/ResultHandler NO_RESULT_HANDLER null;/*** 更新** param ms 映射器语句* param parameter 参数* return 返回的是受影响的行数* throws SQLException SQL异常*/int update(MappedStatement ms, Object parameter) throws SQLException;/*** 查询,含缓存** param ms 映射器语句* param parameter 参数* param rowBounds 分页记录限制* param resultHandler 结果处理器* param key 缓存key* param boundSql SQL对象* param E 返回的类型* return ListE* throws SQLException SQL异常*/E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException;/*** 查询** param ms 映射器语句* param parameter 参数* param rowBounds 分页记录限制* param resultHandler 结果处理器* param E 返回的类型* return ListE* throws SQLException SQL异常*/E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;/*** 获取事务** return 事务对象*/Transaction getTransaction();/*** 提交** param required 是否请求执行* throws SQLException SQL异常*/void commit(boolean required) throws SQLException;/*** 回滚** param required 是否请求执行* throws SQLException SQL异常*/void rollback(boolean required) throws SQLException;/*** 关闭** param forceRollback 是否强制回滚*/void close(boolean forceRollback);/*** 清理session缓存*/void clearLocalCache();/*** 创建缓存key** param ms 映射器语句* param parameterObject 参数对象* param rowBounds 分页记录限制* param boundSql SQL对象* return 缓存key*/CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); }Executor 执行器接口有2个查询方法一个含有缓存 Key另外一个不含有。含有缓存 Key 的 query 方法会被另外一个不含有缓存 Key 的 query 方法调用。 3.5.2 抽象执行器实现 BaseExecutor package com.lino.mybatis.executor;import com.lino.mybatis.cache.CacheKey; import com.lino.mybatis.cache.impl.PerpetualCache; import com.lino.mybatis.mapping.BoundSql; import com.lino.mybatis.mapping.MappedStatement; import com.lino.mybatis.mapping.ParameterMapping; import com.lino.mybatis.reflection.MetaObject; import com.lino.mybatis.session.Configuration; import com.lino.mybatis.session.LocalCacheScope; import com.lino.mybatis.session.ResultHandler; import com.lino.mybatis.session.RowBounds; import com.lino.mybatis.transaction.Transaction; import com.lino.mybatis.type.TypeHandlerRegistry; import org.slf4j.LoggerFactory; import java.sql.SQLException; import java.sql.Statement; import java.util.List;/*** description: 执行器抽象基类*/ public abstract class BaseExecutor implements Executor {private org.slf4j.Logger logger LoggerFactory.getLogger(BaseExecutor.class);protected Configuration configuration;protected Transaction transaction;protected Executor wrapper;/*** 本地缓存*/protected PerpetualCache localCache;private boolean closed;/*** 查询堆栈*/protected int queryStack 0;public BaseExecutor(Configuration configuration, Transaction transaction) {this.configuration configuration;this.transaction transaction;this.wrapper this;this.localCache new PerpetualCache(LocalCache);}Overridepublic int update(MappedStatement ms, Object parameter) throws SQLException {if (closed) {throw new RuntimeException(Executor was closed.);}clearLocalCache();return doUpdate(ms, parameter);}Overridepublic E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {if (closed) {throw new RuntimeException(Executor was closed.);}// 清理局部缓存查询堆栈为0则清理。queryStack 避免递归调用清理if (queryStack 0 ms.isFlushCacheRequired()) {clearLocalCache();}ListE list;try {queryStack;// 根据cacheKey从localCache中查询数据list resultHandler null ? (ListE) localCache.getObject(key) : null;if (list null) {list queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack 0) {if (configuration.getLocalCacheScope() LocalCacheScope.STATEMENT) {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, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);try {list doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}// 存入缓存localCache.putObject(key, list);return list;}Overridepublic E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 1.获取绑定SQLBoundSql boundSql ms.getBoundSql(parameter);// 2.创建缓存keyCacheKey key createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}/*** 更新方法** param ms 映射器语句* param parameter 参数* return 返回的是受影响的行数* throws SQLException SQL异常*/protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;/*** 查询方法** param ms 映射器语句* param parameter 参数* param rowBounds 分页记录限制* param resultHandler 结果处理器* param boundSql SQL对象* param E 返回的类型* return ListE* throws SQLException SQL异常*/protected abstract E ListE doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;Overridepublic Transaction getTransaction() {if (closed) {throw new RuntimeException(Executor was closed.);}return transaction;}Overridepublic void commit(boolean required) throws SQLException {if (closed) {throw new RuntimeException(Cannot commit, transaction is already closed.);}clearLocalCache();if (required) {transaction.commit();}}Overridepublic void rollback(boolean required) throws SQLException {if (!closed) {try {clearLocalCache();} finally {if (required) {transaction.rollback();}}}}Overridepublic void clearLocalCache() {if (!closed) {localCache.clear();}}Overridepublic CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {if (closed) {throw new RuntimeException(Executor was closed.);}CacheKey cacheKey new CacheKey();cacheKey.update(ms.getId());cacheKey.update(rowBounds.getOffset());cacheKey.update(rowBounds.getLimit());cacheKey.update(boundSql.getSql());ListParameterMapping parameterMappings boundSql.getParameterMappings();TypeHandlerRegistry typeHandlerRegistry ms.getConfiguration().getTypeHandlerRegistry();for (ParameterMapping parameterMapping : parameterMappings) {Object value;String propertyName parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) {value boundSql.getAdditionalParameter(propertyName);} else if (parameterObject null) {value null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value parameterObject;} else {MetaObject metaObject configuration.newMetaObject(parameterObject);value metaObject.getValue(propertyName);}cacheKey.update(value);}if (configuration.getEnvironment() ! null) {cacheKey.update(configuration.getEnvironment().getId());}return cacheKey;}Overridepublic void close(boolean forceRollback) {try {try {rollback(forceRollback);} finally {transaction.close();}} catch (SQLException e) {logger.warn(Unexpected exception on closing transaction. Cause: e);} finally {transaction null;closed true;}}/*** 关闭语句** param statement 语句*/protected void closeStatement(Statement statement) {if (statement ! null) {try {statement.close();} catch (SQLException ignore) {}}} }query查询不含缓存方法 在 BaseExecutor#query 主要新增加了关于缓存 Key 的创建创建后调用重载的另外一个含有缓存 Key 的 query 方法。 创建缓存KEY Override public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {if (closed) {throw new RuntimeException(Executor was closed.);}CacheKey cacheKey new CacheKey();cacheKey.update(ms.getId());cacheKey.update(rowBounds.getOffset());cacheKey.update(rowBounds.getLimit());cacheKey.update(boundSql.getSql());ListParameterMapping parameterMappings boundSql.getParameterMappings();TypeHandlerRegistry typeHandlerRegistry ms.getConfiguration().getTypeHandlerRegistry();for (ParameterMapping parameterMapping : parameterMappings) {Object value;String propertyName parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) {value boundSql.getAdditionalParameter(propertyName);} else if (parameterObject null) {value null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value parameterObject;} else {MetaObject metaObject configuration.newMetaObject(parameterObject);value metaObject.getValue(propertyName);}cacheKey.update(value);}if (configuration.getEnvironment() ! null) {cacheKey.update(configuration.getEnvironment().getId());}return cacheKey; }缓存 Key 的创建需要依赖于[mappedStatementId offset limit SQL queryParams environment] 信息构建出一个哈希值。所以这里把这些对应的信息分别传递给 cacheKey#update 方法。 查询数据缓存 Override public E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {if (closed) {throw new RuntimeException(Executor was closed.);}// 清理局部缓存查询堆栈为0则清理。queryStack 避免递归调用清理if (queryStack 0 ms.isFlushCacheRequired()) {clearLocalCache();}ListE list;try {queryStack;// 根据cacheKey从localCache中查询数据list resultHandler null ? (ListE) localCache.getObject(key) : null;if (list null) {list queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack 0) {if (configuration.getLocalCacheScope() LocalCacheScope.STATEMENT) {clearLocalCache();}}return list; }在 query 查询操作中判断 queryStack 是否为0且是否刷新请求如果是则会操作清空缓存。接下来 queryStack 自增以后通过 localCache 获取缓存数据。 首次查询这个数据是空的这个时候会进入到 queryFromDatabase 方法从数据库查询数据并返回结果。此外在 XML 配置中的缓存机制 LocalCacheScope 会在这里判断如果不是 SESSION 机制则清空缓存。 存放缓存数据 private E ListE queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ListE list;localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);try {list doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}// 存入缓存localCache.putObject(key, list);return list; }在一个会话内首次执行 query 查询的时候会把数据库查询到的数据使用 localCache.putObject(key, list) 存放缓存中。同时在存放前是使用占位符占位查询后先清空再存放数据。 删除缓存数据 Override public int update(MappedStatement ms, Object parameter) throws SQLException {if (closed) {throw new RuntimeException(Executor was closed.);}clearLocalCache();return doUpdate(ms, parameter); }Override public void commit(boolean required) throws SQLException {if (closed) {throw new RuntimeException(Cannot commit, transaction is already closed.);}clearLocalCache();if (required) {transaction.commit();} }Override public void rollback(boolean required) throws SQLException {if (!closed) {try {clearLocalCache();} finally {if (required) {transaction.rollback();}}} }Override public void clearLocalCache() {if (!closed) {localCache.clear();} }Override public void close(boolean forceRollback) {try {try {rollback(forceRollback);} finally {transaction.close();}} catch (SQLException e) {logger.warn(Unexpected exception on closing transaction. Cause: e);} finally {transaction null;closed true;} }在前面insert、delete、update都是调用执行器的 update 方法进行处理的。这里的 update、commit、rollback、close都会调用到 clearLocalCache 执行缓存清空。因为 clearLocalCache 也是对外的所以你也可以在缓存机制为 SESSION 级别下手动清空缓存操作。 3.6 解析缓存机制 XMLConfigBuilder.java package com.lino.mybatis.builder.xml;import com.lino.mybatis.builder.BaseBuilder; import com.lino.mybatis.datasource.DataSourceFactory; import com.lino.mybatis.io.Resources; import com.lino.mybatis.mapping.Environment; import com.lino.mybatis.plugin.Interceptor; import com.lino.mybatis.session.Configuration; import com.lino.mybatis.session.LocalCacheScope; import com.lino.mybatis.transaction.TransactionFactory; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.xml.sax.InputSource; import javax.sql.DataSource; import java.io.InputStream; import java.io.Reader; import java.util.List; import java.util.Properties;/*** description: XML配置构建器建造者模式集成BaseBuilder*/ public class XMLConfigBuilder extends BaseBuilder {.../*** 解析配置类型别名、插件、对象工厂、对象包装工厂、设置、环境、类型转换、映射器** return Configuration*/public Configuration parse() {try {// 插件添加pluginElement(root.element(plugins));// 设置settingElement(root.element(settings));// 环境environmentsElement(root.element(environments));// 解析映射器mapperElement(root.element(mappers));} catch (Exception e) {throw new RuntimeException(Error parsing SQL Mapper Configuration. Cause: e, e);}return configuration;}.../*** settings* !--缓存级别SESSION/STATEMENT--* setting namelocalCacheScope valueSESSION/* /settings*/private void settingElement(Element context) {if (context null) {return;}ListElement elements context.elements();Properties props new Properties();for (Element element : elements) {props.setProperty(element.attributeValue(name), element.attributeValue(value));}configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty(localCacheScope)));}...}基于 XMLConfigBuilder 配置构建器解析配置在 XML 文件中的缓存机制并把解析处理出来的内容放到 Configuration 配置项中。 四、测试一级缓存 4.1 修改XML配置文件 mybatis-config-datasource.xml ?xml version1.0 encodingUTF-8? !DOCTYPE configuration PUBLIC -//mybatis.org//DTD Config 3.0//ENhttp://mybatis.org/dtd/mybatis-3-config.dtd configurationpluginsplugin interceptorcom.lino.mybatis.test.plugin.TestPluginproperty nametest00 value100/property nametest01 value200//plugin/pluginssettings!--缓存级别SESSION/STATEMENT--setting namelocalCacheScope valueSESSION//settingsenvironments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver valuecom.mysql.jdbc.Driver/property nameurlvaluejdbc:mysql://127.0.0.1:3306/mybatis?useUnicodetrueamp;characterEncodingutf8/property nameusername valueroot/property namepassword value123456//dataSource/environment/environmentsmappers!--XML配置--mapper resourcemapper/Activity_Mapper.xml//mappers /configuration4.2 单元测试 4.2.1 两次查询 ApiTest.java Testpublic void test_queryActivityById() {// 1.获取映射器对象IActivityDao dao sqlSession.getMapper(IActivityDao.class);// 2.测试验证Activity activity new Activity();activity.setActivityId(100001L);logger.info(测试结果{}, JSON.toJSONString(dao.queryActivityById(activity)));logger.info(测试结果{}, JSON.toJSONString(dao.queryActivityById(activity)));}在单元测试验证中开启 session 后执行力两次相同的查询。验证缓存的使用 测试结果 14:16:03.602 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 1610525991. 拦截SQLSELECT activity_id, activity_name, activity_desc, create_time, update_timeFROM activitywhere activity_id ? 14:16:03.617 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value100001 14:16:03.617 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果{activityDesc:测试活动,activityId:100001,activityName:活动名,createTime:1628424890000,updateTime:1628424890000} 14:16:03.617 [main] INFO c.l.m.s.defaults.DefaultSqlSession - 执行查询 statementcom.lino.mybatis.test.dao.IActivityDao.queryActivityById parameter{activityId:100001} 14:16:03.617 [main] INFO c.l.mybatis.builder.SqlSourceBuilder - 构建参数映射 propertyactivityId propertyTypeclass java.lang.Long 14:16:03.617 [main] INFO c.l.m.cache.impl.PerpetualCache - 一级缓存 key-1917147986:525044925:com.lino.mybatis.test.dao.IActivityDao.queryActivityById:0:2147483647:SELECT activity_id, activity_name, activity_desc, create_time, update_timeFROM activitywhere activity_id ?:100001:development val[{activityDesc:测试活动,activityId:100001,activityName:活动名,createTime:1628424890000,updateTime:1628424890000}] 14:16:03.617 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果{activityDesc:测试活动,activityId:100001,activityName:活动名,createTime:1628424890000,updateTime:1628424890000}从测试结果缓存的日志打印和断点调试缓存查询中可以看到执行第二次查询的时候就可以通过缓存获取数据了证明一级缓存生效了。 4.2.2 提交会话 ApiTest.java Test public void test_queryActivityById() {// 1.获取映射器对象IActivityDao dao sqlSession.getMapper(IActivityDao.class);// 2.测试验证Activity activity new Activity();activity.setActivityId(100001L);logger.info(测试结果{}, JSON.toJSONString(dao.queryActivityById(activity)));// 提交会话sqlSession.commit();logger.info(测试结果{}, JSON.toJSONString(dao.queryActivityById(activity))); }测试结果 14:19:50.865 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 1610525991. 拦截SQLSELECT activity_id, activity_name, activity_desc, create_time, update_timeFROM activitywhere activity_id ? 14:19:50.880 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value100001 14:19:50.880 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果{activityDesc:测试活动,activityId:100001,activityName:活动名,createTime:1628424890000,updateTime:1628424890000} 14:19:50.880 [main] INFO c.l.m.s.defaults.DefaultSqlSession - 执行查询 statementcom.lino.mybatis.test.dao.IActivityDao.queryActivityById parameter{activityId:100001} 14:19:50.880 [main] INFO c.l.mybatis.builder.SqlSourceBuilder - 构建参数映射 propertyactivityId propertyTypeclass java.lang.Long 拦截SQLSELECT activity_id, activity_name, activity_desc, create_time, update_timeFROM activitywhere activity_id ? 14:19:50.880 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value100001 14:19:50.880 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果{activityDesc:测试活动,activityId:100001,activityName:活动名,createTime:1628424890000,updateTime:1628424890000}添加 sqlSession.commit()执行会话提交则会清空缓存此时的二次查询已经不会从缓存中获取数据而是要去数据库读取。 4.2.3 关闭会话 ApiTest.java Test public void test_queryActivityById() {// 1.获取映射器对象IActivityDao dao sqlSession.getMapper(IActivityDao.class);// 2.测试验证Activity activity new Activity();activity.setActivityId(100001L);logger.info(测试结果{}, JSON.toJSONString(dao.queryActivityById(activity)));// 提交会话sqlSession.commit();// 关闭会话sqlSession.close();logger.info(测试结果{}, JSON.toJSONString(dao.queryActivityById(activity))); }测试结果 14:22:22.512 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 1610525991. 拦截SQLSELECT activity_id, activity_name, activity_desc, create_time, update_timeFROM activitywhere activity_id ? 14:22:22.512 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value100001 14:22:22.527 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果{activityDesc:测试活动,activityId:100001,activityName:活动名,createTime:1628424890000,updateTime:1628424890000} 14:22:22.527 [main] INFO c.l.m.d.pooled.PooledDataSource - Returned connection 1610525991 to pool. 14:22:22.527 [main] INFO c.l.m.s.defaults.DefaultSqlSession - 执行查询 statementcom.lino.mybatis.test.dao.IActivityDao.queryActivityById parameter{activityId:100001} 14:22:22.527 [main] INFO c.l.mybatis.builder.SqlSourceBuilder - 构建参数映射 propertyactivityId propertyTypeclass java.lang.Longjava.lang.RuntimeException: Executor was closed.at com.lino.mybatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:177)at com.lino.mybatis.executor.BaseExecutor.query(BaseExecutor.java:107)at com.lino.mybatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:54)at com.lino.mybatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:39)at com.lino.mybatis.binding.MapperMethod.execute(MapperMethod.java:50)at com.lino.mybatis.binding.MapperProxy.invoke(MapperProxy.java:36)at com.sun.proxy.$Proxy4.queryActivityById(Unknown Source)at com.lino.mybatis.test.ApiTest.test_queryActivityById(ApiTest.java:48)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)at org.junit.runners.ParentRunner.run(ParentRunner.java:236)at org.junit.runner.JUnitCore.run(JUnitCore.java:157)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)新增 sqlSession.close()当执行会话关闭后调用 rollback(forceRollback) 方法缓存会被清空同时因为会话已经关闭所以再执行的查询会报错Executor was closed。 五、总结一级缓存 一级缓存属于轻量级缓存仅限于在一次 session 会话内完成所以整个模型也可以简单的理解为 使用 HashMap 存放缓存数据当有发生对数据库的操作则进行缓存清空。 通常如果说你的应用是独立的单体应用或者开发体量较小的运营后台类应用可能不会发生任何由于缓存所产生的脏读问题。但当你的应用是分布式部署并且你的 session 会话过长执行了大范围的 select 操作那么要注意此时数据的有效性。 如果都是类似这样的场景你可能需要关闭一级缓存或者在关键节点及时手动清空缓存。 缓存的设计比较小巧整个结构并不算复杂但它的设计贯穿了整个 session 的生命周期这也是提醒我们在设计一个业务流程时要考虑全局的流程状态流转避免一小部分的问题影响全局的结果。 另外关于 CacheKey 缓存 Key 的哈希设计也可以借鉴如果有大长字符串拼接需要作为 key 使用的场景。
http://www.dnsts.com.cn/news/37864.html

相关文章:

  • 安徽网站建设公司国家信用信息公示系统山东
  • 石家庄智能网站建设外贸专业网站建设
  • 深圳蕾奥规划设计公司网站大学计算机网页制作步骤
  • 辽宁网站建设价位模板建站和自助建站
  • 潍坊专业做网站的公司做网站设计工资多少钱
  • 网站网页设计海报图片网站快照时间
  • 小程序建站平台国家信用信息公示系统四川
  • 自媒体营销的方式有哪些seo五大经验分享
  • 人工智能的网站网站制作公司承担
  • 江西有色建设集团有限公司网站wordpress主机404
  • 网站建设合同的注意事项wordpress自定义末班
  • 知识产权教育网站建设传统建筑网站
  • 网站建设的合同wordpress 自定义评论样式
  • 大学 英文网站建设长沙优化网站推广
  • 无法更新网站主页 dedecms都有哪些可以做app的网站
  • 阿里巴巴网站域名注册企业网银app下载
  • 微信清粉网站开发如何查询企业邮箱
  • 宇讯网站建设中国最新消息新闻
  • 系统网站建设ppt石家庄企业网站制作哪家好
  • 做网站最专业的公司网站建设视频教程网
  • 南京溧水城市建设集团网站大数据毕业后去什么岗位就业
  • 全球云邮登陆网站山东中恒建设集团网站
  • 网站wap版福州设计网站
  • 网站系统设计说明书协会网站建设需要注意什么
  • 那些网站做民宿太原网页设计师招聘信息
  • 安阳 网站建设成都双流 网站建设
  • 杭州网站排名seowordpress4.7.2 xss
  • 网站开发哪里接业务芯片商城网站建设
  • 广告传媒公司杭州seo推广优化公司
  • 西宁做网站好的公司wordpress使用手机号登录