mybatis---缓存

1、缓存介绍

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

2、一级缓存

特性

1、默认开启,也可以手动去关闭 localCacheScope = STATEMENT。
2、作用域是基于 sqlSession(默认),一次数据库操作会话。
3、缓存默认实现类 PerpetualCache ,使用map进行存储缓存,其中:key ==> hashcode + sqlId + sql + hashcode + environments的默认id。
4、查询完就进行存储。
5、先去二级缓存拿,再去一级缓存拿。

失效情况

1、不同的 sqlSession 会使sql缓存失效。创建两个sqlSession看日志即可。
2、同一个 sqlSession ,但是sql语句不一样。调用两个不同的sql看日志即可。
3、同一个 sqlSession ,中间执行了增删改,缓存也会失效,即使不是与当前sql有关的。
4、同一个 sqlSession ,中间手动清除了缓存,缓存会失效。如:sqlSession.clearCache();

/**
 * 一级缓存
 * 特性:
 *      1、默认开启,也可以手动去关闭  localCacheScope = STATEMENT
 *      2、作用域是基于  sqlSession(默认),一次数据库操作会话
 *      3、缓存默认实现类 PerpetualCache ,使用map进行存储缓存
 *          key ==> hashcode + sqlId + sql + hashcode + environments的默认id
 *      4、查询完就进行存储
 *      5、先去二级缓存拿,再去一级缓存拿
 * 失效情况:
 *      1、不同的 sqlSession 会使sql缓存失效
 *      2、同一个 sqlSession ,但是sql语句不一样
 *      3、同一个 sqlSession ,中间执行了增删改,缓存也会失效
 *      4、同一个 sqlSession ,中间手动清除了缓存,缓存会失效
 */
@Test
public void select01() {
    DeptMapper mapper = session.getMapper(DeptMapper.class);
    Dept dept = new Dept();
    dept.setDeptId(1);
    List<Dept> deptList = mapper.selectDept(dept);
    System.out.println(deptList);

    // 清除缓存
    session.clearCache();

    List<Dept> deptList2 = mapper.selectDept(dept);
    System.out.println(deptList2);
}

3、二级缓存

3.1、二级缓存初步使用

特性

1、默认开启了,但是没有实现,如果我们需要用,需要我们自己去实现。
2、作用域:基于全局范围,应用级别的。
3、缓存默认实现类 PerpetualCache ,使用map进行存储,但是二级缓存根据不同的mapper.xml的命名空间多包了一层
org.apache.ibatis.session.Configuration#caches 里面的key = mapper.xml的命名空间 value=PerpetualCache.map
key ==> hashcode + sqlId + sql + hashcode + environments的默认id
4、事务提交的时候进行存储(sqlSession关闭时)。
5、先去二级缓存拿,再去一级缓存拿。

实现步骤

1、开启二级缓存,只是为了醒目,二级缓存已经默认开启 。

<settings>
    <!-- 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

2、因为二级缓存是针对于mapper.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="cool.ale.mapper.DeptMapper">
    <cache></cache>

3、如果存储的是JavaBean,需要实现序列化接口 implements Serializable
如果配置成功了,会在日志中打印出命中率 ==> 从缓存中取出的次数/查询总次数。

public class Dept implements Serializable {
}

测试类

package cool.ale.tests;

import cool.ale.mapper.DeptMapper;
import cool.ale.mapper.EmpMapper;
import cool.ale.pojo.Dept;
import cool.ale.pojo.DeptEmpDTO;
import cool.ale.pojo.EmpDeptDTO;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;


public class MybatisTest {

    private SqlSessionFactory sqlSessionFactory = null;

