MyBatis 缓存机制

1. 概述

1.1 缓存可以极大的提升查询效率,MyBatis 系统中默认定义了两级缓存:一级缓存和二级缓存
  • 默认情况下,只有一级缓存(SqlSession 级别的缓存,也称为本地缓存)开启;
  • 二级缓存(基于namespace 级别的缓存, 也称为全局缓存)需要手动开启和配置;
  • 为了提高扩展性,MyBatis 定义了缓存接口 Cache, 可以通过实现Cache 接口来自定义二级缓存;

2. 一级缓存

  • 一级缓存: 与数据库同一次会话期间,查询到的数据会放在本地缓存中;以后,如果需要获取相同的数据,直接从缓存中获取;
  • 一级缓存就是SqlSession级别的一个Map;
  • 一级缓存失效的情况(即获取相同的数据,需要发送两次sql语句)
    • SqlSession 不同时,一级缓存失效;
    • SqlSession 相同,查询条件不同;
    • SqlSession 相同,但是两次查询之间,执行了增删改操作;
    • SqlSession 相同,手动清除了一级缓存;(clearCache() 方法)
// EmployeeMapper.java
public class EmployeeMapper{

    public Employee getEmpById(Integer id);
}

// EmployeeMapper.xml
<resultMap type="cn.itcast.mybatis.bean.Employee" id="MyEmp">
    <id column="id" property="id"/>
    <result column="last_name" property="lastName"/>
</resultMap>

<select id="getEmpById" resultMap="MyEmp">
    select * from tbl_employee where id=#{id}
</select>

// 测试类
public class MyTest{

    @Test
    public void test01 throw IOException{
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        SqlSession openSession = sqlSessionFactory.openSession();

        try{
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);

            // 一级缓存
            Employee emp01 = mapper.getEmpById(1);
            System.out.println(emp01);

            Employee emp02 = mapper.getEmpById(1);
            System.out.println(emp02);
            System.out.println(emp01==emp02);

        }finally{
            openSession.close();
        }
    }
}

3. 二级缓存

  1. 基于namespace级别的缓存,一个namespace对应一个二级缓存;
  2. 查询出的数据,都会默认放在一级缓存中,只有会话提交或关闭之后,一级缓存中的数据才会转移到二级缓存;
  3. 工作机制:
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果会话关闭,一级缓存中的数据会被保存到二级缓存中; 新的会话查询信息,就可以参照二级缓存中的内容;
    • 例如,使用EmployeeMapper对象,查询到的Employee对象, 和使用DepartmentMapper对象,查询到
      的Department对象分别存放在不同的map中;即不同namespace查询出的数据,会放在自己对应的缓存(map);
  4. 使用步骤:
    • mybatis-config.xml中开启: <setting name="cacheEnabled" value="true"/>;
    • xxxMapper.xml 中配置使用二级缓存:<cache></cache>,常用属性:
      • eviction: 缓存的回收策略;(LRU, FIFO, SOFT, WEAK)
      • flushInterval: 缓存刷新间隔; 即缓存多长时间清空一次,默认不清空;
      • readOnly: 缓存是否只读;
      • size: 缓存存放多少元素;
      • type: 指定自定义缓存的全类名;
    • POJO 需要实现序列化接口;
  5. 和缓存相关的设置/属性:
    • cacheEnabled=false: 关闭的是二级缓存,一级缓存仍可用;
    • select 标签中的 useCache=false: 关闭的也是二级缓存,一级缓存依然可以使用;
    • 每个增删改标签都存在 flushCache=true,默认为true; 表示增删改执行完成后,就会清空一级缓存和二级缓存;
    • 查询标签的fluseCache的属性默认为 false;
    • sqlSession.clearCache(): 只清空当前session中的一级缓存;
// 测试类
public class MyTest{

    @Test
    public void test01 throw IOException{
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        SqlSession openSession = sqlSessionFactory.openSession();
        SqlSession openSession2 = sqlSessionFactory.openSession();

        try{
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);

            // 两次会话,查询内容相同; 二级缓存开启的情况下,只会发送一条SQL语句
            Employee emp01 = mapper.getEmpById(1);
            System.out.println(emp01);
            openSession.close();

            Employee emp02 = mapper2.getEmpById(1);
            System.out.println(emp02);
            openSession2.close();

        }finally{

        }
    }
}

4. 整合第三方缓存(ehcache)

  1. 导入jar包: ehcache-core-2.6.8, slf4j-api, slf4j-log4j, mybatis-ehcache;
  2. 配置 EmployeeMapper.xml
  3. 在类路径下新建ehcache.xml文件
// EmployeeMapper.xml
<mapper namespace="cn.itcast.mybatis.dao.EmployeeMapper">

<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

<resultMap type="cn.itcast.mybatis.bean.Employee" id="MyEmp">
    <id column="id" property="id"/>
    <result column="last_name" property="lastName"/>
</resultMap>

<select id="getEmpById" resultMap="MyEmp">
    select * from tbl_employee where id=#{id}
</select>
</mapper>

// DepartmentMapper.xml
<mapper namespace="cn.itcast.mybatis.dao.DepartmentMapper">

<!-- 引用缓存: namespace,指定和哪个名称空间下的缓存一样 -->
<cache-ref namespace="cn.itcast.mybatis.dao.EmployeeMapper"/>

<resultMap type="cn.itcast.mybatis.bean.Employee" id="MyEmp">
    <id column="id" property="id"/>
    <result column="last_name" property="lastName"/>
</resultMap>

<select id="getEmpById" resultMap="MyEmp">
    select * from tbl_employee where id=#{id}
</select>
</mapper>

参考资料

原文地址:https://www.cnblogs.com/linkworld/p/7795658.html