赞
踩
MyBatis的缓存分为一级缓存和二级缓存(全局缓存) ,缓存示意图如下图所示。默认情况下,一级缓存是开启的,且不能被关闭。
在同一个SqlSession中执行相同的SQL语句时,将查询结果集缓存,第二次查询时直接取缓存,不在操作数据库。注意:一级缓存最多缓存1024条SQL语句。
原理: 当客户端第一次发出SQL查询语句时,MyBatis执行SQL的查询并将查询结果写入SqlSession的一级缓存中,当第二次相同的SQL的查询语句过来时,则直接从缓存中获取数据,具体过程如上图所示。缓存使用的数据结构是Map,其中key是 MapperId+Offset+Limit+SQL+所有的入参。
同一个SqlSession操作相同的SQL语句,如中间有修改操作时,需不要手动flush?
不需要!!!
当同一个SqlSession多次发出相同的SQL语句查询时,MyBatis会直接从缓存中取数据。如果前后两次中间出现过commit操作(修改、添加、删除),则认为数据发生了变化,MyBatis会把SqlSession中的一级缓存区域全部清空,则下一次的SQL语句查询时,需从数据库中查询数据并将查询的结果写入缓存。
1、MyBatis的一级缓存是SqlSession级别的,但是它并不定义在SqlSessio接口的实现类DefaultSqlSession中,而是定义在DefaultSqlSession的成员变量Executor中,Executor是在openSession的时候被实例化出来的,它的默认实现为SimpleExecutor
2、MyBatis中的一级缓存,与有没有配置无关,只要SqlSession存在,MyBastis一级缓存就存在,localCache的类型是PerpetualCache,它其实很简单,一个id属性+一个HashMap属性而已,id是一个名为"localCache"的字符串,HashMap用于存储数据,Key为CacheKey,Value为查询结果
3、MyBatis的一级缓存查询的时候默认都是会先尝试从一级缓存中获取数据的,但是我们看第6行的代码做了一个判断,ms.isFlushCacheRequired(),即想每次查询都走DB也行,将<select>标签中的flushCache属性设置为true即可,这意味着每次查询的时候都会清理一遍PerpetualCache,PerpetualCache中没数据,自然只能走DB
从MyBatis一级缓存来看,它以单纯的HashMap做缓存,没有容量控制,而一次SqlSession中通常来说并不会有大量的查询操作,因此只适用于一次SqlSession,如果用到二级缓存的Mapper级别的场景,有可能缓存数据不断碰到而导致内存溢出。
<insert>、<delete>、<update>最终都会转换为update方法,看一下BaseExecutor的update方法:
- 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);
- }
clearLocalCache()方法 =====> 所有的增、删、改都会清空缓存,这和是否配置了flushCache=true是无关的。
namespace+id 其中namespace为Mapper的全量名!
跨SqlSession的缓存,Mapper级别的缓存。在Mapper级别的缓存内,不同的SQLSession缓存可以共享。
原理: Mapper级别,Mapper以命名空间为单位创建缓存数据结构,缓存使用的数据结构是Map,其中key是 MapperId+Offset+Limit+SQL+所有的入参。MyBatis的二级缓存是CacheExecutor实现的。CacheExecutor是Executor的代理对象。当MyBatis接收到用户的查询请求时首先会根据Map的Key在CacheExecute缓存中查询数据是否存在,存在则从缓存中取,否则将执行数据库查询操作。
开启二级缓存需要进行相关配置:
- public interface Cache {
-
- /**
- * @return The identifier of this cache
- */
- String getId();
-
- /**
- * @param key Can be any object but usually it is a {@link CacheKey}
- * @param value The result of a select.
- */
- void putObject(Object key, Object value);
-
- /**
- * @param key The key
- * @return The object stored in the cache.
- */
- Object getObject(Object key);
-
- /**
- * Optional. It is not called by the core.
- *
- * @param key The key
- * @return The object that was removed
- */
- Object removeObject(Object key);
-
- /**
- * Clears this cache instance
- */
- void clear();
-
- /**
- * Optional. This method is not called by the core.
- *
- * @return The number of elements stored in the cache (not its capacity).
- */
- int getSize();
-
- /**
- * Optional. As of 3.2.6 this method is no longer called by the core.
- *
- * Any locking needed by the cache must be provided internally by the cache provider.
- *
- * @return A ReadWriteLock
- */
- ReadWriteLock getReadWriteLock();
-
- }
- public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
- if (closed) throw new ExecutorException("Executor was closed.");
- CacheKey cacheKey = new CacheKey();
- cacheKey.update(ms.getId());
- cacheKey.update(rowBounds.getOffset());
- cacheKey.update(rowBounds.getLimit());
- cacheKey.update(boundSql.getSql());
- List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
- TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
- for (int i = 0; i < parameterMappings.size(); i++) { // mimic DefaultParameterHandler logic
- ParameterMapping parameterMapping = parameterMappings.get(i);
- if (parameterMapping.getMode() != ParameterMode.OUT) {
- 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);
- }
- }
- return cacheKey;
- }
1、<select>标签所在的Mapper的Namespace+<select>标签的id属性
2、RowBounds的offset和limit属性,RowBounds是MyBatis用于处理分页的一个类,offset默认为0,limit默认为Integer.MAX_VALUE
3、<select>标签中定义的sql语句
即只要两次查询满足以上三个条件且没有定义flushCache="true",那么第二次查询会直接从MyBatis一级缓存PerpetualCache中返回数据,而不会走DB。
-
- /**
- * @author Clinton Begin
- */
- public final class MappedStatement {
-
- private String resource;
- private Configuration configuration;
- private String id;
- private Integer fetchSize;
- private Integer timeout;
- private StatementType statementType;
- private ResultSetType resultSetType;
- private SqlSource sqlSource;
- private Cache cache;
- private ParameterMap parameterMap;
- private List<ResultMap> resultMaps;
- private boolean flushCacheRequired;
- private boolean useCache;
- private boolean resultOrdered;
- private SqlCommandType sqlCommandType;
- private KeyGenerator keyGenerator;
- private String[] keyProperties;
- private String[] keyColumns;
- private boolean hasNestedResultMaps;
- private String databaseId;
- private Log statementLog;
- private LanguageDriver lang;
- private String[] resultSets;
-
- private MappedStatement() {
- // constructor disabled
- }
-
- public static class Builder {
- private MappedStatement mappedStatement = new MappedStatement();
-
- public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
- mappedStatement.configuration = configuration;
- mappedStatement.id = id;
- mappedStatement.sqlSource = sqlSource;
- mappedStatement.statementType = StatementType.PREPARED;
- mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>()).build();
- mappedStatement.resultMaps = new ArrayList<ResultMap>();
- mappedStatement.timeout = configuration.getDefaultStatementTimeout();
- mappedStatement.sqlCommandType = sqlCommandType;
- mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
- String logId = id;
- if (configuration.getLogPrefix() != null) logId = configuration.getLogPrefix() + id;
- mappedStatement.statementLog = LogFactory.getLog(logId);
- mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
- }
-
- public Builder resource(String resource) {
- mappedStatement.resource = resource;
- return this;
- }
-
- public String id() {
- return mappedStatement.id;
- }
-
- public Builder parameterMap(ParameterMap parameterMap) {
- mappedStatement.parameterMap = parameterMap;
- return this;
- }
-
- public Builder resultMaps(List<ResultMap> resultMaps) {
- mappedStatement.resultMaps = resultMaps;
- for (ResultMap resultMap : resultMaps) {
- mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
- }
- return this;
- }
-
- public Builder fetchSize(Integer fetchSize) {
- mappedStatement.fetchSize = fetchSize;
- return this;
- }
-
- public Builder timeout(Integer timeout) {
- mappedStatement.timeout = timeout;
- return this;
- }
-
- public Builder statementType(StatementType statementType) {
- mappedStatement.statementType = statementType;
- return this;
- }
-
- public Builder resultSetType(ResultSetType resultSetType) {
- mappedStatement.resultSetType = resultSetType;
- return this;
- }
-
- public Builder cache(Cache cache) {
- mappedStatement.cache = cache;
- return this;
- }
-
- public Builder flushCacheRequired(boolean flushCacheRequired) {
- mappedStatement.flushCacheRequired = flushCacheRequired;
- return this;
- }
-
- public Builder useCache(boolean useCache) {
- mappedStatement.useCache = useCache;
- return this;
- }
-
- public Builder resultOrdered(boolean resultOrdered) {
- mappedStatement.resultOrdered = resultOrdered;
- return this;
- }
-
- public Builder keyGenerator(KeyGenerator keyGenerator) {
- mappedStatement.keyGenerator = keyGenerator;
- return this;
- }
-
- public Builder keyProperty(String keyProperty) {
- mappedStatement.keyProperties = delimitedStringtoArray(keyProperty);
- return this;
- }
-
- public Builder keyColumn(String keyColumn) {
- mappedStatement.keyColumns = delimitedStringtoArray(keyColumn);
- return this;
- }
-
- public Builder databaseId(String databaseId) {
- mappedStatement.databaseId = databaseId;
- return this;
- }
-
- public Builder lang(LanguageDriver driver) {
- mappedStatement.lang = driver;
- return this;
- }
-
- public Builder resulSets(String resultSet) {
- mappedStatement.resultSets = delimitedStringtoArray(resultSet);
- return this;
- }
-
- public MappedStatement build() {
- assert mappedStatement.configuration != null;
- assert mappedStatement.id != null;
- assert mappedStatement.sqlSource != null;
- assert mappedStatement.lang != null;
- mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
- return mappedStatement;
- }
- }
-
-
- public boolean hasNestedResultMaps() {
- return hasNestedResultMaps;
- }
-
-
-
-
- public BoundSql getBoundSql(Object parameterObject) {
- BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
- List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
- if (parameterMappings == null || parameterMappings.size() <= 0) {
- boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
- }
-
- // check for nested result maps in parameter mappings (issue #30)
- for (ParameterMapping pm : boundSql.getParameterMappings()) {
- String rmId = pm.getResultMapId();
- if (rmId != null) {
- ResultMap rm = configuration.getResultMap(rmId);
- if (rm != null) {
- hasNestedResultMaps |= rm.hasNestedResultMaps();
- }
- }
- }
-
- return boundSql;
- }
-
- private static String[] delimitedStringtoArray(String in) {
- if (in == null || in.trim().length() == 0) {
- return null;
- } else {
- String[] answer = in.split(",");
- return answer;
- }
- }
-
- }
- public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
- executorType = executorType == null ? defaultExecutorType : executorType;
- executorType = executorType == null ? ExecutorType.SIMPLE : 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);
- }
- // 如果开启二级缓存会使用CachingExecutor对原Executor进行包装
- if (cacheEnabled) {
- executor = new CachingExecutor(executor);
- }
- // Mybatis插件功能对Executor的增强
- executor = (Executor) interceptorChain.pluginAll(executor);
- return executor;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。