博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一次子线程事务回滚实践笔记-编程式事务
阅读量:4082 次
发布时间:2019-05-25

本文共 4525 字,大约阅读时间需要 15 分钟。

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

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

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

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

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

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

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

问题描述:

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

1 new Thread(new Runnable() { 2   @Override 3   public void run() { 4      try { 5         processEachPlan(learn); // 处理逐条数据 6      } catch (Exception e) { 7         Logger.info("异常信息:" + e.toString()); 8      } 9   }10 }).start();

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

解决方案:

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

1 new Thread(new Runnable() { 2   @Override 3   public void run() { 4     // spring无法处理thread的事务,声明式事务无效 5     DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 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();

如上代码中,大概意思就是获取了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 class ApplicationContextRegister implements ApplicationContextAware { private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContextRegister.class);   private static ApplicationContext APPLICATION_CONTEXT;   /**  * 设置spring上下文  *  * @param applicationContext spring上下文  * @throws BeansException  */  @Override  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { LOGGER.debug("ApplicationContext registed-->{}", applicationContext);  APPLICATION_CONTEXT = applicationContext;  } public static ApplicationContext getApplicationContext() { return APPLICATION_CONTEXT;  }}

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

最终,

DefaultTransactionDefinition def = new DefaultTransactionDefinition();        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);        PlatformTransactionManager txManager = SpringUtil.getBean(PlatformTransactionManager.class);        TransactionStatus status = txManager.getTransaction(def);
@Componentpublic 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)<tx:advice> & aop

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

参考:

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

txManager.commit(status);

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

你可能感兴趣的文章
数据结构(七)之集合结构
查看>>
数据结构(八)之字典结构
查看>>
React 自定义Hook的思想
查看>>
React Native 无限列表的优化与实践
查看>>
React Redux常见问题总结
查看>>
前端 DSL 实践指南
查看>>
ReactNative: 自定义ReactNative API组件
查看>>
cookie
查看>>
总结vue知识体系之实用技巧
查看>>
(CI & CD)Jenkins+GitHub+Vue
查看>>
前端想要了解的Nginx
查看>>
PM2 入门
查看>>
掌握 TS 这些工具类型,让你开发事半功倍
查看>>
写一个自用的前端脚手架
查看>>
vue + typescript 项目起手式
查看>>
vue + typescript 进阶篇
查看>>
前端开发如何让持续集成/持续部署(CI/CD)跑起来
查看>>
你知道,HTTPS用的是对称加密还是非对称加密?
查看>>
vue+koa2实现最简单的注册登录功能
查看>>
前端如何搭建一个简单的脚手架
查看>>