mybatis开启service层事务时一级缓存的问题

问题:在mybatis service层开启事务管理后,同一个事务中,相同的查询会调用一级缓存,从而查找结果是之前查到的对象而不是数据库中的记录值。

框架:spring+struts2+mybatis
spring对象管理,struts2负责MVC和依赖注入,mybatis数据库访问。

采用以下的配置将事务控制在service层。
applicationContext.xml

<bean name="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*Transaction" propagation="REQUIRED"
            read-only="false"
            rollback-for="java.lang.RuntimeException,com.test.exception.TransactionException" />
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="coreTransactionPointcut"
        expression="execution(public * com.test.service.*.*(..))" /> 

<!--把事务控制在Service层 -->

    <aop:advisor pointcut-ref="coreTransactionPointcut"
        advice-ref="transactionAdvice" />
    </aop:config>
<tx:annotation-driven transaction-manager="transactionManager" />

在需要事务控制的service方法前加注解:
@Transactional

mybatis采用DAO接口+mapper.xml,运行时生成代理类。
IUserDao接口

public interface IUserDao 
{
 public User getById(@Param("id") int id);
}

IUserDao.xml

<mapper namespace="com.test.dao.IUserDAO">

 <select id="getById" parameterType="int" resultType="user">
  SELECT * FROM T_USER WHERE ID=#{id}
 </select>

</mapper>

于是就出现了以下的情况。

@Transactional 
public void testQuery(){
    User first = userDao.getById(3);
    System.out.println("1:"+fisrt.getUserName());
    first.setUserName("Jetty");
    User second = userDao.getById(3);
    System.out.println("2:"+second.getUserName());
}

运行结果

1:Tommy
2:Jetty

看一下数据库,记录并没有被更改。
初步判断是因为mybatis缓存造成的。
在这里了解了一级缓存和二级缓存的定义
http://blog.csdn.net/u010558660/article/details/51801099

这里提供了关闭缓存的三种方法
http://blog.csdn.net/theoffspring/article/details/6111907

我用了第二种方法,单独关闭一条sql语句的缓存。
在Sql映射文件标签中加入属性
flushCache=”true” useCache=”false”

IUserDao.xml

<mapper namespace="com.test.dao.IUserDAO">

 <select id="getById" parameterType="int" resultType="user"  flushCache="true" useCache="false">
  SELECT * FROM T_USER WHERE ID=#{id}
 </select>

</mapper>

getById的缓存已被关闭。

继续测试,发现起作用的其实是 flushCache=”true”,useCache=”false” 不起作用。
原因在这里:
http://blog.csdn.net/ssssny/article/details/52248960

flushCache的作用是自动刷新一级缓存,而useCache的作用是禁用二级缓存,可见在这里起作用的是一级缓存。

一级缓存以SqlSession为作用范围,二级缓存是Application级别的。
当SqlSession遇到一个和先前完全一样的查询时,会去取出上一次查询的对象作为结果。
当记录发生更改时,一级缓存自动刷新。

在我的情况中,第二次查询调用的Sql语句、参数和之前完全相同,记录也没有被更新,符合缓存的条件。系统去内存中查找之前查到的对象。
但这个时候,查询出来的对象已经被我修改了,所以取到的不是数据库中的值而是修改的值。
在映射文件的配置中加入flushCache=”false”,强制每次查询都刷新缓存。问题解决。

但是不知道缓存和事务管理有什么关系,不知道其内部机制。估计是同一个事务内使用同一个SqlSession的原因。

其他参考文献
http://blog.csdn.net/chris_mao/article/details/48804493
mybatis完全配置

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值