mybatis succinctly and (f) - the secondary cache

Brief introduction

  • The last chapter we simply understand the secondary cache configuration. We analyzed in detail under the second-level cache today and why not recommend the use of L2 cache.

  • Level cache is aimed at sqlsession. Secondary cache for a namespace level.

Configuration

  • We have previously referred to the configuration and configure a custom secondary cache secondary cache. Let's start from scratch to achieve second-level cache.

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);
}
if (cacheEnabled) {
  executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
  • We can see from the above code, cacheEnabledthis property is a configuration of the secondary cache control. And this is the default property in the Configuration true. Here it explained mybatis caching feature is enabled by default. The difference between cache and cache of fact, in addition to the range, their difference is that a different order. Secondary cache is truly open configuration cache tags in xml mapper's on the line.

  • We configured here in StudentMapper.xml Then we get the same call twice sqlsession sql in the test class.

SqlSession sqlSession = SqlSessionFactoryUtils.openSqlsession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.getStudentByIdAndName("1", "1");
System.out.println(student);
SqlSession sqlSession1 = SqlSessionFactoryUtils.sqlSessionFactory.openSession();
StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
Student studentByIdAndName = mapper1.getStudentByIdAndName("1", "1");
System.out.println(studentByIdAndName);
  • But the results really surprised. In fact not only called once sql. But called twice. This is just the result of abnormalities. We use the Student accept this result. Let us look at the code level, from the

@Data
@Builder
@Accessors(chain = true)
public class Student {
    /**
     * 学生索引id
     */
    private String id;
    /**
     * 姓名
     */
    private String userName;

    /**
     * 用户昵称
     */
    private String userNick;

    /**
     * 年龄
     */
    private Integer age;
    /**
     * 性别 true : 男  ; false : 女
     */
    private SexEnum sex;
    /**
     * 生日
     */
    private Date birth;
    /**
     * 身高
     */
    private Double height;
}
  • Careful partner may be able to find. We did not realize this entity serialization. But as we have said before the secondary cache entities need to be serialized. Logically, it should being given. This shows that our secondary cache turned on, or precisely, it should be said that second-level cache does not play a role.
  • So we first entity serialization. Then start and found no effect. We take a look CacheingExecutor.commit()inside this method has things to submit tcm.commit().

  • This place is stored in the cache. We look at how mybatis resolve cache is configured in the label mapper.xml.


  • From the above code that we learned mybatis creates a cache object. Which is by a specific method to create the build. We take a look at what things are in the build method.

  • setStandardDecorators这个方法我们不知道做啥的。但是熟悉设计模式的都知道Decorator这个词是装饰者模式。这里这个方法也是用来装饰用的。看看mybatis为我们装饰了那些东西。

  • 首先在newBaseCacheInstance方法中创建原始对象PreprtualCache.然后是加载默认提供的回收机制用的Cache。这个实在build前设置的。
  • 然后就是通过setStandardDecorators进行装饰了。

  • 所以他的装饰链为:SynchronizedCache->LogginCache->SerializedCache->LruCache->PerPetualCache

  • 而在上面的tcm.commit就是在SerializedCache进行缓存对象的。所以我们之前的代码是sqlsession没有提交。所以代码只要稍微改动下。


SqlSession sqlSession = SqlSessionFactoryUtils.openSqlsession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.getStudentByIdAndName("1", "1");
System.out.println(student);
sqlSession.commit();
SqlSession sqlSession1 = SqlSessionFactoryUtils.sqlSessionFactory.openSession();
StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
Student studentByIdAndName = mapper1.getStudentByIdAndName("1", "1");
System.out.println(studentByIdAndName);

  • SynchronizedCache : 同步Cache.这个类就是保证线程安全。所以他的方法基本上是加上synchronized来保证线程安全的。

  • LoggingCache : 日志。在上面我们有个日志是Cache Hit Ratio 0.5 表示二级缓存的命中率。

  • SerializedCache : 就是用来序列化数据的。

  • LruCache : 回收cache的算法

  • PerPetualCache :基本Cache .

源码

CachingExecutor


@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    //获取Cache对象
    Cache cache = ms.getCache();
    if (cache != null) {
      //根据statment配置刷新缓存,默认是insert、update、delete会刷新缓存
      flushCacheIfRequired(ms);
      //二级缓存开启入口。
      if (ms.isUseCache() && resultHandler == null) {
        //这个方法主要用来处理存储过程。后续章节说明
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        //通过缓存事物查询数据
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          //调用委托类查询数据
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //加入缓存,供下次获取
          tcm.putObject(cache, key, list);
        }
        return list;
      }
    }
    //没有开启二级缓存则继续往下走
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

缺点

  • 二级缓存因为更加广泛,所以容易造成脏数据。尤其是在关联查询的时候有序无法控制刷新力度。很容易出现脏读。

自定义二级缓存

  • 在之前我们了解到的PerpetualCache是缓存链上最基本的缓存类。我们自定义的缓存就是替代这个类的。在mybatis中会现根据我们注册进来的类进行实例化。如果没有则用默认的PerpetualCache这个类作为基础缓存类。

Guess you like

Origin www.cnblogs.com/zhangxinhua/p/12120202.html