MyBatis缓存-一级缓存、二级缓存及第三方缓存

186 阅读2分钟

一级缓存

 /*

        MyBatis实现类 PerpetualCache 根据map进行存储的  hashcode+sqlid+sql+hashcode+envenments...
    * 一级缓存
    * 1、默认开启
    * 2、作用于基于sqlSession
    * 失效情况:
    * 1、不同的sqlSession会使一级缓存失效
    * 2、setting localCacheScope SESSION
    *                    STAEMENT 相当于关闭一级缓存
    * 3、同一个查询  查询条件不一样
    * 4、同样的查询 中间执行了增删改查
    * 5、手动清除缓存  sqlSession.clearCache();
    *
    * */
    @Test
    public void Test01(){
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept22 dept22 = new Dept22();
        dept22.setId(1);

        List<Dept22> depts0 =mapper.selectDept(dept22);
        System.out.println(depts0);

        EmpMapper mapper1 = sqlSession.getMapper(EmpMapper.class);
//        Emp emp = new Emp();
//        emp.setUsername("好好");
//        emp.setCreate_date(LocalDate.now());
//        emp.setDept_id(1);
//        mapper1.insertEmp(emp);

        List<Dept22> depts = mapper.selectDept(dept22);
        System.out.println(depts);

        sqlSession.commit();
    }

