mybatis二级缓存问题
1、mybatis
缓存中的问题:
在使用mybatis进行关联查询的时候,如果学生和老师两张表进行关联查询有一下的操作步骤:
1.1 通过学生进行关联查询老师Mapper文件为:
<select id="queryStudentAndTeacher" resultMap="BaseResultMap" useCache="true" >
SELECT
s.id,
s.`name`,
s.gender,
s.major,
s.grade,
s.supervisor_id ,
t.id as tea_id,
t.`name` as tea_name,
t.gender as tea_gender,
t.research_area as tea_research_area,
t.title as tea_title
FROM
student s
LEFT JOIN teacher t ON s.supervisor_id = t.id
</select>
1.2 查询完成后我更新了老师
<update id="updateByExample" parameterType="map" >
update teacher
set id = #{record.id,jdbcType=INTEGER},
name = #{record.name,jdbcType=VARCHAR},
gender = #{record.gender,jdbcType=VARCHAR},
research_area = #{record.researchArea,jdbcType=VARCHAR},
title = #{record.title,jdbcType=VARCHAR}
<if test="_parameter != null" >
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
1.3 再次查询:
我们发现更新了数据,但是查询出的结果却没有变化
@Test
public void TestSecondCache() throws IOException{
org.apache.ibatis.logging.LogFactory.useStdOutLogging();
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(reader);
reader.close();
SqlSession session = null;
session = sqlSessionFactory.openSession();
StudentMapper studentMapper = (StudentMapper) session.getMapper(StudentMapper.class);
Map<String, Object> params = new HashMap<String,Object>();
/**
* 模拟下面的情况
* 1. 执行StudentMapper中的"queryStudentAndTeacher" 操作,此时会将查询到的结果放置到StudentMapper对应的二级缓存StudentCache中;
* 2. 执行teacherMapper中对teacher的更新操作(update、delete、insert)后,teacher的数据更新;
* 3. 再执行1完全相同的查询,这时候会直接从StudentMapper二级缓存StudentCache中取值,将StudentMapper中的值直接返回;
* */
List<Student> students = studentMapper.queryStudentAndTeacher(params);
for (Student student : students) {
System.out.println(student.getId() + student.getTeacher().getName());
}
System.out.println(students.size());
//更新老师
SqlSession session2 = sqlSessionFactory.openSession();
TeacherMapper teacherMapper = session2.getMapper(TeacherMapper.class);
Teacher teacher = new Teacher();
teacher.setName("刘老师");
teacher.setId(1);
TeacherExample teacherExample = new TeacherExample();
teacherExample.createCriteria().andIdEqualTo(1);
teacherMapper.updateByExample(teacher, teacherExample);
session2.commit();
//第二次查询,查看是否从缓存中读取
List<Student> students2 = studentMapper.queryStudentAndTeacher(params);
for (Student student : students2) {
System.out.println(student.getId() + student.getTeacher().getName());
}
System.out.println(students2.size());
checkCacheStatus(session);
}
2、 如果遇到这种情况,应该如何解决?
我们可以上网,网上提供的二级缓存插件来解决该问题:
简介
MyBatis Enhanced Cache, Control your Caches precisely!
该插件主要是为了弥补MyBatis二级缓存控制上的不足,提高二级缓存Cache和数据库数据的同步性和一致性,处理各个Cache之间的关联关系。
该插件可以精确地管理MyBatis的二级缓存,实现对MyBatis二级缓存细粒度的控制。
当执行过对数据库表的更新操作(update、delete、insert)时,可以指定清除由特定的StatementId表示的查询语句产生的缓存。
应用场景:
当前的MyBaits对于缓存的比较粗糙,一般为一个Mapper配置一个Cache缓存,或者多个Mapper共用一个缓存。
而对缓存的维护都是独立的,缓存之间不会相互影响,指定的Mapper中的语句只会影响到该Mapper对应的Cache缓存。
有时候希望当执行某些更新操作时,能够刷新或者清空特定的查询语句产生的缓存,以避免数据不一致的情况。
###举例
现有AMapper.xml 中定义了对数据库表ATable
的CRUD操作,BMapper定义了对数据库表BTable
的CRUD操作;
假设MyBatis的二级缓存开启,并且AMapper中使用了二级缓存,AMapper对应的二级缓存为ACache
;
除此之外,AMapper中还定义了一个跟BTable有关的查询语句,类似如下所述:
<select id="selectATableWidtJoin" resultMap="BaseResultMap" useCache="true">
select * from ATable left join BTable on ....
</select>
-
执行了
selectATableWithJoin
操作,该查询的缓存会被放置到对应的二级缓存ACache
中,
当再次执行相同的查询,则直接从二级缓存ACache
中; -
如果某个时候,BMapper中执行了对BTable的update操作(update 、delete、insert),BTable 的数据已经更新,
-
再次执行
selectATableWithJoin
操作,该查询的结果直接从二级缓存ACache
中取,这时候就造成了**数据不一致**
的情况
针对上述的问题,需要解决:
当BMapper执行对BTable的update操作时,指定刷新 ACache
中的 selectATableWithJoin
语句产生的缓存
该插件正是解决上述的这一问题!
使用方法:
使用此插件非常简单:
-
在mybatisConfig.xml 文件中定义plugin节点如下:
<plugins>
<plugin interceptor="org.luanlouis.mybatis.plugin.cache.EnhancedCachingExecutor">
<property name="dependency" value="dependencys.xml"/>
<property name="cacheEnabled" value="true"/>
</plugin>
</plugins>
-
dependencys.xml配置文件,配置StatemntId依赖关系
<?xml version="1.0" encoding="UTF-8"?>
<dependencies>
<statements>
<statement id="com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey">
<observer id="com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments" />
</statement>
</statements>
</dependencies>
-
<statement>节点配置更新语句和查询语句的依赖关系,如果id表示的更新语句执行了,会清空由配置的id表示的查询语句生成的缓存。
-
<observer>节点表示当父节点 id表示的更新语句执行后,应该清除此语句所产生的缓存