for循环中控制事务单独提交问题

  1. #问题背景
    在项目中由于文档编号生成规则出现问题,需要将所有的记录提取出来重新生成新的文档编号,规则是根据创建日期比如2020-01-21的第一条就是编号就是“202001210001”,当天的编号持续累加。于是操作如下:
    1.将之前生成的编号全部置空
    2.根据条件提取出所有需要更新的数据
    3.for循环更新文档编号
  2. #结果
    在数据库中发现,所有数据的文档编号都是结尾都是0001,并没有出现累加的情况。
  3. #问题原因
    经过分析:按照我们的想法,应该是第一条数据生成了,第二条数据累加。但是第二条数据没有累加,说明第一条数据没有生成,并且所有的数据都是同时生成的才会出现编号结尾全部为“0001”的情况。再往后推导,那就是所有的事务都是同时提交的。也就是说循环中的所有更新操作,全部在一个事务中提交的。并不是按照我们想的那样,更新一条数据提交一条数据。为了验证猜想,去查看了项目中关于事务的配置情况如下。
	<!-- 配置事务拦截器Bean -->
	<bean id="transactionInterceptor"
		class="org.springframework.transaction.interceptor.TransactionInterceptor">
		<!-- 为事务拦截器bean注入一个事物管理器 -->
		<property name="transactionManager" ref="transactionManage"></property>
		<property name="transactionAttributes">
			<!-- 定义事务传播属性 -->
			<props>
				<prop key="insert*">PROPAGATION_REQUIRED</prop>
				<prop key="update*">PROPAGATION_REQUIRED</prop>
				<prop key="save*">PROPAGATION_REQUIRED</prop>
				<prop key="add*">PROPAGATION_REQUIRED</prop>
				<prop key="copy*">PROPAGATION_REQUIRED</prop>
				<prop key="remove*">PROPAGATION_REQUIRED</prop>
				<prop key="delete*">PROPAGATION_REQUIRED</prop>
				<prop key="create*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>

配置的是传播机制是:PROPAGATION_REQUIRED,spring的默认配置。
支持当前事务,如果当前不存在事务,则创建事务。
如果当前存在事务,则加入当前事务,合并成一个事务。

  1. #解决问题
    经过上面的分析,我们现在已经可以明确问题出在了什么地方了。也就是当前事务的传播机制导致的所有的事务合并成了一个事务提交。
    那么解决这个问题就是需要修改当前事务的传播机制。

那么让我们回顾一下spring的几种事务传播机制:

事务传播机制具体内涵
PROPAGATION_REQUIRED (默认)支持当前事务,如果当前没有事务,则新建事务如果当前存在事务,则加入当前事务,合并成一个事务
REQUIRES_NEW新建事务,如果当前存在事务,则把当前事务挂起这个方法会独立提交事务,不受调用者的事务影响,父级异常,它也是正常提交
NESTED如果当前存在事务,它将会成为父级事务的一个子事务,方法结束后并没有提交,只有等父事务结束才提交如果当前没有事务,则新建事务如果它异常,父级可以捕获它的异常而不进行回滚,正常提交但如果父级异常,它必然回滚,这就是和 REQUIRES_NEW 的区别
SUPPORTS如果当前存在事务,则加入事务如果当前不存在事务,则以非事务方式运行,这个和不写没区别
NOT_SUPPORTED以非事务方式运行如果当前存在事务,则把当前事务挂起
MANDATORY如果当前存在事务,则运行在当前事务中如果当前无事务,则抛出异常,也即父级方法必须有事务
NEVER以非事务方式运行,如果当前存在事务,则抛出异常,即父级方法必须无事务
  1. 解决方法

由上面的事务传播机制的介绍,我们需要一种在已经存在事务的时候,不加入当前事务,自己独立提交的机制。由此可以定位到:REQUIRES_NEW 这种传播机制上。

所以,我们就在更新方法入口方法上加上了Propagation.REQUIRES_NEW,代码如下:
在这里插入图片描述

public class UpdateSelfEntryDataTimmer {
	@Autowired
	SelfEntryApi selfEntryApi;
	@Autowired
    SelfEntryService selfEntryService;
	public void quartzJob() throws Exception{
		//省略部分代码
		//1.查询需要更新的记录
		String selfEntryInfoList = selfEntryApi.getSelfEntryInfoList();
		//2.处理数据成list
		List<String> strings = Arrays.asList(selfEntryInfoList.split(","));
        //3.循环更新数据
        for(String str :strings){
        	SelfEntryModel model = selfEntryApi.getSelfEntryModelBySeId(str);
        	PerfectInfo perfectInfo = model.getPerfect_info();
        	selfEntryService.saveOrUpdateSelfEntryModel(model, model.getCurrent_commitedstep());
        }
	}	
}

//在service包中,相当于controller包
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
	public ServiceResult saveOrUpdateSelfEntryModel(SelfEntryModel model,String stepStr) throws Exception{
		ServiceResult serviceResult = new ServiceResult();
		//1.保存入司人员信息
		SelfEntryModel selfEntryModel = selfEntryApi.saveOrUpdateSelfEntryModel(model,stepStr);
		//部分代码省略
		return serviceResult;
	}

重新置空文档编号,再次执行后查看数据库的文档编号,没有出现相同的问题,且都是正确编号。


版权声明:本文为qq_31542771原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。