0三、mybatis一级缓存和二级缓存

一级缓存:

Mybatis对缓存提供支持,可是在没有配置的默认状况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。因此在参数和SQL彻底同样的状况下,咱们使用同一个SqlSession对象调用一个Mapper方法,每每只执行一次SQL,由于使用SelSession第一次查询后,MyBatis会将其放在缓存中,之后再查询的时候,若是没有声明须要刷新,而且缓存没有超时的状况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。html

看下流程图:java

为何要使用一级缓存,不用多说也知道个大概。可是还有几个问题咱们要注意一下。
一、一级缓存的生命周期有多长?
a、MyBatis在开启一个数据库会话时,会 建立一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
b、若是SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。
c、若是SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,可是该对象仍可以使用。
d、SqlSession中执行了任何一个update操做(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,可是该对象能够继续使用spring

下面演示下mybatis-spring整合,触发一级缓存sql打印的变化。sql

@Override
public User doSomeBusinessStuff(String userId) throws Exception {
    userMapper.getUser("3");
    userMapper.getUser("3");
    return null;
}

 

为何会查询两次,难道没有缓存有问题?上面说了,一级缓存是SqlSession级别的,2次执行getUser方法,都建立了一个新的SqlSession,实际上是userMapper的这个接口被spring代理了,里面有个很关键的代码。数据库

当执行完getUser方法后,会调用Commit(Commit会清空整个SqlSession的一级缓存),和close方法,致使第一次和第二次执行getUser获得的SqlSession不是同一个,因此执行了2次sql查询。若是避免commit和close,就要开启事务(不晓得怎么开启事务请查看这边文章:开启事务缓存

@Transactional
@Override
public User doSomeBusinessStuff(String userId) throws Exception {
    userMapper.getUser("3");
    userMapper.getUser("3");
    return null;
}

打印sqlmybatis

此次只建立了一次SqlSession,且只打印了一次sql。app

二级缓存

先看下二级缓存的工做机制,ide

二级缓存默认是不开启的,若是须要开启二级缓存。开启二级缓存有2种方式,这里介绍使用注解的方式测试

@CacheNamespace(blocking = true)
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{userId}")
    User getUser(@Param("userId") String userId);
}

在你的Mapper上加上@CacheNamespace(blocking = true)注解就能够了。看下使用二级缓存后打赢出来的日志。

@Override
public User doSomeBusinessStuff(String userId) throws Exception {
    userMapper.getUser("3");
    userMapper.getUser("3");
    return null;
}

主要未避免一级缓存,先把事务关了。

能够发现也是打印了一次sql,可是建立了两次SqlSession,说明第二次是从二级缓存里面取得。二级缓存的select语句将会被缓存,insuret,update,deleted语句会刷新缓存。

针对二级缓存,其余博主说须要实现Serializable接口接口,但在测试中发现能够不用实现也会触发二级缓存。

二级缓存有个坑:那就是当你在2个mapper里面都引用了同一张表,就好比,mapper1里面有个User表查询操做,mapper2有个user表更新操做,当再次在mapper1里面在执行查询操做,发现拿到的还更新前的数据。这就是缓存Key生成原则问题,缓存key是经过mapper进行划分的,相同的mapper里面全部方法,使用的是同一个缓存区域,因此不一样的mapper里面操做同一张表就会出现上面那种问题。在现实中,线上的一个应用最少2个实例,这个时候,这个问题就暴露出来了,因此缓存最好仍是使用第三方缓存插件。

参考:

博客:http://www.javashuo.com/article/p-ubjqsvaw-dg.html