二级缓存

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kdy.mapper.DeptMapper">
    <cache-ref namespace="com.kdy.mapper.EmpMapper"/>
    <cache></cache>
    <select id="selectDept" resultType="com.kdy.pojo.Dept22" useCache="false" >
        select *  from dept
        <where>
            <if test="id!=null and id!=''">
                and id = #{id}
            </if>
            <if test="dept_name!=null and dept_name!=''">
                and dept_name= #{dept_name}
            </if>
        </where>
    </select>



    <insert id="insertDept" flushCache="false">
        insert into dept(dept_name) values(#{dept_name})
    </insert>

</mapper>


/**
 * 二级缓存
 *  1、默认也是开启 没有实现
 *  2、作用域 基于全局范围 应用级别
 *  不通的SQLSESSION 共享缓存
 *  3、缓存默认实现也是PerpetualCache 但是二级缓存 根据不同的mapper命名空间多饱了一层
 *
 * 实现
 *
 * 1、开启二级缓存 mybatis config 文件中 <setting name="cacheEnabled" value="true"></setting>  默认就是开启的,增加可读性
 * 2、mapper映射文件配置 在需要用到二级缓存的映射文件中加入 <cache></cache>   基于Mapper映射文件实现缓存的  基于Mapper映射文件的命名空间来进行存储的
 * (  Configuation 文件中 protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");  是存储二级缓存的
 *  key:mapper namespace    value: PerpetualCache里面的Mapper
 * )
 * 3、在需要使用到的二级缓存javaBean中实现序列化接口   implements Serializable
 *      命名空间
 *
 * 4、sqlSession.commit执行后  缓存生效
 *
 *
 *  - Cache Hit Ratio [com.kdy.mapper.DeptMapper]: xxx 缓存命中率  1/(1-xxx)   =  查询总次数
 *
 *
 * 缓存顺序:
 * 一级缓存在查询完就行存储
 * 二级缓存是在事务提交或关闭的时候存储
 *
 *先在二级缓存中查,没有查到再去一级缓存
 * PerpetualCache中 id  命名空间是二级缓存  LocalCache是一级缓存
 *
 *
 * 二级缓存失效:
 *  1、 同一个命名空间执行了 增删改  并提交
 *     当Mapper中  增删改标签 添加 flushCache="false"   缓存不会清空  但是要慎重脏读问题
 *     除非保证查询数据不会增删改
 *
 *  2、 让查询的数据不存储到缓存中
 *  select  useCache="false"
 *
 *  3、执行增删改 清除关联缓存
 *  <cache-ref namespace="com.kdy.mapper.DeptMapper"/>
 *
 *
 *
 * **/

    @Test
    public void cache02() throws IOException {
        String mybatisconfig =  "mybatis.config.xml";
        InputStream inputStream = Resources.getResourceAsStream(mybatisconfig);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
        SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
        DeptMapper mapper = sqlSession1.getMapper(DeptMapper.class);
        DeptMapper mapper1 = sqlSession2.getMapper(DeptMapper.class);
        Dept22 dept22 = new Dept22();
        dept22.setId(1);
       List<Dept22> dept2222 =mapper.selectDept(dept22);
        sqlSession1.commit();
        System.out.println(dept2222);
        EmpMapper mapper2 = sqlSession1.getMapper(EmpMapper.class);
//        Emp emp = new Emp();
//        emp.setUsername("kongjkong");
//        emp.setCreate_date(LocalDate.of(2022,2,2));
//        emp.setDept_id(1);
//        mapper2.insertEmp(emp);


        List<Dept22> dept333 =mapper.selectDept(dept22);
        sqlSession1.commit();
        System.out.println(dept333);
//        sqlSession1.commit();
//        EmpMapper mapper2 = sqlSession1.getMapper(EmpMapper.class);
//        Emp emp = new Emp();
//        emp.setUsername("孔德阳");
//        mapper2.insertEmp(emp);
//        mapper1.insertDept("nihao");
//        sqlSession2.commit();
//        dept2222 =mapper.selectDept(dept22);

//        dept2222 = mapper1.selectDept(dept22);
//        sqlSession2.commit();

//        System.out.println(1111);
    }

cache标签配置


<!--
    eviction 当缓存达到size 设定的数量时   以那种方式进行 回收缓存
LRU – 最近最少使用:移除最长时间不被使用的对象。( 默认 )
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。


    flushInterval  刷新时间 单位 毫秒


    size   可以缓存的数量   map key的数量

    readOnly  true 缓存的数据性能更高   数据是指向地址的   sqlsession 提交后  改变值会造成脏读
               true后 是引用值

               false  默认  读取 缓存的数据性能更低   复制数据  sqlsession 提交后  改变值不会造成脏读
               false后 是复制值
      type=""          org.apache.ibatis.cache.impl.PerpetualCache

     指定第三方缓存  实现 Cache接口


-->
    <cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="false"

    ></cache>
//readOnly true 脏读演示
@Test
public void cache03() throws IOException {

        String mybatisconfig =  "mybatis.config.xml";
        InputStream inputStream = Resources.getResourceAsStream(mybatisconfig);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
        SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
        DeptMapper mapper = sqlSession1.getMapper(DeptMapper.class);
        DeptMapper mapper1 = sqlSession2.getMapper(DeptMapper.class);
        Dept22 dept22 = new Dept22();

        dept22.setId(1);
        List<Dept22> dept22s  =  mapper1.selectDept(dept22);
        System.out.println(dept22s);

        sqlSession2.commit();
    dept22s.get(0).setDept_name("新经理");



        List<Dept22> dept223  =  mapper1.selectDept(dept22);
        System.out.println(dept223);

}

第三方缓存

为什么使用第三方缓存

Mybastis JVM内存空间中,缓存过多会造成内存溢出。

常用的缓存中间件

Memcached Redis 缓存中间件 ehcache 不需要配置 相对简单

MyBatis 与 Redis 简单整合使用

redis支持 string hash list hashset

更简单实现复杂的业务

还可以实现 分布式下面的分布式锁 分布式session MQ使用等

1、下载win版本redis codeload.github.com/cinience/Re…

① 设置密码 redis.windows.conf requirepass 123456

② 运行 redis-server.exe redis.windows.conf

③ 启动 redis-cli.exe 启动客户端 输入 auth 123456

set str helloword

get str

2、安装mybatis redis 依赖

github地址 :github.com/mybatis/red…

文档地址 : mybatis.org/redis-cache…

① 添加依赖

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-redis -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-redis</artifactId>
    <version>1.0.0-beta2</version>
</dependency>

② Mapper xml中加入cache type

<cache
        eviction="FIFO"
        flushInterval="60000"
        size="512"
        readOnly="false"
        type="org.mybatis.caches.redis.RedisCache"
></cache>

③ 加入redis 配置文件

host=localhost
port=6379
connectionTimeout=5000
soTimeout=5000
password=
database=0

注: 可以在mybatis redis 仓库中找示例找到源码 并把redis.去掉

3、验证

下载 github.com/cinience/Re… 的客户端

image.png