程序员

Mybatis (四) 缓存机制

2017-12-10  本文已影响0人  其实我很菜啊

许多运用程序为了提高性能而增加缓存,特别是从数据库中获取的数据,如果缓存中没有,就到表中去查询,表查询的数据添加到缓存中去,下次查询是直接从缓存中读取,Mybatis 包含一个非常强大的查询缓存特性,它可以非常方便的配置和定制,缓存可以极大的提升查询效率。就如同第一次访问某网站时耗时会就一下感觉到卡,关闭网页再访问相同网页时就流畅了很多,所以缓存机制是很重要的。

一、mybatis 的一级缓存

在默认情况下,mybatis 的一级缓存是默认开启的,类似与hiberante,所谓一级缓存也就是基于一个 sqlsession 的查询语句, 即 session 级别的缓存,非全局缓存或者称为非二级缓存。
一级缓存(local cache),即本地缓存,作用域默认为 sqlsession ,当 Session close 后,该 session 中所有 Cache 将被清空,本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存。

  1. 不同 SqlSession 对应不同的一级缓存
  2. 同一个 SqlSession 但是查询条件不同
  3. 同一个 SqlSession 两次查询期间执行了任何一次增删改的操作
  4. 同一个 SqlSession 两次查询手动清空了缓存

UserMapping.xml

<resultMap type="cn.softjx.modle.User" id="UserMap">
        <result column="t_id" property="id"/>
        <result column="t_username" property="username"/>
        <result column="t_password" property="password"/>
        <result column="t_sid" property="sid"/>
    </resultMap>

   <select id="selectAll2" resultMap="UserMap" >
        select t_id,t_username,t_password from t_user;
    </select>

映射文件不做任何处理,mybatis默认一级缓存
Test.java

@Test
    public void testSelect(){
        try {                   
            List<User> user=mapper.selectAll2();
            for(User users:user){
                System.out.println(users.getPassword()+"  "+users.getUsername()+"   "+users.getSid());
            }
            List<User> user1=mapper.selectAll2();
            for(User users:user1){
                System.out.println(users.getPassword()+"  "+users.getUsername()+"   "+users.getSid());
            }                       
        } finally{
            // TODO Auto-generated catch block
            session.close();
        }
    }

声明了两个不同的 user 对象,但都是在同一个 Session 内执行查询的,代码虽然执行了两次数据库的查询,但是实际上只是在数据库查询了一次并将其缓存在同一 Session 中,再使用同一个 Session 查询同一字段时直接读取一级缓存中的数据内容。

一级缓存.png

总共打印了18条数据,从 log4j 的打印结果可以看出从数据库中查询出9条数据,并且只是发送了一次 SQL 语句。
注意:
1. 用同一 Session 但查询条件不同缓存失效
2. 用同一 Session 在第二次查询前执行了任何造成数据库改动的操作(CRDU)
再使用同一句查询语句缓存失效

二、Mybatis 二级缓存

如果要实现 mybatis 的二级缓存,二级缓存需要手动开启和配置,它是基于 namespace 级别的缓存,全局作用域缓存,一般来说有两种方式 :

  1. 采用 mybatis 内置的 cache 机制
  2. mybatis 定义了缓存接口 Cache,我们可以通过实现 Cache 接口来定义二级缓存,可采用第三方 Cache 框架。

在表映射文件中加入 <cache /> 语句,并且相应的 model 要实现 java 的 Serializable 接口,因为二级缓存就是序列化和反序列化的过程,<cache /> 表示:

  1. 所有在映射文件里的 select 语句都将被缓存
  2. 所有在映射文件里的 insert,update 和 delete 语句会被清空缓存。
  3. 缓存不会被设定的时间所清空。
  4. 每个缓存可以存储 1024 个列表或对象的引用
  5. 缓存将作为 “读/写” 缓存,意味着获取对象不是共享的且对调用者是安全的,不会有其他的调用者或线程潜在修改。

使用步骤 :

  1. 全局配置文件中开启二级缓存,默认是打开的
    <setting name="cacheEnabled" value="ture" />
  2. 需要使用二级缓存的映射文件处理使用 cache 配置缓存
  3. POJO需要实现 Serializable 接口
  4. 如果会话关闭:一级缓存的数据会保存到二级缓存中,新的会话查询信息就可以参照二级缓存中的内容,二级缓存会在 SqlSession 关闭或提交后才会生效。

bean.java 实现序列化

public class User implements Serializable{
    private Integer id;
    private String username;
    private String password;
    private Integer sid;
    
    private School school;
    
    
    public User() {
                
    }
}

config.xml

<settings>
    
    <setting name="cacheEnabled" value="true"></setting>
    
</settings> 

Mapping.xml 配置 <cache /> 标签

<mapper namespace="cn.softjx.dao.inter.UserMapping">
    
<cache />                  
    <resultMap type="cn.softjx.modle.User" id="UserMap">
        <result column="t_id" property="id"/>
        <result column="t_username" property="username"/>
        <result column="t_password" property="password"/>
        <result column="t_sid" property="sid"/>
       ......

在 Test.java 中声明三个不同的 session 执行相同的查询

                User useBean=new User();
                useBean.setId(4);
                List<User> user=mapper.selectAll3(useBean);
                for(User users:user){
                    System.out.println(users.getPassword()+"  "+users.getUsername()+"   "+users.getSid());
                }
                                            
                
                session.close();
                
                UserMapping mapper1=session1.getMapper(UserMapping.class);
                
                List<User> user1=mapper1.selectAll3(useBean);
                for(User users:user1){
                    System.out.println(users.getPassword()+"  "+users.getUsername()+"   "+users.getSid());
                }
                                                
                session1.close();
            
                UserMapping mapper2=session2.getMapper(UserMapping.class);              
                List<User> user2=mapper2.selectAll3(useBean);
                for(User users:user2){
                    System.out.println(users.getPassword()+"  "+users.getUsername()+"   "+users.getSid());
                }                                                               
                session2.close();
结果.png image.png

虽然使用了不同的 Session 但只发送了一条 SQL。即不同的sqlSession可以共享一个 Mapping
日志中有一个 Hit Ratio 命中率,它是如何计算的呢。第一次查询由于缓存中是没有数据的所以是 0.0,第二次查询从缓存中取 1-(1/2)=0.5,第三次查询
1-(1/3)=0.666666

cache 标签中还有几个属性 eviction , flushInterval ,readOnly

  1. LRU :最近不使用的缓存数据,移出最长时间不被使用的数据
  2. FIFO :先进先出
  3. SOFT : 软引用,移除基于垃圾回收器状态和软引用规则对象
  4. WEAK :软引用,更积极的垃圾回收器状态和软引用规则对象
    ,默认是 LRU

上篇:Mybatis (三)动态sql:http://www.jianshu.com/p/71c0cdd9c249

上一篇 下一篇

猜你喜欢

热点阅读