Mybatis的缓存机制Cache

Mybatis提供对缓存的支持,分为一级缓存和二级缓存,在没有配置的情况下,系统默认会使用一级缓存。

一级缓存(SqlSession级别)

  我们都知道每个SqlSession对象之间的缓存是互不影响的,当同一个SqlSession执行多次相同的SQL语句时(主要针对select),系统只会到底层访问数据库一次,后续执行sql时,会从一级缓存里面读取第一次访问的数据。当这个SqlSession对象执行close(),或者显示声明清空缓存时,对应的一级缓存也会清空,从而提高效率。值得注意的是,如果SqlSession执行DML操作(即insert、update、delete),并提交到数据库时,也会起到清空该SqlSession对象对应的一级缓存的作用,目的是为了保证缓存里面的数据是最新的。,避免脏读现象。

二级缓存(SqlSessionFactory级别)

  有些书籍说二级缓存是Mapper级别,可能是依据二级缓存的配置是在xxxMapper.xml上配置的吧,不过本人更认同是SqlSessionFactory级别,为什么呢?因为同一个Configuration里面是创建一个SqlSessionFactory,SqlSessionFactory是属于线程安全的,SqlSessionFactory可以创建很多个SqlSession来进行事务操作,也就是说,SqlSessionFactory是由很多个SqlSession对象共享的,同样的,二级缓存的数据也是由很多个SqlSession共享的。如何才能让系统操作二级缓存呢?原理很简单,不同的SqlSession对象执行相同的namespace下的sql语句,当第一个SqlSession对象调用close()关闭一级缓存时,第一次查询得到的数据将会被保存到二级缓存,后面的SqlSession对象调用相同sql语句时,就会从二级缓存中获取数据。当然,使用二级缓存,需要对应的返回对象(即POJO)实现序列化。

配置:在需要使用的xxxMapper.xml里面配置<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true" />

eviction:代表回收策略,目前支持4种策略

1.LRU,最近最少使用的,移除最长时间不用的对象;

2.FIFO,先进先出;

3.SOFT,移除基于垃圾回收器状态和软引用规则的对象;

4.WEAK,更积极移除基于垃圾回收器状态和弱引用规则的对象。

flushInterval:代表刷新时长,单位毫秒

size:代表缓存最多可以存储多少个对象,注意,设置过大会导致内存溢出

readOnly:意味着缓存数据只能读取,不能修改

上面是较为详细配置,当然也可以简单一点,直接写上<cache />就可以了。

下面给出一个较为有意思的栗子:

xml配置代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC 
    "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learn.mapper.EmployeeMapper">
    <cache />
    
    <select id="getEmpList" resultType="employee">
         select * from tb_employee
    </select>

</mapper>

JUnit4测试代码如下:

@Test
    public void testCache(){
        SqlSession ss1 = null;
        SqlSession ss2 = null;
        try{
            ss1 = SqlSessionFactoryUtil.initSqlSessionFactory().openSession();
            ss2 = SqlSessionFactoryUtil.initSqlSessionFactory().openSession();
            EmployeeMapper em1 = ss1.getMapper(EmployeeMapper.class);
            EmployeeMapper em2 = ss2.getMapper(EmployeeMapper.class);
            List<Employee> list1 =  em1.getEmpList();
            //ss1.close();
            list1 = em2.getEmpList();
        }catch(Exception e){
            ss1.rollback();
            ss2.rollback();
            e.printStackTrace();
        }finally{
            if(ss1 != null){
                ss1.close();
            }
            if(ss2 != null){
                ss2.close();
            }
        }
    }

代码中实例化两个SqlSession对象,分别是ss1和ss2,用户检验数据读取的操作。留意上面注释了ss1.close();这一行。

下面是执行上面测试代码的日志结果:

Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [com.learn.mapper.EmployeeMapper]: 0.0
Opening JDBC Connection
Created connection 275310919.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]
==>  Preparing: select * from tb_employee 
==> Parameters: 
<==      Total: 6
Cache Hit Ratio [com.learn.mapper.EmployeeMapper]: 0.0
Opening JDBC Connection
Created connection 1948863195.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@74294adb]
==>  Preparing: select * from tb_employee 
==> Parameters: 
<==      Total: 6
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]
Returned connection 275310919 to pool.
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@74294adb]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@74294adb]
Returned connection 1948863195 to pool.

可以看到即使配置了<cache />二级缓存,第一个SqlSession对象ss1没有手动执行close()方法时,ss1对应的一级缓存数据仍然没有保存到二级缓存里面,即二级缓存里面没有数据,当第二个SqlSession对象ss2执行相同的sql语句时,会找一级缓存有没有数据,因为第一次执行,当然没有数据了,然后找二级缓存,刚才说过了,ss1的一级缓存数据并没有保存到二级缓存里面,所以二级缓存也没有数据,因此ss2就会再次操作数据库。可以看到log日志会有两条select语句。

此时,如果将close();的注释去掉,再执行一下测试代码,日志结果如下:

Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [com.learn.mapper.EmployeeMapper]: 0.0
Opening JDBC Connection
Created connection 275310919.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]
==>  Preparing: select * from tb_employee 
==> Parameters: 
<==      Total: 6
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]
Returned connection 275310919 to pool.
Cache Hit Ratio [com.learn.mapper.EmployeeMapper]: 0.5

显而易见,此时只访问一次数据库。

posted @ 2018-09-02 16:40  KamShing  阅读(498)  评论(0编辑  收藏  举报