MyBatis 是持久层框架,支持一级缓存和二级缓存
mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
1. 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
3. 对于缓存数据更新机制,当某一个作用域(一级缓存/二级缓存)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被清空。
一级缓存
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
package com.aiit.test;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.aiit.pojo.User;
public class TestMyBatisDemo {
/*
* 一级缓存: 也就Session级的缓存(默认开启)
*/
public static void main(String[] args) {
String resource="mybatis.cfg.xml";
InputStream inputStream = TestMyBatisDemo.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//1.查询
String statement1 = "com.aiit.dao.UserMapper.selectOne";
User user1 = session.selectOne(statement1,1);
System.out.println("查询结果1: " + user1);
/*
必须是同一个Session,如果session对象已经close()过了就不可能用了
*/
user1 = session.selectOne(statement1,1);
System.out.println("查询结果2: " + user1);
session.close();
/**
* session被关闭下面代码就没有结果,需要重新执行session = factory.openSession();
* user1 = session.selectOne(statement1,1);
System.out.println("查询结果3: " + user1);
*/
session = factory.openSession();
user1 = session.selectOne(statement1,1);
System.out.println("查询结果4: " + user1);
//3.修改 执行增删改时都会自动清除缓存,确保操作后和数据库一致
String statement3 = "com.aiit.dao.UserMapper.updateOne";
User user3 = new User(9, "中国");
int updateResult = session.update(statement3, user3);
System.out.println("修改成功"+updateResult);
session.commit();
session.close();
}
}
特别注意的地方:
1.一级缓存: 也就Session级的缓存(默认开启)
2. 必须是同一个Session,如果session对象已经close()过了就不可能用了,需要重新session = factory.openSession();
3.一旦执行增删改,缓存将会被清除
二级缓存
1.在mybatis.cfg.xml中开启二级缓存
<configuration>
<settings>
<!--这个配置使全局的映射器(二级缓存)启用或禁用缓存-->
<setting name="cacheEnabled" value="true" />
.....
</settings>
....
</configuration>
2.UserMapper.xml在映射文件中开启二级缓存
具体配置 开启缓存cache、执行缓存useCache、刷新缓存flushCache
<?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.aiit.dao.UserMapper">
<!--开启本mapper的namespace下的二级缓存-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
<!--可以通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是关闭-->
<select id="selectOne" parameterType="int" resultType="com.aiit.pojo.User" useCache="true">
SELECT * FROM tbl_user WHERE tbl_user.id=#{id}
</select>
<!--刷新二级缓存 flushCache
<select id="selectOne" parameterType="int" resultType="com.aiit.pojo.User" flushCache="true">
SELECT * FROM tbl_user WHERE tbl_user.id=#{id}
</select>
-->
</mapper>
3.使用二级缓存时,User类必须实现一个Serializable接口===> User implements Serializable
4.测试二级缓存
使用两个不同的SqlSession对象去执行相同查询条件的查询,第二次查询时不会再发送SQL语句,而是直接从缓存中取出数据
package com.aiit.test;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.aiit.pojo.User;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
String resource="mybatis.cfg.xml";
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//开启两个不同的SqlSession
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
String statement1 = "com.aiit.dao.UserMapper.selectOne";
User user1 = session1.selectOne(statement1,1);
//一定要提交事务之后二级缓存才会起作用
session1.commit();
System.out.println("查询结果1: " + user1);
//由于使用的是两个不同的SqlSession对象,所以即使查询条件相同,一级缓存也不会开启使用
user1 = session2.selectOne(statement1,1);
System.out.println("查询结果1: " + user1);
}
}
5.验证我们可以根据两条查询语句的时间,查看是否执行缓存
long startTime = System.currentTimeMillis(); //获取开始时间
User user1 = session1.selectOne(statement1,1); //测试的代码段
long endTime = System.currentTimeMillis(); //获取结束时间
long time = endTime - startTime ;
System.out.println("查询时间: " + time );
特别注意:
1. 映射语句文件中的所有select语句将会被缓存。
2. 映射语句文件中的所有insert,update和delete语句会刷新缓存。
3. 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
4. 缓存会根据指定的时间间隔来刷新。
5. 缓存会存储1024个对象
cache标签
<cache
eviction="FIFO" <!--回收策略为先进先出-->
flushInterval="60000" <!--自动刷新时间60s-->
size="512" <!--最多缓存512个引用对象-->
readOnly="true"/> <!--只读-->
1.eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一处最长时间不用的对象
(2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,移除最长时间不用的对形象
2. flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当SQL被执行的时候才会去刷新缓存。
3.size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
4. readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有办法修改缓存,他的默认值是false,不允许我们修改。