• 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

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

  • 相关阅读:
    数据库事务的特性以及隔离级别
    非受检异常(运行时异常)和受检异常的区别等
    在测试crontab执行脚本的时候,修改了linux的系统时间,crontab不执行了。
    perl的foreach循环的坑
    javascript中快速求数组的全部元素的相加之和
    js 百度地图
    PHP 取前一天或后一天、一个月时间
    php截取字符串函数
    js jquery 基本元素操作
    PHP 替换标签和标签内的内容
  • 原文地址:https://www.cnblogs.com/SysoCjs/p/9574318.html
Copyright © 2020-2023  润新知