Java主动让子线程回滚_一次子线程事务回滚实践笔记-编程式事务

(一)问题的引出、主要解决手段

在线程中使用 batchupdate ,中的每一条记录都会自动的commit(但仍使用一个数据库连接会话,有点像hibernate一级缓存的概念,多个事务,一个会话),如果有异常,则只有异常的数据执行失败,其他数据不会rollback,并且后续的数据可以继续执行

业务中这样导致多线程任务异常数据的捕捉十分不易,必须使batchupdate批次有一个失败,就全部失败,然后打印日志,重爬该批次数据。

而线程作为非spring托管类,无法直接使用声明式事务解决

作者使用编程式事务解决了batchupdate的事务控制,只要有一次exception,则所有的数据都rollback,commit 时会一次把所有的数据 提交

选自:jdbctemplate batchupdate 的事务管理  http://blog.csdn.net/huijianpang/article/details/44780385

(二)我这里用了另一篇文文章的代码

问题描述:

在Spring的web项目中,查询了多行数据,对这些数据遍历处理,并对每一条数据采取线程的方式去执行,方式如下:

48304ba5e6f9fe08f3fa1abda7d326ab.png1 new Thread(newRunnable() {2   @Override3   public voidrun() {4   try{5 processEachPlan(learn); // 处理逐条数据6   } catch(Exception e) {7   Logger.info("异常信息:" +e.toString());8 }9   }10 }).start();

48304ba5e6f9fe08f3fa1abda7d326ab.png

问题在于run(){}方法中的processEachPlan(learn)不受声明式事务管理了,但是我的需求是让每一个processEachPlan(learn)都在各自的事务中管理,这样能够保证逐条处理的数据的完整性。

解决方案:

我的想法是能否自己控制事务,解决方式如下:

48304ba5e6f9fe08f3fa1abda7d326ab.png1 new Thread(newRunnable() {2   @Override3   public voidrun() {4     //spring无法处理thread的事务,声明式事务无效

5     DefaultTransactionDefinition def = newDefaultTransactionDefinition();6 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);7   PlatformTransactionManager txManager = ContextLoader.getCurrentWebApplicationContext().getBean(PlatformTransactionManager.class);8   TransactionStatus status =txManager.getTransaction(def);9

10   try{11 processEachPlan(learn);12   txManager.commit(status); //提交事务

13   } catch(Exception e) {14   Logger.info("异常信息:" +e.toString());15   txManager.rollback(status); //回滚事务

16 }17   }18}).start();

48304ba5e6f9fe08f3fa1abda7d326ab.png

如上代码中,大概意思就是获取了spring配置中的bean,以及相关定义来开启事务,processEachPlan(learn)如果执行成功,那么commit()提交事务,如果出现异常,那么rollback()回滚。在我的项目中,测试是成功的。

我这里也成功了

(三)其中有个小插曲,我的

spring boot环境 ContextLoader.getCurrentWebApplicationContext()返回null

参考了这个帖子:http://www.oschina.net/question/2416168_2189114

springboot中,ContextLoader.getCurrentWebApplicationContext()获取的为Null

中,

另外推荐使用ApplicationContextAware的方式获取ApplicationContext,这样对非web及web环境都有很好的支持,我的工程这样写的:

@Component@Lazy(false) public classApplicationContextRegisterimplementsApplicationContextAware{ private static finalLogger LOGGER= LoggerFactory.getLogger(ApplicationContextRegister.class);private staticApplicationContext APPLICATION_CONTEXT;/*** 设置spring上下文**@paramapplicationContextspring上下文*@throwsBeansException*/@Overridepublic voidsetApplicationContext(ApplicationContext applicationContext)throwsBeansException{ LOGGER.debug("ApplicationContext registed-->{}",applicationContext);APPLICATION_CONTEXT= applicationContext;} public staticApplicationContextgetApplicationContext(){ returnAPPLICATION_CONTEXT;}

}

想起来,以前解决过提取spring boot环境 ApplicationContext的问题:

最终,DefaultTransactionDefinition def = new DefaultTransactionDefinition();

def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

PlatformTransactionManager txManager = SpringUtil.getBean(PlatformTransactionManager.class);

TransactionStatus status = txManager.getTransaction(def);@Component

public class SpringUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext = null;

// 非@import显式注入,@Component是必须的,且该类必须与main同包或子包

// 若非同包或子包,则需手动import 注入,有没有@Component都一样

// 可复制到Test同包测试

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

if(SpringUtil.applicationContext == null){

SpringUtil.applicationContext = applicationContext;

}

System.out.println("---------------com.ilex.jiutou.util.Test.Main.SubPackage.SpringUtil---------------");

}

//获取applicationContext

public static ApplicationContext getApplicationContext() {

return applicationContext;

}

//通过name获取 Bean.

public static Object getBean(String name){

return getApplicationContext().getBean(name);

}

//通过class获取Bean.

public static T getBean(Class clazz){

return getApplicationContext().getBean(clazz);

}

//通过name,以及Clazz返回指定的Bean

public static T getBean(String name,Class clazz){

return getApplicationContext().getBean(name, clazz);

}

}

done

(四)扩展:编程式事务和声明式事务

编程式事务:

1)PlatformTransactionManager——本文采用

2)使用TransactionTemplate

声明式事务:

1)@Transactional

2) & aop

(五)3.2出现问题:Unable to fetch a connection in 30 seconds, none available[size:100; busy

参考:

查下来是hibernate连接池爆掉了,原因是事务未提交

txManager.commit(status);

注意编程式事务要显示提交

你可能感兴趣的:(Java主动让子线程回滚)