Learn more about MyBatis second-level cache and mybatis

Source: Internet
Author: User

Learn more about MyBatis second-level cache and mybatis
Learn more about MyBatis second-level Cache 1. Complete Cache creation process

We start with parsing the mybatis-config.xml configuration file from SqlSessionFactoryBuilder:

Reader reader = Resources.getResourceAsReader("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

Then:

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());

See the parser. parse () method:

parseConfiguration(parser.evalNode("/configuration"));

View the location where the Mapper. xml file is processed:

mapperElement(root.evalNode("mappers"));

View the XMLMapperBuilder that processes er. xml:

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration,                     resource, configuration.getSqlFragments());mapperParser.parse();

Continue to read the parse method:

configurationElement(parser.evalNode("/mapper"));

Here:

String namespace = context.getStringAttribute("namespace");if (namespace.equals("")) {     throw new BuilderException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);cacheRefElement(context.evalNode("cache-ref"));cacheElement(context.evalNode("cache"));

SeenamespaceIs in xml<mapper>The attribute of the element. Then, the following are processed successively.cache-refAndcache, FollowedcacheWill overwrite the previouscache-refBut ifcache-refReference not foundcacheWill not be overwritten until the final processing is completed.cacheInsteadcache-refOverwrite. Is it a bit dizzy and messy? Therefore, do not configure the two at the same time. In fact, few will do the same.

Let's see how MyBatis works.<cache/>:

private void cacheElement(XNode context) throws Exception {    if (context != null) {        String type = context.getStringAttribute("type", "PERPETUAL");        Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);        String eviction = context.getStringAttribute("eviction", "LRU");        Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);        Long flushInterval = context.getLongAttribute("flushInterval");        Integer size = context.getIntAttribute("size");        boolean readWrite = !context.getBooleanAttribute("readOnly", false);        boolean blocking = context.getBooleanAttribute("blocking", false);        Properties props = context.getChildrenAsProperties();        builderAssistant.useNewCache(typeClass, evictionClass,                         flushInterval, size, readWrite, blocking, props);    }}

From the source code, we can see that MyBatis reads those attributes and can easily reach the default values of these attributes.

The method for creating a cache object for Java isbuilderAssistant.useNewCacheLet's take a look at this Code:

public Cache useNewCache(Class<? extends Cache> typeClass,                         Class<? extends Cache> evictionClass,                         Long flushInterval,                         Integer size,                         boolean readWrite,                         boolean blocking,                         Properties props) {    typeClass = valueOrDefault(typeClass, PerpetualCache.class);    evictionClass = valueOrDefault(evictionClass, LruCache.class);    Cache cache = new CacheBuilder(currentNamespace)            .implementation(typeClass)            .addDecorator(evictionClass)            .clearInterval(flushInterval)            .size(size)            .readWrite(readWrite)            .blocking(blocking)            .properties(props)            .build();    configuration.addCache(cache);    currentCache = cache;    return cache;}

From the point where this method is called, we can see that the returned value cache is not used, and the MappedStatement is used in the subsequent process.currentCache.

Ii. Use the Cache Process

In the systemCachingExecutorMedium:

@Overridepublic <E> List<E> query(        MappedStatement ms, Object parameterObject,         RowBounds rowBounds, ResultHandler resultHandler,         CacheKey key, BoundSql boundSql) throws SQLException {  Cache cache = ms.getCache();

After obtaining the cache, first determine whether there is a secondary cache.
Only pass<cache/>,<cache-ref/>Or@CacheNamespace,@CacheNamespaceRefThe er. xml or Mapper interface marked to use the cache (the same namespace cannot be used at the same time) will have a second-level cache.

  if (cache != null) {

If the cache exists (<insert>,<select>,<update>,<delete>OfflushCacheAttribute to determine whether to clear the cache.

    flushCacheIfRequired(ms);

Then, according to the xml configuration attributesuseCacheTo determine whether to use the cache (resultHandler generally uses the default value, rarely null ).

    if (ms.isUseCache() && resultHandler == null) {

Make sure that the method does not have an Out parameter. mybatis does not support the cache of stored procedures. Therefore, if it is a stored procedure, an error is reported here.

      ensureNoOutParams(ms, parameterObject, boundSql);

If no problem exists, it will take the value from the cache based on the key:

      @SuppressWarnings("unchecked")      List<E> list = (List<E>) tcm.getObject(cache, key);

If no cache is available, the query is executed and the query results are cached.

      if (list == null) {        list = delegate.<E>query(ms, parameterObject,                         rowBounds, resultHandler, key, boundSql);        tcm.putObject(cache, key, list); // issue #578 and #116      }

Returned results

      return list;    }  }

If no cache is available, the query is executed directly.

  return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

In the above Codetcm.putObject(cache, key, list);The result is cached. However, MyBatis is saved to a Map (default cache configuration) in serialized form until the sqlsession is closed.


Iii. Considerations for Using Cache 1. You can only use Cache on a table with only one table operation

Not only do you want to ensure that this table only has single table operations in the system, but all the operations related to this table must be in onenamespace.

2. Use the cache when the query is far greater than the insert, update, and delete operations.

We should be clear about this.Remember, this must be done on the premise of 1!

4. Avoid using second-level cache

There may be many people who do not understand this. The benefits of level-2 cache are far inferior to those hidden dangers.

Why avoid using second-level cache?

There is no harm in complying with the [Considerations for Cache Usage.

In other cases, there will be a lot of harm.

Some operations on a table are not independent of the table. namespace.

For exampleUserMapper.xmlMost of them targetuserTable operations. HoweverXXXMapper.xmlForuserSingle Table operations.

This causesuserThe data in the two namespaces is inconsistent. IfUserMapper.xmlInXXXMapper.xmlThe cache is still valid.userThe cache results for single-table queries may be incorrect.

The more dangerous situation is thatXXXMapper.xmlWhen insert, update, and delete operations are performedUserMapper.xmlThe various operations in are full of unknown and risks.

Operations on such a single table may be uncommon. But you may think of a common situation.

Multi-Table operations cannot use the cache

Why not?

First, no matter what the multi-table operation writesnamespaceA table is not in thisnamespace.

For example, two tables:roleAnduser_roleIf I want to query all roles of a userroleMulti-Table operations will be involved.

<select id="selectUserRoles" resultType="UserRoleVO">    select * from user_role a,role b where a.roleid = b.roleid and a.userid = #{userid}</select>

Like the above query, you will writeXml??

Whether it is writtenRoleMapper.xmlOrUserRoleMapper.xmlOr an independentXxxMapper.xml. If the second-level cache is used, the above query results may be incorrect.

If you modify the role of this user, the result is wrong when the above query uses the cache.

This should be easy to understand.

In my opinion, there is no solution in the current cache method of MyBatis. Multi-Table operations cannot be cached at all.

If you want them to use the samenamespace(Pass<cache-ref>To avoid dirty data, the meaning of the cache is lost.

We can see that, in fact,Level 2 Cache not available. It is useless to introduce so much in the entire article.


5. How to save the second-level cache?

The second-level cache cannot be used more efficiently.

However, multi-Table operations are avoided.Dirty dataThere is still a solution. The solution is to use the Interceptor to determine which tables are involved in the executed SQL statements (which can be parsed using jsqlparser), and then automatically clear the cache of the relevant tables. However, this method is very inefficient for caching.

Designing such a plug-in is quite complicated. Since I didn't want to implement it, I won't talk nonsense.

Finally, we recommend that you discard the second-level cache and use controllable cache at the business layer to replace it better.

If you have a better solution, leave a message.~~~

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.