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.*.*(..))" /> 



    <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

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

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

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

@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

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

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

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完全配置

你可能感兴趣的:(工作日志)