基于spring声明事务“不回滚”问题解决。



由于工作需要需要把数据保存不同的数据库表,如果某一环节出现问题对应表中数据都全部回滚,结果测试过程发现方法内报

ApplicationException(自定义异常继承了Exception) 异常时b,c表数据都回滚,但是唯有a表所仍然提交了。

相关事务定义也配置

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Transactional(value="sptWarehouseTxManager", 
isolation = Isolation.REPEATABLE_READ, 
propagation = Propagation.REQUIRED, 
rollbackFor = Exception.class)
public @interface WarehouseTransactional{

}
 
 
<beans
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="       
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
      http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      	
	<context:annotation-config/>

	<!-- 监听器 -->
	<bean id="warehouseStartupListener" class="com.autrade.spt.warehouse.listener.WarehouseStartupListener"/>

	<!-- SptWarehouse 数据源 -->
    <bean id="dataSource" parent="parentDataSource" init-method="init" destroy-method="close">    
        <property name="url" value="${jdbc.url}" />  
        <property name="username" value="${jdbc.username}" />  
        <property name="password" value="${jdbc.password}" />    
    </bean>
    
	<!-- MyBatis -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	  	<property name="dataSource" ref="dataSource" />
	  	<property name="configLocation" value="classpath:resources/mybatis/warehouse/myBatisConfig.xml" />
	  	<property name="mapperLocations" value="classpath:resources/mybatis/warehouse/mapper/*.xml"/>
	</bean>
	<bean id="warehouseSqlSession" class="org.mybatis.spring.SqlSessionTemplate"
		autowire="byName">
		<constructor-arg ref="sqlSessionFactory" />
	</bean>
	<!-- MyBatis -->
	
	<!-- 配置事务管理对象-->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
		<qualifier value="sptWarehouseTxManager"/>
	</bean>
	<!-- 将所有具有@Transactional注解的Bean自动配置为声明式事务支持 -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
</beans>

/*Zw申请实现   ServiceA
 * @author krcis
 * @date   2018年6月4日
 * <p></p>
 * <p>描述</p>
 */
public class ZwApplyService extends WhServiceBase implements IZwApplyService {
	private static final Logger logger = LoggerFactory.getLogger(ZwApplyService.class);
	@Autowired
    private IZwApplyDao zwApplyDao;
	@Autowired
	private IWhReceiptService whReceiptService;
	@Override
	@WarehouseTransactional
	public zwRespEntity zwApproved(zwApproveUpEntity upEntity) throws ApplicationException, DBException {
		  zwRespEntity resp = new zwRespEntity();
		   zwRecordQueryUpEntity whRecordUpEntity = new zwRecordQueryUpEntity();
			TblwhReceiptMasterEntity whReceiptEntity = new TblwhReceiptMasterEntity();
			 TblzwApplyEntity zwApplyUpEntity = new TblzwApplyEntity();
				 whRecordUpEntity.setStatus(zwRecordStatus.P.toString());//仅查询未入本地仓单申请
				 PagesDownResultEntity<TblzwRecrodEntity>  whRecordList = zwApplyDao.findzwRecordList(whRecordUpEntity);
						 while(null != whRecordList && whRecordList.getDataList().size()>0){
							for(TblzwRecrodEntity whRecord :whRecordList.getDataList()){
								whReceiptEntity = new TblwhReceiptMasterEntity();
								whReceiptEntity.setBlockNumber(BigDecimal.ZERO);
								BeanUtils.copyProperties(whRecord, whReceiptEntity);
								//step1:转为系统内部仓单 表A
								whReceiptService.saveWharehouse(whReceiptEntity);//事务”未回滚“
								
								//step2:更新仓单申请明细状态为 ’已映射本地仓单‘ 表B

								whRecord.setStatus(zwRecordStatus.D.toString());
								zwApplyDao.updatezwRecord(whRecord);//事务回滚
								 whRecordUpEntity.setPageNo(whRecordUpEntity.getPageNo()+1);
								 whRecordList = zwApplyDao.findzwRecordList(whRecordUpEntity);
							  }
						}
						
						 zwApplyUpEntity.setApprovedStatus(zwApplyStatus.D.toString());
						 //step3:更新仓单申请状态为已审核通过 表C
						 zwApplyDao.updatezwApply(zwApplyUpEntity);//事务回滚
						
						 //step4:调用远程审核通过
						  resp = 	RemotePostUtil.doPost("/trade/service/approve", upEntity, zwRespEntity.class);
						  logger.info("审核结果{}",JsonUtility.toJSONString(resp)); 
						
			
                          throw new ApplicationException("测试事务回流");

			 return resp;
	}
		
}
 
 
/**
serviceB
**/
public class WhReceiptService  extends WarehouseServiceBase implements IWhReceiptService  {
	private static final Logger logger = LoggerFactory.getLogger(WhReceiptService.class);
	@Autowired
	private IWhReceiptDao whReceiptDao;

	@Override
	@ServiceExceptionAop
	@WarehouseTransactional
	public void saveWharehouse(TblwhReceiptMasterEntity entity)
			throws Exception {
		  String  warehouseId = getNextKey(KeySequenceId.KEYSEQ_WAREHOUSEID);
		  entity.setWarehouseId(warehouseId);
		//保存记录
		  whReceiptDao.insert(entity);
		
	}

	
}

通过跟踪源码发现执行    serviceB.saveWharehouse时事务是正常开启,当执行到

throw new ApplicationException("测试事务回流");  当前异常类型是符件条件的(定义的为Exception是事务回滚,见事务定义)事务同样是回滚的。

详见下图:


//校验事务回流异常规则:


//满足条件继续执行jdbc  事务回滚


//执行数据库事务回滚



通过上面调试结果可以知道的是事务其实是有回滚的,但是为什么其他两个B,C表中数据被回滚条,唯有A表数据仍然能插入了数据库中呢,这个问题让人百思不知其解。理论上代码上应该不会有问题,后来和另外一个同事讨论怀疑是数据库表的问题。

后来看了A表的引擎是MyISAM(之前A表被删除过,又被重建过),而B、C表则是InnoDB。

问题就是出现在这。 由于【MyISAM是非事务安全型的,不支持事务,而InnoDB是事务安全型的(支持事务处理等高级处理)】

 网上看了别人也遇到过同样的问题,真的没想到这个问题,之前创建的表一直设置在innoDB。现在总算找到问题了,其实我所遇到的并不是事务没回滚而是表引擎导致(mysql)


问题总结:

1.如果事务不回滚可以确认一下【 rollbackFor 】配置的异常类型,默认为RuntimeException类型。可以通过配置设置异常类型,设置某种类型才触发事务回滚,同时也可以设置[noRollbackFor]设置默种异常不回滚

2.如果使用的是mysql 的一定要注同数据库表的引擎要设置成[InnoDB],默认为;MyISAM.




猜你喜欢

转载自blog.csdn.net/youaremy1bestlove/article/details/80594651
今日推荐