ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

(七)Mybatis-缓存

2022-05-02 23:31:06  阅读:117  来源: 互联网

标签:mapper 缓存 查询 println 二级缓存 Mybatis out


(七)Mybatis-缓存

一、简介

问题:查询=》连接数据库=》消耗资源!

解决方案:

  • 一次查询的结果,给他暂存在一个可以直接取到的地方=》内存:缓存。
  • 我们再次查询相同数据的时候,直接走缓存,就不去数据库查了。
  1. 什么是缓存【cache】
    • 存在内存中的临时数据。
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据文件)查询,而是从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据。
    • 比如菜单数据

二、Mybatis缓存

  • mybatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存,缓存可以极大地提升查询效率
  • mybatis系统中默认定义了2级缓存:一级缓存和二级缓存。
    • 默认情况下啊,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。namespace又和一个接口绑定,一个接口mapper对应一个表或一个业务,所以是一张表级别的缓存,整个mapper文件里的所有方法共享。
    • 为了提供扩展性,mybatis定义了缓存接口cache,我们可以通过cache接口来定义二级缓存。

三、一级缓存

在一次sqlSession会话中有效,如下从开启到关闭sqlSession

 @Test
    public void testGetBlogList() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            List<Blog> blogList = mapper.getBlogList();
            for (Blog blog : blogList) {
                System.out.println(blog);
            }
        }
    }

一级缓存默认开启,我们只需要测试即可

3.1 测试步骤

1 开启日志

在核心配置文件mybatis-config.xml

<settings>
<!--    <setting name="logImpl" value="STDOUT_LOGGING"/>-->
    <setting name="logImpl" value="LOG4J"/>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

2 编写测试实体pojo类

package com.happy.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
   private int id;
   private String name;
   private String pwd;
}

3 编写mapper接口

package com.happy.dao;

import com.happy.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserMapper {
    @Select("select * from user")
    List<User> getUserList();

    //规范最好给参数取名字
    User getUserById(@Param("id") int id);
}

4 编写mapper.xml文件

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

<mapper namespace="com.happy.dao.UserMapper">

    <select id="getUserById" parameterType="int" resultType="user">
        select *
        from user
        where id = #{id}
    </select>

</mapper>

5 测试使用

测试步骤
  • 开启日志
  • 测试在一个session内查询两次相同记录
  • 查看日志输出
package com.happy.dao;

import com.happy.pojo.User;
import com.happy.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserMapperTest {

    @Test
    public void testGetUserList() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }


    @Test
    public void testGetUserById() {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            System.out.println("============第1次查询=============");
            User user1 = mapper.getUserById(3);
            System.out.println(user1);

            System.out.println("============第2次查询=============");
            User user2 = mapper.getUserById(3);
            System.out.println(user2);
        }
    }
}

缓存失效情况
  1. 查询不同的东西

  2. 增删该操作DML操作,可能会改变原来的数据,所以必定会刷新缓存。

    即在两次查询之间,加入修改操作后(即使是修改其他行数据),缓存也会刷新。

  3. 查询不同的mapper.xml

  4. 手动清楚缓存。

6 小结:

  • 一级缓存默认是开启的,只在一次sqlSession中有效
  • 也就是拿到连接和关闭连接这个区间段。
  • sqlSession的一级缓存就是一个map,下次来会去查这个map。

四、二级缓存

4.1 二级缓存产生的原因

默认情况下,只启用了本地的会话缓存即一级缓存,它仅仅对一个会话中的数据进行缓存

二级缓存产生的原因:

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。
  • 当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被再次保存到二级缓存中。
  • 新的会话查询信息,就可以从二级缓存中获取内容;
  • 不同的mapper查出的数据会放在自己对应的缓存(map)中。

4.2 二级缓存介绍

要启用全局的二级缓存,只需要在你的sql映射文件中添加一行:

<cache/>

二级缓存只作用于cache标签所在的映射文件中的语句,如果你混合使用Java API和XML映射文件,在共用接口中的语句将不会被默认缓存、你需要使用@CacheNamespaceRef注解指定缓存作用域。

这些属性可以通过cache元素的属性来修改,比如:

<cache
       eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>

上面这个更高级的配置策略:

  • 创建了一个FIFO缓存,

  • 每隔60秒刷新,

  • 最多可以存储结果对象或列表512个应用

  • 而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同的线程中调用者产生冲突。

可用的清除策略有:

  • LRU- 最近最少使用:移除最长时间不被使用的对象。
  • FIFO-先进先出:按对象进入缓存的顺序来移除他们。
  • SOFT-软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK-弱引用:更积极地基于垃圾收集器和弱引用规则移除对象。

4.3 开启步骤

1 在核心配置文件显示开启全局二级缓存

虽然默认为true

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

2 在mapper文件里开启缓存

在要使用二级缓存的mapper.xml文件里开启,也可以自定义一些参数

<cache
       eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>

3 测试使用

