zoukankan      html  css  js  c++  java
  • 一次子线程事务回滚实践笔记-编程式事务

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

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

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

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

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

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



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

    Spring的@Transactional事务无法处理thread线程的解决方案

    问题描述:

    在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的问题:

    spring boot 的 ApplicationContext 及 getbean

    最终,

            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> T getBean(Class<T> clazz){
            return getApplicationContext().getBean(clazz);
        }
    
        //通过name,以及Clazz返回指定的Bean
        public static <T> T getBean(String name,Class<T> clazz){
            return getApplicationContext().getBean(name, clazz);
        }
    
    }

    done


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

    Spring事务管理实现方式之编程式事务与声明式事务详解

    编程式事务:

    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数据库连接池爆满的原因及源码分析

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

    txManager.commit(status);

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

  • 相关阅读:
    ruby2.2.2在msvc2010上编译
    msvc2010生成的指令序列有问题,可能跟pgo有关
    Could not load file or assembly 'Oracle.DataAccess' or one of its dependencies. An attempt was made to load a program with an incorrect format.
    正则总结
    实现点击按钮全选功能
    轮播图(省代码方法)
    js实现复制URL功能
    字符串的常用方法
    两种文件上传的实现-Ajax和form+iframe
    jquery中attr和prop的区别
  • 原文地址:https://www.cnblogs.com/silyvin/p/9106647.html
Copyright © 2011-2022 走看看