MyBatis二级缓存带来的问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis二级缓存带来的问题相关的知识,希望对你有一定的参考价值。
参考技术A MyBatis二级缓存使用的在某些场景下会出问题,来看一下为什么这么说。假设我有一条select语句(开启了二级缓存):
selecta.col1, a.col2, a.col3, b.col1, b.col2, b.col3fromtableA a, tableB bwherea.id= b.id;
对于tableA与tableB的操作定义在两个Mapper中,分别叫做MapperA与MapperB,即它们属于两个命名空间,如果此时启用缓存:
MapperA中执行上述sql语句查询这6个字段
tableB更新了col1与col2两个字段
MapperA再次执行上述sql语句查询这6个字段(前提是没有执行过任何insert、delete、update操作)
此时问题就来了,即使第(2)步tableB更新了col1与col2两个字段,第(3)步MapperA走二级缓存查询到的这6个字段依然是原来的这6个字段的值,因为我们从CacheKey的3组条件来看:
<select>标签所在的Mapper的Namespace+<select>标签的id属性
RowBounds的offset和limit属性,RowBounds是MyBatis用于处理分页的一个类,offset默认为0,limit默认为Integer.MAX_VALUE
<select>标签中定义的sql语句
对于MapperA来说,其中的任何一个条件都没有变化,自然会将原结果返回。
这个问题对于MyBatis的二级缓存来说是一个无解的问题,因此使用MyBatis二级缓存有一个前提: 必须保证所有的增删改查都在同一个命名空间下才行 。
Java——Mybatis二级缓存
一、什么是二级缓存
二级缓存是mapper级别的缓存,Mybatis默认是没有开启二级缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,也就是说,二级缓存是跨SqlSession的,因此二级缓存的作用范围更大。
UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。
二、二级缓存原理
不同的sqlsession都要调用mapper下的sql语句发起数据库请求。
第一次调用mapper下的SQL去查询用户信息。查询到的信息会存到该mapper对应的二级缓存区域内。
第二次调用相同namespace下的mapper映射文件中相同的SQL去查询用户信息。会去对应的二级缓存内取结果。
如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该namespace下的二级缓存。
sqlsession对象销毁mapper中的二级缓存数据仍然存在。
三、开启二级缓存
1、 在核心配置文件SqlMapConfig.xml中加入以下内容(开启二级缓存总开关):
cacheEnabled设置为 true
2、在映射文件中,加入以下内容,开启二级缓存:
3、实现序列化
由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化。
如果该类存在父类,那么父类也要实现序列化。
禁用二级缓存
该statement中设置userCache=false可以禁用当前select语句的二级缓存,即每次查询都是去数据库中查询,默认情况下是true,即该statement使用二级缓存。
刷新二级缓存
四、如何解决一级缓存中Spring和Mybatis中sqlSession关闭的问题?
Spring和MyBatis整合时, 每次查询之后都要进行关闭sqlSession,关闭之后数据被清空。所以spring整合之后,如果没有事务,一级缓存是没有意义的。而如果开启二级缓存的话,关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。这样,缓存在sqlsession关闭之后依然存在。
四、问题
二级缓存是建立在同一个namespace下的,如果对表的操作查询可能有多个namespace,那么得到的数据就是错误的。
举个例子:
订单和订单详情,orderMapper、orderDetailMapper。在查询订单详情时我们需要把订单信息也查询出来,那么这个订单详情的信息被二级缓存在orderDetailMapper的namespace中,这个时候有人要修改订单的基本信息,那就是在orderMapper的namespace下修改,他是不会影响到orderDetailMapper的缓存的,那么你再次查找订单详情时,拿到的是缓存的数据,这个数据其实已经是过时的。
根据以上,想要使用二级缓存时需要想好两个问题:
对该表的操作与查询都在同一个namespace下,其他的namespace如果有操作,就会发生数据的脏读。
对关联表的查询,关联的所有表的操作都必须在同一个namespace。
以上是关于MyBatis二级缓存带来的问题的主要内容,如果未能解决你的问题,请参考以下文章