MyBatis(六)MyBatis的缓存

MyBatis的一级缓存

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问

我们在Mapper里面设置方法如下所示:

 /**
     * 根据员工id查询员工信息
     *
     * @return
     */
    Emp getEmpById(@Param("empId") Integer empId);

我们设置映射文件如下所示:

<select id="getEmpById" resultType="Emp">
        select * from t_emp where emp_id=#{empId}
    </select>

 我们设置测试类如下所示:

@Test
    public void  testGetEmpById(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
        Emp emp1= mapper.getEmpById(1);
        System.out.println(emp1);
        Emp emp2= mapper.getEmpById(1);
        System.out.println(emp2);
    }

我们运行之后如下所示:

 我们发现查询了两次,但是sql只运行了一次,第一次是从咱们的数据库里面查的,第二次是从缓存里面查的。

我们将我们的测试类修改如下所示:

 /**
     * Mybatis的一级缓存:
     * MyBatis的一级缓存是SqlSession级别的,即通过同一个SqlSession查询的数据会被缓存
     * 再次使用同一个SqlSession查询同一条数据,会从缓存中获取
     */
    @Test
    public void  testGetEmpById(){
        SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp1= mapper1.getEmpById(1);
        System.out.println(emp1);
        Emp emp2= mapper1.getEmpById(1);
        System.out.println(emp2);
        SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();
        CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
        Emp emp3 = mapper2.getEmpById(1);
        System.out.println(emp3);
    }

我们运行之后如下所示:

我们发现第一次查询是在sql里面查的,第二次是在缓存里面查的,后面又执行了一次sql,这次我们重新创建了一个sqlSession,所以上面里面已经没有他的缓存信息,我们需要重新执行sql进行查找。

我们通过同一个sqlsession所查询出来的数据,会被缓存,当我们再次通过同一个sqlsession去查询相同的数据的时候,会直接从缓存里面进行查找,并不会从我们的数据库里面重新去查了。

使一级缓存失效的四种情况:

(1)不同的SqlSession对应不同的一级缓存

(2)同一个SqlSession但是查询条件不同

(3)同一个SqlSession两次查询期间执行了任何一次增删改操作

我们设计方法进行验证一下:

 /**
     * 添加员工信息
     * @param emp
     */
    void insertEmp(Emp emp);

我们设计的映射文件如下所示:

 <insert id="insertEmp" >
        insert into t_emp values(null,#{empName},#{age},#{gender},null)
    </insert>

 我们设计的测试类如下所示:

@Test
    public void  testGetEmpById(){
        SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp1= mapper1.getEmpById(1);
        System.out.println(emp1);

        mapper1.insertEmp(new Emp(null,"小虎",25,"男"));

        Emp emp2= mapper1.getEmpById(1);
        System.out.println(emp2);

    }

 我们运行之后如下所示:

我们发现再次查找1的时候sql语句再次执行,再次从数据库里面进行查找 

(4)同一个SqlSession两次查询期间手动清空了缓存

我们设置的测试类如下所示:

@Test
    public void  testGetEmpById(){
        SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp1= mapper1.getEmpById(1);
        System.out.println(emp1);
//清空sqlsession级别的缓存
       sqlSession1.clearCache();

        Emp emp2= mapper1.getEmpById(1);
        System.out.println(emp2);

    }

我们运行之后如下所示:

MyBatis的二级缓存:

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取

二级缓存开启的条件:

<a>在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置

<b>在映射文件中设置标签<cache/> 


<mapper namespace="com.rgf.mybatis.mapper.CacheMapper">
    <cache/>

<c>二级缓存必须在SqlSession关闭或提交之后有效

我们设置的测试类如下所示:

 @Test
    public void  testCache() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp1 = mapper1.getEmpById(1);
        System.out.println(emp1);
        SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
        CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
        Emp emp2 = mapper2.getEmpById(1);
        System.out.println(emp2);
    }

我们运行之后如下所示:

我们发现查询同一条数据,sql语句仍然运行了两次。 

我们将SqlSession关闭之后重新运行,关闭之后,才会将存放在一级数据里面的东西存放到二级缓存里面,如下所示:

 @Test
    public void  testCache() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp1 = mapper1.getEmpById(1);
        System.out.println(emp1);
        sqlSession1.close();
        SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
        CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
        Emp emp2 = mapper2.getEmpById(1);
        System.out.println(emp2);
        sqlSession2.close();
    }

运行之后如下所示:

错误提示我们Emp没有序列化的异常 

我们进行下一条件。

<d>查询的数据所转换的实体类类型必须实现序列化的接口

我们在Emp实体类里面找到如下所示:
实现序列化接口。

public class Emp implements Serializable {
    private  Integer empId;
    private  String empName;
    private  Integer age;
    private  String gender;

我们重新运行之后如下所示:

我们发现此时的sql语句即执行了一个 。

我们发现该日志里面存在一个一级缓存里面没有的:

Cache Hit Ratio

这是缓存命中率,这个与我们当前查询的总数量有关系。

使二级缓存失效的一种情况:

同一个SqlSessionFactory两次查询期间执行了任何一次增删改操作

二级缓存的相关配置:

在mapper配置文件中添加的cache标签可以设置一些属性:(这些都有默认值,一般不需要配置)

(1)evication属性:缓存回收策略,默认的是LRU。

LRU:最近最少使用的:移除最长时间不被使用的对象

FIFO:先进先出:按对象进入缓存的顺序来移除他们

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

WEAK:弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象

(2)fiushInterval属性:刷新间隔,单位毫秒

默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

使二级缓存刷新可以通过两次查找过程中实现增删改。

(3)size属性:引用数目,正整数

代表缓存最多可以存储多少个对象,太大容易导致内存溢出。

(4)readOnly属性:只读,true/false

true:只读缓存:会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改,这提供了很重要的性能优势

false:读写缓存;会返回缓存对象的拷贝(通过序列化),修改了拷贝后,对象并没有被修改。这会慢一些,但是安全,默认是false.

MyBatis缓存查询的顺序:

先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用

如果二级缓存没有命中,再查询一级缓存

如果一级缓存也没有命中,则查询数据库

Sq;Session关闭之后,一级缓存中的数据会写入二级缓存 

 整合第三方缓存EHCache(针对二级缓存)

1、添加依赖:

 <!--Mybatis EHCache整合包-->
    <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-ehcache</artifactId>
      <version>1.2.1</version>
    </dependency>
    <!--slf4j日志门面的一个具体实现-->
    <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
      <scope>test</scope>
    </dependency>

2、各jar包功能

jar包名称 作用
mybatis-ehcache Mybatis和EHCcache的整合包
ehcache EHCache核心包
slf4-api SLF4J日志门面包
logback-classic 支持SLF4J门面接口的一个具体实现

猜你喜欢

转载自blog.csdn.net/weixin_59448049/article/details/127533425