转载和引用,请注明原文出处! Fork me on GitHub
结局很美妙的事,开头并非如此!

Mybatis系列(四):Mybatis缓存

一、MyBatis缓存介绍

  MyBatis 提供了一级缓存二级缓存的支持

       1. 一级缓存: 默认开启,基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush  close 之后,该Session中的所有Cache就将清空。在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则SqlSession的缓存清空。

             注意:

                       每次查询会先去缓存中找,如果找不到,再去数据库查询,然后把结果写到缓存中。Mybatis的内部缓存使用一个HashMap,keyhashcode+statementId+sql语句。Value为查询出来的结果集映射成的java对象。

                        SqlSession执行insertupdatedelete等操作commit后会清空该SQLSession缓存。

  2. 二级缓存:需要手动开启,与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。在同一个namespace下的mapper文件中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则二级缓存清空。

              注意:

                       二级缓存是mapper级别的。Mybatis默认是没有开启二级缓存。

                       第一次调用mapper下的SQL去查询用户信息。查询到的信息会存到该mapper对应的二级缓存区域内。

                       第二次调用相同namespace下的mapper映射文件中相同的SQL去查询用户信息。会去对应的二级缓存内取结果。

                       如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该namespace下的二级缓存。

  3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D(增删改) 操作后,默认该作用域下所有 select 中的缓存将被clear。

二、开启二级缓存示例

1、  在核心配置文件SqlMapConfig.xml中加入以下内容(开启二级缓存总开关)

1 <settings>
2   <setting name="cacheEnabled" value="true"/>
3 </settings>

2、在映射文件中,加入以下内容,开启二级缓存

 

 1 <mapper namespace="me.gacl.mapping.userMapper">
 2 <!-- 开启二级缓存 -->
 3 <cache/>

字面上看就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句将会被缓存。
  • 映射语句文件中的所有 insert,update delete 语句会刷新缓存。
  • 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)1024 个引用。
  • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。 

   所有的这些属性都可以通过缓存元素的属性来修改。比如:

1 <cache
2   eviction="FIFO"
3   flushInterval="60000"
4   size="512"
5   readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,并每隔60秒刷新,存数结果对象或列表的512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。

可用的收回策略有:

  • LRU – 最近最少使用的:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

  默认的是 LRU

flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024

readOnly(只读)属性可以被设置为truefalse。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。 

3、实现序列化

由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象(实体类)执行序列化。

如果该类存在父类,那么父类也要实现序列化。

4、禁用二级缓存和刷新二级缓存

禁用二级缓存:该statement中设置userCache=false可以禁用当前select语句的二级缓存,即每次查询都是去数据库中查询默认情况下是true,即该statement使用二级缓存。

1 <select id="findUserByName" parameterType="java.lang.String" resultType="com.study.mybatis.model.UserModel" userCache="false" >
2     select * from t_user where username LIKE '%${value}%'
3 </select>

刷新二级缓存:

1 <select id="findUserByName" parameterType="java.lang.String" resultType="com.study.mybatis.model.UserModel" flushCache="true" >
2     select * from t_user where username LIKE '%${value}%'
3 </select>

 

三、使用自定义缓存

 

除了这些自定义缓存的方式,你也可以通过实现你自己的缓存或为其他第三方缓存方案 创建适配器来完全覆盖缓存行为。

 

1 <cache type="com.domain.something.MyCustomCache"/>

这个示 例展示了如何使用 一个 自定义的缓存实 现。type 属性指定的类必须实现org.mybatis.cache.Cache 接口。这个接口是MyBatis 框架中很多复杂的接口之一,但是简单给定它做什么就行。 

 1 public interface Cache {
 2   String getId();
 3   int getSize();
 4   void putObject(Object key, Object value);
 5   Object getObject(Object key);
 6   boolean hasKey(Object key);
 7   Object removeObject(Object key);
 8   void clear();
 9   ReadWriteLock getReadWriteLock();
10 }

缓存配置和缓存实例是绑定在 SQL 映射文件的命名空间的。因此,所有在相同命名空间的语句正如绑定的缓存一样。 语句可以修改和缓存交互的方式,或在语句的基础上使用两种简单的属性来完全排除它们。默认情况下,语句可以这样来配置

1 <select ... flushCache="false" useCache="true"/>
2 <insert ... flushCache="true"/>
3 <update ... flushCache="true"/>
4 <delete ... flushCache="true"/>

因为那些是默认的,你明显不能明确地以这种方式来配置一条语句。相反,如果你想改变默认的行为,只能设置flushCacheuseCache属性。比如,在一些情况下你也许想排除从缓存中查询特定语句结果,或者你也许想要一个查询语句来刷新缓存。相似地,你也许有一些更新语句依靠执行而不需要刷新缓存。

posted @ 2017-10-17 23:44  小不点啊  阅读(573)  评论(0编辑  收藏  举报