统一Spring 和 非spring事务处理的方案

背景

1: 接手的系统中,数据库操作部分如下,下文称为ECon的方式:
    
         ECon con = null;
          try {
               con = ConMan.get("order");
               ...//do something with con    

          } catch (SQLException e) {
            if (con!=null) {
                              try {
                                   con.rollback();    
                              } catch (Exception ex) {}
                         }
          } finally {
               Closer.close(con);
          }        

为了保证事务,只能是在不同的方法中将con传来传去。 

2:由于1中的方式极其不方便。团队倾向于使用spring来处理,所以很多方法就是用spring来写
 
 
问题
  由于代码不断的在修改,就会产生ECon调用Spring写得代码、Spring调用ECon的旧代码等等,如何保证这些代码的事务一致性?
 
问题分析与解决
     事务一致性要解决的问题
     1:数据库连接Connection的一致性。在同一个request线程中,无论何时获取连接,都保证是同一个。
     解决方案:必然使用ThreadLocal来存储。要么改写ConMan.get("order") 来适配Spring,要么改写Spring来适配ConMan.get("order"). 目前倾向于Spring,因为Spring中有DataSourceUtils来取得当前事务的连接。
                     暂不下结论。继续后面的分析。
 
     2:由于ECon的代码中会catch exception回滚和 finally 关闭连接。 因为要和Spring整合在一起,互相嵌套调用,为了保证事务一致性,我们要保证内层的rollback和close没有真实的rollback和close,应该仅仅是标记。
     只有最外层的回滚或关闭,才是真实的操作。
     解决方案:这个特性和Spring的事务管理非常的像。果断翻阅Spring事务管理的源代码。
     最后得到Spring的解决方案:  
public void doLogic1(){ 
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    // explicitly setting the transaction name is something that can only be done     programmatically
    def.setName("SomeTxName");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);
    try {
        // execute your business logic here
         doLogic2();
    }
    catch (MyException ex) {
        txManager.rollback(status);
            throw ex;
    }
    txManager.commit(status);
 
}

 

public void doLogic2(){
     DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done     programmatically
    def.setName("SomeTxName");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);
    try {
         // execute your business logic2 here
     
    }
    catch (MyException ex) {
        txManager.rollback(status);
        throw ex;
    }
    txManager.commit(status);
}        

  

这个例子就是 doLogic1调用doLogic2,两个方法都显式调用rollback 和 commit。 那么他们是如何保证事务一致性的呢。 
 
最后发现基本逻辑如下:
 
   
  第一层事务开始(如doLogic1())
     TransactionStatus.isNewTransaction 返回 true
     
       第二层事务开始(如doLogic2)
               TransactionStatus.isNewTransaction 返回 false
、     第二层事务结束:
             if(TransactionStatus.isNewTransaction()){ doLogic2返回是false,所以不会真正的提交
                    status.commit()//真实的提交
             }
   第一层事务结束
        if(TransactionStatus.isNewTransaction()){ doLogic1返回的true,所有会真正的提交
                    status.commit()//真实的提交
        }

 

同理rollback 和 close,Spring会判断当前的TransactionStatus 是否是新起的事务,如果是则真实操作,如果不是则在事务管理器中标记一下,到最外层的新新起事务中进行判断操作。

Spring的事务管理方式完全满足需求。至此,问题的解决方案全部搞定。

 
编码:
1:修改EConMan:
SpringEconManager.java :
 
     public static ECon get(String dsname, TransactionDefinition definition) {
        DataSourceTransactionManager transactionManager = getTransactionManager(dsname);
        TransactionStatus status = transactionManager.getTransaction(definition);
        DataSource dataSource = transactionManager.getDataSource();
        ECon eCon = (ECon) DataSourceUtils.getConnection(dataSource);

        return SpringEcon.newSpringEcon(dsname, transactionManager, status, definition, dataSource, eCon);
    }

    /**
     * 用 SpringEconManager.close(econ), 替换 Closer.close(econ)
     *
     * @param con
     */
    public static void close(ECon con) {
        if(con == null){
            return;
        }
        if (con instanceof SpringEcon) {
            SpringEcon wrapperEcon = (SpringEcon) con;
            if (!wrapperEcon.getStatus().isCompleted()) {
                if (wrapperEcon.getStatus().isRollbackOnly()) {
                    rollback(con);
                } else {
                    commit(con);
                }
            }
            DataSourceUtils.releaseConnection(wrapperEcon.getEcon(), wrapperEcon.getDatasource());
        } else {
            Closer.close(con);
        }
    }

    /**
     * 用 SpringEconManager.rollback(econ), 替换econ.rollback()
     *
     * @param con
     */
    public static void rollback(ECon con) {
        if(con == null){
            return;
        }
        if (con instanceof SpringEcon) {
            SpringEcon wrapperEcon = (SpringEcon) con;
            TransactionStatus status = wrapperEcon.getStatus();
            wrapperEcon.getTransactionManager().rollback(status);
        } else {
            try {
                con.rollback();
            } catch (SQLException e) {
                Throwables.propagate(e);
            }
        }
    }

    /**
     * 用 SpringEconManager.commit(econ), 替换econ.commit()
     *
     * @param con
     */
    public static void commit(ECon con) {
        if(con == null){
            return;
        }
        if (con instanceof SpringEcon) {
            SpringEcon wrapperEcon = (SpringEcon) con;
            TransactionStatus status = wrapperEcon.getStatus();
            if (!status.isRollbackOnly()) {
                wrapperEcon.getTransactionManager().commit(status);
            }

        } else {
            try {
                con.commit();
            } catch (SQLException e) {
                Throwables.propagate(e);
            }
        }
    }

 