    @Before
    public void before() throws IOException {
        // 从 xml中构建 SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    }


    /**
     * 二级缓存
     * 特性:
     *      1、默认开启了,但是没有实现,如果我们需要用,需要我们自己去实现
     *      2、作用域:基于全局范围,应用级别的
     *      3、缓存默认实现类 PerpetualCache ,使用map进行存储,但是二级缓存根据不同的mapper.xml的命名空间多包了一层
     *          org.apache.ibatis.session.Configuration#caches 里面的key = mapper.xml的命名空间 value=PerpetualCache.map
     *          key ==> hashcode + sqlId + sql + hashcode + environments的默认id
     *      4、事务提交的时候进行存储(sqlSession关闭时)
     *      5、先去二级缓存拿,再去一级缓存拿
     * 实现:
     *      1、开启二级缓存,只是为了醒目,二级缓存已经默认开启   <setting name="cacheEnabled" value="true"/>
     *      2、因为二级缓存是针对于mapper.xml文件的,所以在需要使用到二级缓存的文件中加入 <cache></cache>
     *      3、如果存储的是JavaBean,需要实现序列化接口 implements Serializable
     *          如果配置成功了,会在日志中打印出命中率 ==> 从缓存中取出的次数/查询总次数
     * 失效:
     *      1、同一个命名空间进行了增删改操作,会导致二级缓存失效
     *          如果不想失效,可以在增删改上设置一个 flushCache="false"
     *          flushCache  是否执行完本条sql就要清除缓存,默认为true
     *      2、让查询不缓存数据到二级缓存中 useCache="false"
     *          useCache是否使用缓存,默认为true
     *      3、如果希望当一个命名空间下执行了增删改操作,同时清空另一个命名空间的缓存
     *          可以设置    <cache-ref namespace="cool.ale.mapper.DeptMapper"/>
     *          但是目前只能设置一个,弊端。
     */
    @Test
    public void select02() {
        try (SqlSession session = sqlSessionFactory.openSession(true)) {
            DeptMapper mapper = session.getMapper(DeptMapper.class);
            Dept dept = new Dept();
            dept.setDeptId(1);
            List<Dept> deptList = mapper.selectDept(dept);
            System.out.println(deptList);
        }

        try (SqlSession session = sqlSessionFactory.openSession(true)) {
            DeptMapper mapper = session.getMapper(DeptMapper.class);
            Dept dept = new Dept();
            dept.setDeptId(1);
            List<Dept> deptList = mapper.selectDept(dept);
            System.out.println(deptList);
        }
    }

    @Test
    public void select03() {
        SqlSession session = sqlSessionFactory.openSession();
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        Dept dept = new Dept();
        dept.setDeptId(1);
        List<Dept> deptList = mapper.selectDept(dept);
        System.out.println(deptList);

        session.commit();

        SqlSession session2 = sqlSessionFactory.openSession(true);
        DeptMapper mapper2 = session.getMapper(DeptMapper.class);
        Dept dept2 = new Dept();
        dept2.setDeptId(1);
        List<Dept> deptList2 = mapper2.selectDept(dept2);
        System.out.println(deptList2);
    }

}

在这里插入图片描述

失效情况

1、同一个命名空间进行了增删改操作,会导致二级缓存失效。
如果不想失效,可以在增删改上设置一个 flushCache=“false”
flushCache 是否执行完本条sql就要清除缓存,默认为true

<!-- flushCache属性当设置为false时,就算执行了这条insert语句,也不会清空mybatis二级缓存,默认为true -->
<insert id="insertDept" flushCache="false"></insert>

2、让查询不缓存数据到二级缓存中 useCache=“false”
useCache是否使用缓存,默认为true
这个里面也有 flushCache 属性,默认值为false

<!--    useCache是否使用缓存,默认为true
    	flushCache  是否执行完本条sql就要清除缓存,默认为false
 -->
<select id="selectDept" resultMap="BaseMap" useCache="false" flushCache="false">
    select * from dept
        <where>
            <if test="deptId != null and deptId != ''">
                and dept_id = #{deptId}
            </if>
            <if test="deptName != null and deptName != ''">
                and dept_name = #{deptName}
            </if>
        </where>
</select>

3、如果希望当一个命名空间下执行了增删改操作,同时清空另一个命名空间的缓存
可以设置
但是目前只能设置一个,弊端。

<?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="cool.ale.mapper.EmpMapper">
      <cache></cache>
      <!-- 如果这个命名空间执行了增删改操作,将会同步清空 DeptMapper 命名空间下的缓存 -->
      <cache-ref namespace="cool.ale.mapper.DeptMapper"/>

3.2、cache的参数

<cache
  type="org.apache.ibatis.cache.impl.PerpetualCache"
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

type:默认的缓存实现类,可以改变指定第三方的缓存实现类。
eviction:
    LRU :– 最近最少使用:移除最长时间不被使用的对象。
    FIFO :– 先进先出:按对象进入缓存的顺序来移除它们。
    SOFT :– 软引用:基于垃圾回收器状态和软引用规则移除对象
    WEAK :– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval:(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size:(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly:(只读)属性可以被设置为 true 或 false。当设置为true时,mybatis会默认这个变量不会被修改,就会将地址直接给需要引用此变量的地方,所以获取的更快,但也会引起脏数据。false是会copy一份。

4、整合第三方缓存

4.1、整合Windows版本的Redis缓存

4.1.1、 在github上下载一个windows版本的redis:windows版本的redis地址

进来之后点击note进入之后下载一个解压版本的。

在这里插入图片描述

解压版图示:

在这里插入图片描述

4.1.2、开始搭建

1、在mapper.xml文件中添加引用redis的类

<?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="cool.ale.mapper.DeptMapper">
	<!-- 调用redis缓存 -->
    <cache type="org.mybatis.caches.redis.RedisCache" />

    <resultMap id="BaseMap" type="cool.ale.pojo.Dept">
        <result column="DEPT_ID" property="deptId"></result>
        <result column="DEPT_NAME" property="deptName"></result>
    </resultMap>

    <select id="selectDept" resultMap="BaseMap">
        select * from dept
            <where>
                <if test="deptId != null and deptId != ''">
                    and dept_id = #{deptId}
                </if>
                <if test="deptName != null and deptName != ''">
                    and dept_name = #{deptName}
                </if>
            </where>
    </select>

</mapper>

2、在resouce文件夹创建redis.properties文件

redis.host=localhost
redis.port=6379
redis.connectionTimeout=5000
redis.soTimeout=5000
redis.password=
redis.database=0
redis.clientName=

3、写出测试类

package cool.ale.tests;

import cool.ale.mapper.DeptMapper;
import cool.ale.mapper.EmpMapper;
import cool.ale.pojo.Dept;
import cool.ale.pojo.DeptEmpDTO;
import cool.ale.pojo.EmpDeptDTO;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;


public class MybatisTest {

    private SqlSessionFactory sqlSessionFactory = null;

    @Before
    public void before() throws IOException {
        // 从 xml中构建 SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    }

    @Test
    public void select02() {
        try (SqlSession session = sqlSessionFactory.openSession(true)) {
            DeptMapper mapper = session.getMapper(DeptMapper.class);
            Dept dept = new Dept();
            dept.setDeptId(1);
            List<Dept> deptList = mapper.selectDept(dept);
            System.out.println(deptList);
        }

        try (SqlSession session = sqlSessionFactory.openSession(true)) {
            DeptMapper mapper = session.getMapper(DeptMapper.class);
            Dept dept = new Dept();
            dept.setDeptId(1);
            List<Dept> deptList = mapper.selectDept(dept);
            System.out.println(deptList);
        }
    }

    @Test
    public void select03() {
        SqlSession session = sqlSessionFactory.openSession();
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        Dept dept = new Dept();
        dept.setDeptId(1);
        List<Dept> deptList = mapper.selectDept(dept);
        System.out.println(deptList);

        session.commit();

        SqlSession session2 = sqlSessionFactory.openSession(true);
        DeptMapper mapper2 = session2.getMapper(DeptMapper.class);
        Dept dept2 = new Dept();
        dept2.setDeptId(1);
        List<Dept> deptList2 = mapper2.selectDept(dept2);
        System.out.println(deptList2);
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值