MyBatis_缓存_二级

ehcache-cache: https://github.com/mybatis/ehcache-cache 


总结:

  1. 开启二级缓存需要在MyBatis配置文件里面(我的mybatis-config.xml)配置   <setting name="cacheEnabled" value="true"/> ,默认是ture,为了防止版本变更我们还是需要配置这个。
  2. 如果是 <setting name="cacheEnabled" value="false"/> ,这个时候关闭的只是二级缓存,一级缓存依然还在。
  3. 每个select标签都有 useCache="true",而增删改的默认为 useCache="false"。
  4. 每个增删改标签的:flushCache="true":(一级二级都会清除),这就是为什么两个一样的查询中间有一个增删改操作后,二级缓存找不到命中的(被清除),一级缓存失效了。查询标签:flushCache="false", 如果 flushCache="true",每次查询之后都会清空缓存,缓存是没有被使用的。
  5. sqlSession.clearCache() 只是清楚当前session的一级缓存。
  6. 我们的POJO需要实现序列化接口。

# 建库
CREATE DATABASE if not exists mybatis;
use mybatis;
# 员工表
drop table if exists tbl_employee;
CREATE TABLE tbl_employee(
  id INT(11) PRIMARY KEY AUTO_INCREMENT,
  last_name VARCHAR(255), #姓名
  gender CHAR(1), # 性别
  email VARCHAR(255) # 邮箱
);

INSERT INTO tbl_employee(last_name,gender,email) VALUES ("tom","0","tom@atguigu");
SELECT * FROM tbl_employee;


# 部门表
drop table if exists tbl_dept;
CREATE TABLE tbl_dept(
  id INT(11) PRIMARY KEY AUTO_INCREMENT, # 部门id
  dept_name VARCHAR(255)	# 部门名字
);
INSERT INTO tbl_dept(dept_name) VALUES ("开发部") ,("测试部");

SELECT * FROM tbl_employee;
SELECT * FROM tbl_dept;


原理

 


 benn 对象:

package com.atguigu.mybatis.bean;
import java.io.Serializable;
public class Employee implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	public Employee(Integer id, String lastName, String email, String gender) {
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
	}
	// 省略 get set toString 方法
}
package com.atguigu.mybatis.bean;
import java.io.Serializable;
import java.util.List;
public class Department implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer id;
	private String departmentName;
    //省略 get set toString 方法
}

EmployeeMapper  接口:

package com.atguigu.mybatis.dao;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapper {
    Employee getEmpById(Integer id);

    Long addEmp(Employee employee);
}
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> 