2:使用SpringEcon继承Econ,覆盖相关方法:

SpringEcon.java :
 
@Override
    public void commit() throws SQLException {
        SpringEconManager.commit(this);
    }

    @Override
    public void rollback() throws SQLException {
       SpringEconManager.rollback(this);
    }

    @Override
    public void close() {
       SpringEconManager.close(this);
    }
 

  

3:旧代码清理:
只需将出现
 con = ConMan.get("order");
改为
con = SpringEconManager.get("order");

  

 
事务测试:
 
仅展示表明思路的代码:
1: Spring 调用ECon
 
@Transactional
    public void testRollback(String name) {
        insert1(name);
        jdbcinsert2(name);
        throw new RuntimeException();
    }

    @Transactional
    public void testRollback1(String name) {
        // rollback and catch excpetion
        insert1WithRollback(name);
        jdbcinsert2(name);
    }

    @Transactional
    public void testRollback2(String name) {

        jdbcinsert2(name);
        // rollback and catch excpetion
        insert1WithRollback(name);

    }

    @Transactional
    public void testRollback3(String name) {
        jdbcinsert2(name);
        // rollback and catch excpetion
        insert1WithRollback(name);
        SpringEconManager.commit(SpringEconManager.get("order"));

    }

    @Transactional
    public void testCommit(String name) {
        insert1(name);
        jdbcinsert2(name);
    }

2: Econ调用Spring 

 
@Test
    public void testRollback() {
        ECon con = SpringEconManager.get("order");
        String name = UUID.randomUUID().toString();
        try {
            insert1(name);
            jdbcinsert2(name);
            SpringEconManager.rollback(con);

        } finally {
            SpringEconManager.close(con);
        }
        Assert.assertTrue(CollectionUtils.isEmpty(query1(name)));
        Assert.assertTrue(CollectionUtils.isEmpty(query2(name)));

    }

    @Test
    public void testRollback1() {
        ECon con = SpringEconManager.get("order");
        String name = UUID.randomUUID().toString();
        try {
            // rollback and catch excpetion
            insert1WithRollback(name);
            jdbcinsert2(name);

        } finally {
            SpringEconManager.close(con);
        }
        Assert.assertTrue(CollectionUtils.isEmpty(query1(name)));
        Assert.assertTrue(CollectionUtils.isEmpty(query2(name)));

    }

    @Test
    public void testRollback2() {
        ECon con = SpringEconManager.get("order");
        String name = UUID.randomUUID().toString();
        try {
            jdbcinsert2(name);
            // rollback and catch excpetion
            insert1WithRollback(name);
        } finally {
            SpringEconManager.close(con);
        }
        Assert.assertTrue(CollectionUtils.isEmpty(query1(name)));
        Assert.assertTrue(CollectionUtils.isEmpty(query2(name)));

    }

    @Test
    public void testRollback3() {
        ECon con = SpringEconManager.get("order");
        String name = UUID.randomUUID().toString();
        try {
            jdbcinsert2(name);
            // rollback and catch excpetion
            insert1WithRollback(name);
            SpringEconManager.commit(con);
        } finally {
            SpringEconManager.close(con);
        }
        Assert.assertTrue(CollectionUtils.isEmpty(query1(name)));
        Assert.assertTrue(CollectionUtils.isEmpty(query2(name)));

    }

                

转载于:https://www.cnblogs.com/NanguoCoffee/archive/2013/03/30/2990549.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值