13.缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。要启用全局的二级缓存
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
13.1 默认缓存(一级缓存)
mybatis默认在会话级别开启缓存,select查询会触发缓存,当有更新数据则会刷新缓存
@Test
public void testSelectByExample() {
SqlSession session = factory.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student stu1 = mapper.selectByPrimaryKey(1);
System.out.println("stu1:"+stu1);
//执行update 触发缓存刷新
// mapper.updateByPrimaryKey(stu1);
Student stu2 = mapper.selectByPrimaryKey(1);
System.out.println("stu2 = " + stu2);
session.close();
}
13.2 二级缓存
二级缓存默认关闭,需要手动的打开,在namespace层生效,可以在某一个不需要缓存的 Statement上加useCache="false"
在SqlMapConfig.xml配置文件中设置cacheEnabled 为true
在需要开启缓存的Mapper.xml添加
<cache/>
标记xml<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
当SQLSession执行commit或者close的时候才添加二级缓存的数据
使用
在sqlmapconfig.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>
<properties resource="db/jdbc.properties">
</properties>
<settings>
<!-- 打印sql -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 3.4.1 之前的版本(true) 也得加上 aggressiveLazyLoading false-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- ll ls -l 类型别名-->
<typeAliases>
<!-- <typeAlias type="com.neuedu.mybatis.entity.UmsUser" alias="User"/>-->
<!-- <typeAlias type="com.neuedu.mybatis.entity.Dept" alias="Dept"/>-->
<package name="com.neuedu.mybatis.entity"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 链接数据的信息-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 每张表的 statement-->
<!-- <mapper resource="mapper/DeptMapper.xml"/>-->
<!-- <mapper resource="mapper/UmsUserMapper.xml"/>-->
<!-- 1 xml:namespace 跟 接口的全限定名保持一致
2 接口的方法名 跟 statement 的 id 保持一致
3 接口的参数类型 跟 statement 的 parameterType 保持一致
4 接口的返回值类型 跟 statement 的 resultType 保持一致
5 路径得保持一致 :
Mapper.java 的路径: com.neuedu.mybatis.mapper. XXXX。Java
Mapper.xml 的路径 com.neuedu.mybatis.mapper. XXXX。xml
-->
<!-- <mapper class="com.neuedu.mybatis.mapper.DeptMapper"></mapper>-->
<!-- <mapper class="com.neuedu.mybatis.mapper.UmsUserMapper"></mapper>-->
<!-- 要求 同 class的方式一样-->
<package name="com.neuedu.mybatis.mapper"/>
</mappers>
</configuration>
在某一个Mapper.xml中的namespace中添加 <cache/>
标签
在Java中使用
@Test
public void testNamespaceCache() throws IOException {
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getInstance();
SqlSession session1 = sqlSessionFactory.openSession();
SqlSession session2 = sqlSessionFactory.openSession();
TeacherExample example = new TeacherExample();
TeacherMapper teacherMapper1 = session1.getMapper(TeacherMapper.class);
List<Teacher> teachers = teacherMapper1.selectByExample(example);
System.out.println("teachers = " + teachers);
//触发二级缓存 存储数据
session1.commit();
TeacherMapper teacherMapper2 = session2.getMapper(TeacherMapper.class);
List<Teacher> teachers2 = teacherMapper2.selectByExample(example);
List<Teacher> teachers3= teacherMapper2.selectByExample(example);
System.out.println("teachers2 = " + teachers2);
session1.close();
session2.close();
}
注意:
二级缓存不是查完设置的缓存,需要sqlsession对象 调用commit 或者close 方法
控制台输出
Cache Hit Ratio [com.neuedu.mybatis.mapper.TeacherMapper]: 0.0
Opening JDBC Connection
Created connection 1086849943.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40c80397]
==> Preparing: select teacher_id, name, age, salary from teacher
==> Parameters:
<== Columns: teacher_id, name, age, salary
<== Row: 1, 张飞, 50, 5555.01
<== Row: 2, 赵云, 55, 8873.00
<== Row: 3, 刘备, 66, 88.00
<== Total: 3
teachers = [Teacher [Hash = 2014563089, teacherId=1, name=张飞, age=50, salary=5555.01, serialVersionUID=1], Teacher [Hash = 198098993, teacherId=2, name=赵云, age=55, salary=8873.00, serialVersionUID=1], Teacher [Hash = 553672556, teacherId=3, name=刘备, age=66, salary=88.00, serialVersionUID=1]]
Cache Hit Ratio [com.neuedu.mybatis.mapper.TeacherMapper]: 0.5
Cache Hit Ratio [com.neuedu.mybatis.mapper.TeacherMapper]: 0.6666666666666666
teachers2 = [Teacher [Hash = 2024711353, teacherId=1, name=张飞, age=50, salary=5555.01, serialVersionUID=1], Teacher [Hash = 711310213, teacherId=2, name=赵云, age=55, salary=8873.00, serialVersionUID=1], Teacher [Hash = 1267042315, teacherId=3, name=刘备, age=66, salary=88.00, serialVersionUID=1]]
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40c80397]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40c80397]
Returned connection 1086849943 to pool.
13.3 Mybatis的默认缓存实现 :
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.impl;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
/**
* @author Clinton Begin
*/
public class PerpetualCache implements Cache {
private final String id;
private final Map<Object, Object> cache = new HashMap<>();
public PerpetualCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public int getSize() {
return cache.size();
}
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
@Override
public Object removeObject(Object key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
public boolean equals(Object o) {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
if (this == o) {
return true;
}
if (!(o instanceof Cache)) {
return false;
}
Cache otherCache = (Cache) o;
return getId().equals(otherCache.getId());
}
@Override
public int hashCode() {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
return getId().hashCode();
}
}
13.4 使用Ehcache类库
13.4.1 添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mybatis-parent-java1</artifactId>
<groupId>com.neuedu.mybatis</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>08-mybatis-cache</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--第三方的缓存类库 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.2</version>
</dependency>
<!-- 整合Ehcache 和mybatis的Cache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
</project>
13.4.2 开启二级缓存
在sqlMapConfig.xml中设置 缓存启动
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="cacheEnabled" value="true"/>
</settings>
13.4.3 在namespace 中添加Ehcache的缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
13.4.4 添加Ehcache主配置文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!--diskStore:缓存数据持久化的目录 地址 -->
<diskStore path="D:\ehcache" />
<defaultCache
maxEntriesLocalHeap="0"
eternal="false"
diskPersistent = "true"
timeToIdleSeconds="1200"
timeToLiveSeconds="1200">
</defaultCache>
</ehcache>
