Spring事务与自定义多线程陷阱

        场景:Spring+Ibatis环境,使用spring aop事务(配置到service层),在一个service方法中,自定义了一个多线程,结果事务不起作用了,不用线程,则事务有效。

        原因:Spring的事务是通过ThreadLocal来保证线程安全的,事务和当前线程绑定,所以自己开了多线程自然会让事务失效。

        Spring的事务管理器是通过ThreadLocal来保存每个线程的副本,从而实现线程安全的,再结合IoC和Aop实现高级声明式事务的功能,所以Spring的事务天然地和线程有着千丝万缕的联系。只能维护web应用的多线程,不支持多线程里的多线程。

        其他方案:修改代码架构,把逻辑处理部分抽出来,放在另外一个service中,然后通过xxx.service的方法去调用(在事务范围外做的线程操作),这样就有了事务。

        应用场景:对历史数据进行迭代处理,处理完成一条就添加到数据库,不成功则抛出异常(如果不使用多线程则可以做到一批数据要么全部成功,有一个失败就全部回滚)。


        代码片段如下:

        代码一ReclaimMatchSubscriptionServiceImpl、

private void saveHistoryMatches(final MatchedInfoParams params, final CommonTaskName taskName, final List<Integer> matchIds) {
    new Thread() {
        @Override
        public void run() {
            MatchedInfoParams clonedParams = params.clone();
            for (final Integer matchId : matchIds) {
                try {
                    clonedParams.setMatchId(matchId);
                    saveWorkingTask(clonedParams, taskName, TaskPriority.LOW);
                } catch (Exception e) {
                    logger.error("Save history matches to working task failed: matchedId=" + matchId + ", companyId=" + params.getCompanyId() + ", taskName=" + taskName
                            + ", matchType=" + params.getMatchType(), e);
                }
            }
        }
    }.start();
}

public void saveWorkingTask(T params, CommonTaskName taskName, TaskPriority priority) {
    Assert.notNull(params, "CommonTaskParams must not be null");
    Assert.notNull(taskName, "CommonTaskName must not be null");
    Assert.notNull(priority, "TaskPriority must not be null");

    if (isServiceToDeal(taskName)) {
        String uniqueKey = new MD5().MD5(uniqueKey(params));
        WorkingCommonTask oldTask = commonTaskService.getWorkingTaskByNameAndKey(taskName, uniqueKey);
        if (isAddToWorkingTask(oldTask)) {
			WorkingCommonTask task = new WorkingCommonTask();
			task.setCompanyId(params.getCompanyId());
			task.setTaskName(taskName.getCode());
			task.setUniqueKey(uniqueKey);
			task.setRetrievalField(retrievalField(params));
			task.setRetryCount(0);
			task.setTaskPriority(priority.getCode());
			task.setTaskStatus(CommonTaskStatus.CREATED.getCode());
			task.setTimeout(0);
			commonTaskService.saveWorkingTask(task, getBiz(params));
		}
	}
}


        代码二CommonTaskServiceImpl、

public int saveWorkingTask(WorkingCommonTask workingTask, String bizData) {
        Assert.notNull(workingTask, "workingTask must not be null");
        Assert.notNull(bizData, "bizData must not be null");

        if (workingTask.getTimeout() <= 0) {
            workingTask.setTimeout(timeout);
        }

        int taskId = commonTaskDao.saveWorkingTask(workingTask);

        CommonTaskExtraInfo taskExtraInfo = new CommonTaskExtraInfo();
        taskExtraInfo.setTaskId(taskId);
        taskExtraInfo.setBizData(bizData);
        commonTaskDao.saveTaskExtraInfo(taskExtraInfo);

        commonTaskDao.saveTaskLog(new CommonTaskLog(taskId, workingTask.getTaskStatus(), workingTask.getExtraInfo()));

        return taskId;
    }

另外,对于第一段代码事务失效,还有另外一种解释:

public class A {
    @annotation
    public B () {
       
    }

    public C () {
      B();
    }

}

当一个类中,方法B上面有注解(譬如:事务注解),方法B被方法C调用,当其他方法调用方法C的时候方法B上面的注解是无效的!

参与评论 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

thjnemo

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值