@Test
    public void testGetUserByIdByCache2() {
        SqlSession sqlSession1=null;
        SqlSession sqlSession2=null;
        try {
            sqlSession1 = MybatisUtils.getSqlSession();
            sqlSession2 = MybatisUtils.getSqlSession();
            UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
            UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
            System.out.println("============第1次查询=============");
            User user1 = mapper1.getUserById(1);
            System.out.println(user1);

            System.out.println("============第2次查询=============");
            User user2 = mapper1.getUserById(1);
            System.out.println(user2);

//            注意这里在其他sqlsession使用前必须关闭,如果不关闭sqlsession,不会把对象引用放到二级缓存共享给其他sqlsession
            sqlSession1.commit();
//            sqlSession1.close();
            System.out.println("============第3次查询,使用sqlsession2=============");
            User user3 = mapper2.getUserById(1);
            System.out.println(user3);

            System.out.println("============判断两个对象是否相同=============");
            System.out.println(user1 == user3);
        }
        catch (Exception e){

        }finally {
            sqlSession1.close();
            sqlSession2.close();
        }
    }

4 小结事项:

注意:

readOnly="true"/>
  • 如果readOnly设置为true,则不需要让user类实现Serializable接口,否则需要。
  • 如果readOnly设置为true,则两次从缓存中取出对象为一个,否则不是一个。
  • 注意这里在其他sqlsession使用前必须关闭或者提交,如果不关闭sqlsession,不会把对象引用放到二级缓存共享给其他sqlsession。

五、自定义缓存(二级缓存)

实现以下接口

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.ibatis.cache;

import java.util.concurrent.locks.ReadWriteLock;

public interface Cache {
    String getId();

    void putObject(Object var1, Object var2);

    Object getObject(Object var1);

    Object removeObject(Object var1);

    void clear();

    int getSize();

    default ReadWriteLock getReadWriteLock() {
        return null;
    }
}

mybatis已经实现的缓存策略如下:默认使用LRU,清除最不常使用。

六、ehcache(第三方缓存)

  • ehcache是一个纯java的进程内缓存框架,具有快速、精干等特点,是hibernate中默认的cache provider。
  • 是一种广泛使用的开源java分布式缓存,主要面向通用缓存。

要在程序中使用ehcache

6.1 使用步骤

1 引入依赖

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>

2 集成第三方缓存

除了上述自定义缓存方式,可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。

<!--    开启二级缓存,在这个mapper文件中有效-->
    <cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"
            type="org.mybatis.caches.ehcache.EhcacheCache"
        />

在mapper中指定使用我们的ehcache缓存实现。

3 将ehcache的配置文件ehcache.xml放在resources下

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <!-- 配置缓存文件的路劲
        java.io.tmpdir,表示临时文件夹,windows表示在C:\Documents and Settings\Administrator\Local Setting\Temp
     -->
    <diskStore path="../temp/ehcache"/>

    <!-- 设定缓存的默认数据过期策略 -->
    <!--
        name:缓存名称
        maxElementsInMemory:缓存最大个数
        eternal:对象是否永久有效,一但设置了,timeout将不起作用
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒),仅当eternal=false对象不是永久有效时使用
        timeToLiveSeconds:设置对象在失效前允许存活时间,最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用
        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB
        maxElementsOnDisk:硬盘最大缓存个数
        diskPersistent: 是否缓存虚拟机重启期数据
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU。
            最近使用(LRU)"策略,其它还有先入先出FIFO,最少使用LFU,较少使用LRU
        clearOnFlush:内存数量最大时是否清除
    -->
    <defaultCache
            maxElementsInMemory="1000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"/>

    <cache name="userCache"
           maxElementsInMemory="1000"
           eternal="false"
           overflowToDisk="true"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LRU"/>

</ehcache>

4 测试使用

和mybatis自带的二级缓存没有区别,只是多了一些缓存持久化存放到本地。

七、Mybatis的一级缓存和二级缓存执行顺序

在这里插入图片描述

1、先判断二级缓存是否开启,如果没开启,再判断一级缓存是否开启,如果没开启,直接查数据库

2、如果一级缓存关闭,即使二级缓存开启也没有数据,因为二级缓存的数据从一级缓存获取

3、一般不会关闭一级缓存

4、二级缓存默认不开启

5、如果二级缓存关闭,直接判断一级缓存是否有数据,如果没有就查数据库

6、如果二级缓存开启,先判断二级缓存有没有数据,如果有就直接返回;如果没有,就查询一级缓存,如果有就返回,没有就查询数据库

综上:先查二级缓存,再查一级缓存,再查数据库;即使在一个sqlSession中,也会先查二级缓存;一个namespace中的查询更是如此;缓存执行顺序是:二级缓存–>一级缓存–>数据库

在这里插入图片描述

八、其他缓存

工作中一般使用redis作为数据库缓存,而不使用自定义缓存。

标签:mapper,缓存,查询,println,二级缓存,Mybatis,out
来源: https://www.cnblogs.com/happycarpediem/p/16217342.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有