0 0

spring+mybatis,Support事务传播级别下无法及时拿到新插入数据的问题0

Spring+Mybatis,事务配置是按照官网上配的

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  
            destroy-method="close"> 
        <property name="driverClass" value="${jdbc.driverClassName}" /> 
        <property name="jdbcUrl" value="${jdbc.url}" /> 
        <property name="user" value="${jdbc.username}" /> 
        <property name="password" value="${jdbc.password}" /> 
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}" /> 
        <property name="minPoolSize" value="${c3p0.minPoolSize}" /> 
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}" /> 
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}" /> 
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}" /> 
        <property name="maxStatements" value="${c3p0.maxStatements}" /> 
    </bean> 
   
    <!-- 把数据源通过spring注入mybatis的sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

<!-- ScanMapperFiles,自动扫描所有Mapper映射文件 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bigplayer.test.mapper" />
</bean>
 
  <!-- 事务处理管理器 ,事务管理器指定的 DataSource 必须和用来创建 SqlSessionFactoryBean 的 是同一个数据源,否则事务管理器就无法工作了-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>

<!-- 定义事务的管理者 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 定义使用事务的方法特征行为 -->
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" read-only="false"
                            rollback-for="java.lang.Exception" no-rollback-for="java.lang.RuntimeException"/>
      <tx:method name="insert*" propagation="REQUIRED" read-only="false"
                            rollback-for="java.lang.RuntimeException" />
<tx:method name="update*" propagation="REQUIRED" read-only="false"
                            rollback-for="java.lang.Exception" />
     
<tx:method name="find*" propagation="SUPPORTS"/>
<tx:method name="get*" propagation="SUPPORTS"/>
<tx:method name="select*" propagation="SUPPORTS"/>

<tx:method name="*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice>

<!-- 配置事务切入点 -->
<aop:config>
<!--把事务控制在Service层-->
<aop:pointcut id="bussinessService"
expression="execution(public
* com.bigplayer.test.service.*.*(..))" />
<!-- 指定bussinessService切入点应用txAdvice处理器,即该切入点的所有符合特征的方法均具备了事务性 -->
<aop:advisor pointcut-ref="bussinessService" advice-ref="txAdvice" />
</aop:config>

注意这句<tx:method name="*" propagation="SUPPORTS" />

然后我先弄了一个TestService接口,只有一个方法testDao()
而后弄了一个UserInfoService接口
public abstract void insertUserRegister(UserInfo userInfo);
public abstract UserInfo selectUserLogin(Integer id);
public abstract List<UserInfo> selectAllUser();
public abstract void updateUserInfo(UserInfo userInfo);

然后分别实现了TestService接口和UserInfoService接口,其中UserInfoService接口的实现类UserInfoServiceImpl的四个方法在独立工作时都是正常的
TestService的TestServiceImpl实现类的方法内容是这样的

private UserInfoService userInfoService;

public UserInfoService getUserInfoService() {
return userInfoService;
}

    @Resource
public void setUserInfoService(UserInfoService userInfoService) {
this.userInfoService = userInfoService;
}

@Override
public UserInfo testDao() {

UserInfo userInfo = userInfoService.selectUserLogin(1); //此时数据库时空的,查到的数据为null
if (userInfo != null) {
logger.log(Level.INFO, userInfo.getUserName());
}
else {
logger.log(Level.INFO, "不存在该数据");
}
UserInfo userInfo2 = new UserInfo();
userInfo2.setUserName("4sdzfasrtxdh56");
userInfo2.setPassword("asafdsfdf");
userInfoService.insertUserRegister(userInfo2); //插入新数据

userInfo = userInfoService.selectUserLogin(1); //再次查询
if (userInfo != null) {
logger.log(Level.INFO, userInfo.getUserName());
}
else {
logger.log(Level.INFO, "不存在该数据");
}

return null;
}

外部调用这段代码
public static void main(String[] args) {
System.out.println( "Hello Mybatis!" );

ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
TestService testService = context.getBean(TestService.class);
testService.testDao();
//logger.log(Level.INFO, testService.selectUserLogin(1).getUserName());
}

这段代码,就是简单的先查询数据库中不存在的一个数据,而后插入这个数据,再次查询这行数据
根据单独测试表明,执行插入数据库,数据库里立即就有了数据,但是,奇怪的是,selectUserLogin查不出来。
想修正这个问题也很简单,事务配置的那句<tx:method name="*" propagation="SUPPORTS" />,不传播级别由SUPPORTS改成REQUIRED立即迎刃而解。
但是我想不清楚理由,TestServiceImpl由spring生成,它内部的userInfoService肯定被事务增强过,因此插入调用时应该立即起效,单步测试证明也确实起效了,而TestService此时本身是工作在SUpport传播级别下的,为什么此时它调用了同样是SUPPORT级别的userInfoService.selectUserLogin方法,会查不出刚刚插入的数据,userInfoService.insertUserRegister本身已经被事务加强,调用结束时就应该插入完毕了。原因到底在哪里

问题补充:补充一点,通过观察sql语句发现,第二次查询的时候mybatis并没有去查数据库,而是直接从缓存里取的数据。也就是说这更像是缓存没有被更新。进一步测试表明,把insert换成update,却一切正常,能得到更新的数据
2014年5月12日 18:22

2个答案 按时间排序 按投票排序

0 0

SUPPORTS
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行
这就跟平常用的普通非事务的代码只有一点点区别了。

应该是你的插入方法和select方法 不在事物里面的 
所以没有查到

2014年5月12日 22:16
0 0

你用的是什么数据库?还有能否把UserInfoService的实现代码粘上来看看。

我这里测试是没有问题的,是可以读到刚刚插入的数据的。只不过我这里使用的是@Transactional的注解。

2014年5月12日 18:54

相关推荐

Global site tag (gtag.js) - Google Analytics