老司机学习MyBatis之一级缓存原理以及失效情况

一、前言

MyBatis将数据缓存设计成两级结构,分为一级缓存、二级缓存
一级缓存是Session会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中,又被称之为本地缓存。一级缓存是MyBatis内部实现的一个特性,用户不能配置,默认情况下自动支持的缓存,用户没有定制它的权利(不过这也不是绝对的,可以通过开发插件对它进行修改);
每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话。在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,如果不采取一些措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。
为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了。

如下图所示,MyBatis会在一次会话的表示—-一个SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询结果存入缓存并返回给用户。


这一节,我们将简单的说说什么是一级缓存以及什么情况下会导致一级缓存失效?

二、案例

创建一个新的Maven工程MyBatisPrimaryCache,工程结构目录如下


在测试类MyBatis中增加测试方法testPrimaryCache


打印查看一下控制台

2017-08-17 21:08:20,836 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==>  Preparing: select id,emp_name empName,emp_email empEmail, dept_id deptId from t_emp where id = ? 
2017-08-17 21:08:20,888 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==> Parameters: 1(Integer)
2017-08-17 21:08:20,924 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] <==      Total: 1
Emp [id=1, empName=queen3aasd21, [email protected], deptId=1]
Emp [id=1, empName=queen3aasd21, [email protected], deptId=1]
true

从控制台我们可以看到,即便Java代码中查询Emp信息,写了两段代码,但是实际上去后台数据库查询只做了一次SQL调用,第二次查询并没有去数据库查询,第二次获取的数据与第一次一样,它就直接从缓存中拿了数据。

如上我们简单的体验了一下一级缓存的存在

一级缓存我们也称作SqlSession级别的缓存,一级缓存默认就是开启的。既然是SqlSession级别的缓存,那就说明每个SqlSession对象拥有自己的一份数据,每个SqlSession之间是不能共享缓存的。但是每个独立的SqlSession缓存也有失效的时候。

下面我们通过案例看看哪些情况会导致一级缓存失效

①SqlSession不同

上面第一个测试案例,我们查询emp01和emp02都是使用的同一个mapper对象,如果mpper不是同一个会是什么情况,修改代码如下:


测试打印,查看一下控制台

2017-08-17 21:28:41,663 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==>  Preparing: select id,emp_name empName,emp_email empEmail, dept_id deptId from t_emp where id = ? 
2017-08-17 21:28:41,731 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==> Parameters: 1(Integer)
2017-08-17 21:28:41,774 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] <==      Total: 1
Emp [id=1, empName=queen3aasd21, [email protected], deptId=1]

2017-08-17 21:28:41,786 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==>  Preparing: select id,emp_name empName,emp_email empEmail, dept_id deptId from t_emp where id = ? 
2017-08-17 21:28:41,787 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==> Parameters: 1(Integer)
2017-08-17 21:28:41,789 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] <==      Total: 1
Emp [id=1, empName=queen3aasd21, [email protected], deptId=1]
false

如上,我们发现控制台打印了两段SQL,第二次查询取数据库重新获取了数据,封装成了一个新的对象,emp01==emp02 为false,说明不是同一个对象。一级缓存失效。

②SqlSession相同,但是查询条件不一样(当前一级缓存中还没有数据)


必然上面查询会发出两条SQL,查询2号员工时,因为本来就是第一次查询,所以不可能存在于缓存中的数据,需要调用数据库查询数据。

③SqlSession相同,两次查询之间增加了增删改操作


测试打印,查看一下控制台如下:

2017-08-17 21:45:12,885 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==>  Preparing: select id,emp_name empName,emp_email empEmail, dept_id deptId from t_emp where id = ? 
2017-08-17 21:45:12,960 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==> Parameters: 1(Integer)
2017-08-17 21:45:13,000 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] <==      Total: 1
Emp [id=1, empName=queen3aasd21, [email protected], deptId=1]
2017-08-17 21:45:13,052 [main] [com.queen.mybatis.mapper.EmpMapper.updateEmp]-[DEBUG] ==>  Preparing: update t_emp SET emp_name=?, emp_email=? where id=? 
2017-08-17 21:45:13,053 [main] [com.queen.mybatis.mapper.EmpMapper.updateEmp]-[DEBUG] ==> Parameters: tom123(String), [email protected](String), 3(Integer)
2017-08-17 21:45:13,053 [main] [com.queen.mybatis.mapper.EmpMapper.updateEmp]-[DEBUG] <==    Updates: 1
=====数据修改成功!======
2017-08-17 21:45:13,054 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==>  Preparing: select id,emp_name empName,emp_email empEmail, dept_id deptId from t_emp where id = ? 
2017-08-17 21:45:13,054 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==> Parameters: 1(Integer)
2017-08-17 21:45:13,056 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] <==      Total: 1
Emp [id=1, empName=queen3aasd21, [email protected], deptId=1]
false

如上所述,一级缓存失效了,虽然两次都是查询的同一条数据,但是却发了两次SQL,封装了两次对象且不是同一个对象。所以这种情况也会导致缓存失效。这种情况也比较容易理解,因为你在做修改的时候,可能就会修改到这条数据,当你修改了这条数据后,难道我还要去缓存中拿数据吗?当然是不行的。只要数据库有变化,就要再次去数据库查询数据。

④SqlSession相同,但是我们手动清除了一级缓存(缓存清空)


测试打印,查看一下控制台如下:

2017-08-17 21:56:21,619 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==>  Preparing: select id,emp_name empName,emp_email empEmail, dept_id deptId from t_emp where id = ? 
2017-08-17 21:56:21,680 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==> Parameters: 1(Integer)
2017-08-17 21:56:21,723 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] <==      Total: 1
Emp [id=1, empName=queen3aasd21, [email protected], deptId=1]
2017-08-17 21:56:21,724 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==>  Preparing: select id,emp_name empName,emp_email empEmail, dept_id deptId from t_emp where id = ? 
2017-08-17 21:56:21,724 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] ==> Parameters: 1(Integer)
2017-08-17 21:56:21,726 [main] [com.queen.mybatis.mapper.EmpMapper.findEmpById]-[DEBUG] <==      Total: 1
Emp [id=1, empName=queen3aasd21, [email protected], deptId=1]
false

我们发现这个时候也一样,缓存也失效了,第二次查询还是去数据库重新查询了一遍数据。因为缓存中已经没有数据了。


=======欢迎大家拍砖,小手一抖,多多点赞哟!=======

版权声明:本文为博主原创文章,允许转载,但转载必须标明出处。


猜你喜欢

转载自blog.csdn.net/gaomb_1990/article/details/80641964