【SSM】SSM之MyBatis框架:MyBatis的缓存技术

一、缓存:

请先观察下面程序的执行结果:

package cn.jingpengchong.userinfo.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

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 cn.jingpengchong.userinfo.dao.IUserInfoDao;
import cn.jingpengchong.userinfo.vo.UserInfo;

public class Test {
	public static void main(String[] args) {
		try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
			SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession session = factory.openSession();
			IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
			List<UserInfo> list = userInfoDao.selectAll();
			System.out.println(list.size());

			list = userInfoDao.selectAll();
			System.out.println(list.size());
			
			session.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

运行结果如下:
在这里插入图片描述
我们发现明明我们程序中查了两次数据库,但从日志输出来看实际上只访问了一次数据库,那么这是什么原因呢?答案就是:缓存。为了提升查询效率,提高用户体验,MyBatis提供了数据缓存支持,依据数据缓存的有效范围默认定义了一级缓存和二级缓存。第一次查询数据库后,Mybatis自动将结果放在了一级缓存中,当第二次的查询语句与第一次一样的时候,并且中间没有操作数据库中的数据且没有清除缓存时,Mybatis就会直接把缓存中的数据返回给用户。这样做的好处就是减少了对数据库的频繁访问对数据库造成的损害,并且提升了查询速度。

二、一级缓存:

一级缓存为SqlSession级别的缓存,也称为本地缓存,默认是开启的,不能关闭。但是以下4种情况将会再次访问数据库:

1、在不同的SqlSession中查询数据:

这种情况严格意义上来讲并不算是一级缓存失效了,因为一级缓存是SqlSession级别的缓存,在该SqlSession外自然也就访问不到它的缓存了。例如,将上面程序中的try代码块中的代码做如下修改:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

session = factory.openSession();
userInfoDao = session.getMapper(IUserInfoDao.class);
list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

此时如果再次运行程序,就会访问两次数据库了:
在这里插入图片描述

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

这种情况也不算是缓存失效了,缓存中的数据还在,只不过由于缓存中没有存储该语句查询出来的数据,因此第二次Mybatis还会去访问数据库。例如,将上面程序中的try代码块中的代码做如下修改:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll(20);
System.out.println(list.size());

list = userInfoDao.selectAll(25);
System.out.println(list.size());

session.close();

此时如果再次运行程序,就会访问两次数据库了:
【SSM】SSM之MyBatis框架:MyBatis的缓存技术_第1张图片

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

这种情况是缓存失效了,因为增删改都有一个flushCache属性并且默认值是true,如果执行了增删改操作就会刷新缓存。当然,如果是false的话就不会刷新缓存了,但是这种做法是错误的,会导致第二次查询到的数据不正确。例如,将上面程序中的try代码块中的代码做如下修改:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());

userInfoDao.delete("'%四%'");

list = userInfoDao.selectAll();
System.out.println(list.size());

session.close();

此时如果再次运行程序,就会访问两次数据库了:
【SSM】SSM之MyBatis框架:MyBatis的缓存技术_第2张图片

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

既然清除了缓存,那么之前的缓存当然是失效了。例如,将上面程序中的try代码块中的代码做如下修改:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());

session.clearCache();

list = userInfoDao.selectAll();
System.out.println(list.size());

session.close();

此时如果再次运行程序,就会访问两次数据库了:
在这里插入图片描述

三、二级缓存:

该级缓存为namespace级别的缓存,通过SqlSession查询数据,这些数据将会放到当前会话的一级缓存中;如果当前会话关闭,则一级缓存中的数据会被保存到二级缓存中,此后新的SqlSession将从二级缓存中查找数据。
二级缓存默认不开启,但如果使用二级缓存需要在每个XML映射文件中添加以配置该级缓存,二级缓存就可以通过在全局配置文件配置setting标签来关闭该级缓存:

  • cache标签属性:
  • eviction:缓存回收策略:
    • LRU – 最近最少使用的:移除最长时间不被使用的对象,默认值
    • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
    • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
  • flushInterval:刷新间隔,单位毫秒,默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
  • size:引用数目,正整数,代表缓存最多可以存储多少个对象,太大容易导致内存溢出
  • readOnly:只读,默认为false。true:只读缓存;会给所有调用者返回缓存对象的相同实例,速度快;false:读写缓存;会返回缓存对象的拷贝(通过序列化),速度慢但安全。
    并且二级缓存存储的数据必须可序列化,如果是自定义类型的话,要实现Serializable接口。
    例如,在XML映射文件中添加后,将上面程序中的try代码块中的代码做如下修改:
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

session = factory.openSession();
userInfoDao = session.getMapper(IUserInfoDao.class);
list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

运行程序,会发现尽管两次查询不在同一个SqlSession中,依然只访问一次数据库,并且我们可以从日志中清楚的看到第二次查询是从缓存中获取的:
在这里插入图片描述
但是如果两次查询之间做了增删改操作,那么同样会使得缓存中的数据被清空:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

session = factory.openSession();
userInfoDao = session.getMapper(IUserInfoDao.class);
userInfoDao.delete("'%四%'");
list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

运行结果如下:
【SSM】SSM之MyBatis框架:MyBatis的缓存技术_第3张图片
从中我们看到了Mybatis缓存技术的好处,同时也发现了其中存在的问题,比如,我们仅仅修改数据库中的一条数据,它就会把缓存给清空了,其他查询就只能再次访问数据库了。

你可能感兴趣的:(SSM)