MyBatis框架一级缓存与二级缓存

为了提升查询效率,提高用户体验,MyBatis提供了数据缓存支持,依据数据缓存的有效范围默认定义了一级缓存和二级缓存。

一级缓存

1、该级缓存默认开启,不能关闭;

2、该级缓存为SqlSession级别的缓存,也称为本地缓存;

3、以下4种情况将会导致该级缓存失效:

用到的数据表:

      a、在不同SqlSession中查询数据;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.jd.userinfo.dao.IUserInfoDao;

public class Test {
	public static void main(String[] args) {
		try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession sqlSession = sqlSessionFactory.openSession(true);
			IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			
			sqlSession.close();
			
			sqlSession = sqlSessionFactory.openSession(true);
			userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			sqlSession.close();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

输出结果如下:

我们可以看到,虽然我们获取三次数据,但是第二次获取并没有访问数据库直接输出了,是因为 mybatis 缓存已经存在了需要的数据,那就没有必要再访问数据库了,而第三次请求查询时,上一次的 SQLSession 已经关闭,又重新连接创建了一个新的 SQLSession,因此重新访问数据库并返回数据。

      b、相同SqlSession中查询数据,但查询条件不同

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.jd.userinfo.dao.IUserInfoDao;

public class Test {
	public static void main(String[] args) {
		try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession sqlSession = sqlSessionFactory.openSession(true);
			IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.getName("%m%").size());
			userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.getName("%e%").size());
			sqlSession.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

虽然在同一个 SQLSession 中,但是即使同一个查询方法,但查询条件不同(模糊查询),则也会重新访问数据库获取数据;执行结果如下:

      c、相同SqlSession中查询数据,但两次查询之间执行了增删改操作

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.jd.userinfo.dao.IUserInfoDao;

public class Test {
	public static void main(String[] args) {
		try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession sqlSession = sqlSessionFactory.openSession(true);
			IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			
			System.out.println(userInfoDao.delete("7"));
			
			userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			sqlSession.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

即使在同一个 SQLSession 中执行相同的查询,但是两次查询之间进行了增删改操作(即使操作失败了,如上例);为了防止因数据库的变化而导致 cache 缓存中的数据错误,会把 cache 缓存中的数据全部清空,重新从数据库中获取;执行结果如下:

      d、相同SqlSession中查询数据,但第二次查询前,程序调用SqlSession对象clearCache()方法手动清除了一级缓存

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.jd.userinfo.dao.IUserInfoDao;

public class Test {
	public static void main(String[] args) {
		try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession sqlSession = sqlSessionFactory.openSession(true);
			IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			
			sqlSession.clearCache();
			
			userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			sqlSession.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

在同一个 SQLSession 中,即使两个查询操作相同,但之间执行了 sqlSession.clearCache() 方法清除了缓存,第二次查询还是需要向数据库请求数据。

二级缓存

1、该级缓存默认不开启,但如果使用二级缓存需要在每个XML映射文件中添加<cache></cache>以配置该级缓存。二级缓存可以通过在全局配置文件配置setting标签来关闭该级缓存;

cache标签属性:

eviction:缓存回收策略:

                 LRU – 最近最少使用的:移除最长时间不被使用的对象,默认值

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

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

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

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

size:引用数目,正整数,代表缓存最多可以存储多少个对象,太大容易导致内存溢出

readOnly:只读,默认为false。true:只读缓存;会给所有调用者返回缓存对象的相同实例,速度快;false:读写缓存;会返回缓存对象的拷贝(通过序列化),速度慢但安全。

2、该级缓存为namespace级别的缓存;

3、工作机制:通过SqlSession查询数据,这些数据将会放到当前会话的一级缓存中;如果当前会话关闭,则一级缓存中的数据会被保存到二级缓存中,此后新的SqlSession将从二级缓存中查找数据;

使用二级缓存需要配置<cache></cache>标签开启二级缓存,如下:(以上代码的配置无<cache></cache>标签)

<?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.jd.userinfo.dao.IUserInfoDao">

	<cache></cache>

	<select id="get" resultType="com.jd.vo.UserInfo">
		select * from user_info
	</select>
	
	<select id="getName" resultType="com.jd.vo.UserInfo">
		select * from user_info
		<where>
			<if test="name!=null">
				name like #{name}
			</if>
		</where>
	</select>
	
	<delete id="delete">
		delete from user_info where id=#{id}
	</delete>
</mapper>

然后看 Test.java

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.jd.userinfo.dao.IUserInfoDao;

public class Test {
	public static void main(String[] args) {
		try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession sqlSession = sqlSessionFactory.openSession(true);
			IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			
			sqlSession.close();
			
			sqlSession = sqlSessionFactory.openSession(true);
			userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			sqlSession.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

虽然这两次查询是不同 SQLSession 中的同一种方法,但是因为我们开启了二级缓存的原因,当以一级缓存关闭时,二级缓存会将一级缓存的数据取出放入到二级缓存,因此,即使不在同一个 SQLSession 中,第二次相同的方法获取数据时可以直接从二级缓存中获取已有的缓存数据而不必再访问数据库。

4、select标签的useCache属性用于设置是否使用二级缓存;insert、update、delete或select标签均有flushCache属性,其中增删改默认true,即sql执行以后,会同时清空一级和二级缓存,查询默认false。

<?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.jd.userinfo.dao.IUserInfoDao">

	<cache></cache>

	<select id="get" resultType="com.jd.vo.UserInfo">
		select * from user_info
	</select>
	
	<select id="getName" resultType="com.jd.vo.UserInfo">
		select * from user_info
		<where>
			<if test="name!=null">
				name like #{name}
			</if>
		</where>
	</select>
	
	<delete id="delete" flushCache="false">
		delete from user_info where id=#{id}
	</delete>
</mapper>

将 delete 方法的 flushCache 属性值设置为 false;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.jd.userinfo.dao.IUserInfoDao;

public class Test {
	public static void main(String[] args) {
		try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession sqlSession = sqlSessionFactory.openSession(true);
			IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			sqlSession.close();
			
			sqlSession = sqlSessionFactory.openSession(true);
			userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.delete("5"));
			
			userInfoDao = sqlSession.getMapper(IUserInfoDao.class);
			System.out.println(userInfoDao.get().size());
			sqlSession.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

二级缓存虽然会将一级缓存的数据取出来,但是正常情况下增删改操作也会清空缓存中的数据,这里将 delete 方法的 flushCache 属性值设置为 false; 不让改方法清理缓存了,得到的结果如下:

我们可以看到,即使删除操作成功了也没有删除缓存中的数据,并且直接使用了;因此为了数据的及时性与正确性,一般不会将增删改方法的 flushCache 属性值设置为 false; 

5、为了提高扩展性,MyBatis定义了Cache缓存接口,可以通过实现该缓存接口自定义二级缓存,jar包下载: https://github.com/mybatis

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值