eviction:缓存的回收策略:(默认的是 LRU

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

flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值

readOnly:是否只读:

  • true:只读:mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快。
  • false:非只读:mybatis觉得获取的数据可能会被修改。mybatis会利用序列化&反序列(benn 需要实现序列化接口)的技术克隆一份新的数据给你。安全,速度慢。

size:缓存存放多少元素;

type="":指定自定义缓存的全类名,实现Cache接口即可。

EmployeeMapper.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="com.atguigu.mybatis.dao.EmployeeMapper">

    <!--二级缓存-->
    <!--<cache eviction="LRU" />-->
    <!--使用外部jar包-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache" />

    <!--useCache="false" 关闭的是二级缓存-->
    <select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee" useCache="true">
        select *
        from tbl_employee
        where id = #{id}
    </select>

    <!--flushCache="true" 增删改默认是true  执行增删改过后就会清空缓冲-->
    <insert id="addEmp" parameterType="com.atguigu.mybatis.bean.Employee"
            useGeneratedKeys="true" keyProperty="id"
            flushCache="true">
        insert into tbl_employee (last_name, email, gender)
        values (#{lastName}, #{email}, #{gender})
    </insert>


</mapper>

 EhcacheCache 的使用还需要在根路径下有一个 ehcache.xml文件

ehcache.xml :

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="F:\Test\ehcache"/>

    <defaultCache
            maxElementsInMemory="10000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

属性说明:

  • diskStore:指定数据在磁盘中的存储位置。
  • defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略

以下属性是必须的:

  • maxElementsInMemory - 在内存中缓存的element的最大数目
  • maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
  • eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
  • overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上

以下属性是可选的:

  • timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
  • timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
  • diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
  • diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
  • diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
  • memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)

 DepartmentMapper 接口:

package com.atguigu.mybatis.dao;
import com.atguigu.mybatis.bean.Department;
public interface DepartmentMapper {
	
	Department getDeptById(Integer id);
}

为了不用在配置一次缓存,我们可以引用其他写好的缓存配置

 DepartmentMapper.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="com.atguigu.mybatis.dao.DepartmentMapper">
    <!-- 引用缓存:namespace:指定和哪个名称空间下的缓存一样 -->
    <cache-ref namespace="com.atguigu.mybatis.dao.EmployeeMapper"/>
    <!--public Department getDeptById(Integer id);  -->
    <select id="getDeptById" resultType="com.atguigu.mybatis.bean.Department">
        select id, dept_name departmentName
        from tbl_dept
        where id = #{id}
    </select>
</mapper>

/* 二级缓存:(全局缓存):基于namespace级别的缓存:一个namespace对应一个二级缓存:
*         工作机制:
*         1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
*         2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
*         3、sqlSession===EmployeeMapper==>Employee
*                         DepartmentMapper===>Department
*             不同namespace查出的数据会放在自己对应的缓存中(map)
*             效果:数据会从二级缓存中获取
*                 查出的数据都会被默认先放在一级缓存中。
*                 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
*         使用:
*             1)、开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
*             2)、去mapper.xml中配置使用二级缓存:
*                 <cache></cache>
*             3)、我们的POJO需要实现序列化接口
*
* 和缓存有关的设置/属性:
*             1)、cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)
*             2)、每个select标签都有useCache="true":
*                     false:不使用缓存(一级缓存依然使用,二级缓存不使用)
*             3)、【每个增删改标签的:flushCache="true":(一级二级都会清除)】
*                     增删改执行完成后就会清楚缓存;
*                     测试:flushCache="true":一级缓存就清空了;二级也会被清除;
*                     查询标签:flushCache="false":
*                         如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;
*             4)、sqlSession.clearCache();只是清楚当前session的一级缓存;
*             5)、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;
*                                 STATEMENT:可以禁用一级缓存;
*
*第三方缓存整合:
*        1)、导入第三方缓存包即可;
*        2)、导入与第三方缓存整合的适配包;官方有;
*        3)、mapper.xml中使用自定义缓存
*        <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
*/

测试:

public SqlSessionFactory getSqlSessionFactory() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    return new SqlSessionFactoryBuilder().build(inputStream);
}
/**
 * 测试一级缓存是否关闭
 */
@Test
public void testFirstLevelCache() throws 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();
    }
}



/**
 * 查询过后 插入数据
 * 两级缓存都被清楚了,是拿不到数据的
 * 这个时候没有找到缓存的内容,所有还是会执行查询的
 */
@Test
public void testSecondLevelCache2() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession openSession = sqlSessionFactory.openSession();
    SqlSession openSession2 = sqlSessionFactory.openSession();

    //1、
    EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);

    Employee emp01 = mapper.getEmpById(1);
    System.out.println(emp01);
    openSession.close();//这里要关闭才会使用二级缓存

    //两级缓存都被清楚了,是拿不到数据的
    mapper2.addEmp(new Employee(null, "aaa", "nnn", "0"));
    Employee emp02 = mapper2.getEmpById(1);
    System.out.println(emp02);
    openSession2.close();
}




/**
 * 测试二级缓存
 */
@Test
public void testSecondLevelCache() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession openSession = sqlSessionFactory.openSession();
    SqlSession openSession2 = sqlSessionFactory.openSession();

    //1、
    EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);

    Employee emp01 = mapper.getEmpById(1);
    System.out.println(emp01);
    openSession.close();//这里要关闭才会使用二级缓存

    //第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
    Employee emp02 = mapper2.getEmpById(1);
    System.out.println(emp02);
    openSession2.close();
}

 


pom.xml 

mybatis-ehcache 依赖下面即可日志包,需要一并配置

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>

<!--日志包 mybatis-ehcache 需要-->
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.26</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>


<!--数据库驱动-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.37</version>
</dependency>

mybatis-config.xml

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

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--显式的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题  -->
        <!--开启全局二级缓存 默认为 true-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <environments default="dev_mysql">
        <environment id="dev_mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>

    </environments>

    <mappers>
        <!-- 批量注册: -->
        <package name="com.atguigu.mybatis.dao"/>
    </mappers>
</configuration>

log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n"/>
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info"/>
    </logger>
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

 

©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页