spring事务在多库下的处理

这一章主要介绍,分布式事务的雏形-spring事务在多库下的处理。这个算是比较可贵而且很容易在开发过程当中遇到各类坑。介绍以前先讲一下问题的原因,前一阵,一个朋友说他在作一个业务,须要先从A库捞取一些数据,而后再B库里面根据对A库数据的处理来决定是否插入一条数据,在测试的时候测了回滚的状况,可是死活回滚不成功,后来在架构师的帮助下解决了。解决方法是重新配置一个事务管理器,这个东西引发了个人注意,由于我以为若是在开事务的时候先切数据源应该不会这么麻烦,带着这个问题来探讨。spring

要清楚地看到问题,就必须看一下spring怎么保证从事务中获取的连接是同一个。核心其实就是一个线程局部变量,而后放了一个map,map的key : datasource,value:connection包装类,至于这么作的缘由:spring保证多个数据源依然能够在事务中准确获取连接。sql

那回到那个问题,切数据源为何回滚无效?假设不在@transactional(value="xxx")配置事务管理器,那么事务管理器一开始会用的默认数据源。相似下图。那么此时在取连接的时候,会看下当前是否存在事务,不存在直接提交,若是存在,那么会拿到datasource,从map中取连接,可是很遗憾,此时的数据源已经不是事务管理器中的那个数据源了,因此取不到。若是配置了事务管理器,那么一切就好办了。编程

bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 数据源
    dataSource在applicationContext-dao.xml中配置了
     -->
    <property name="dataSource" ref="dataSource"/>
</bean>

 

带着这个问题,假设场景变一下,有A库和B库,在A,B库同时插入数据,有一个出现异常就会滚,怎么解决?解决方案看似不少,可是都和事务管理器有关。在探讨这个问题的时候也让我一直存在疑惑的究竟是用mybatis的接口好仍是原生dao好,在解决分布式事务问题,最好用编程式事务+原生dao,否则好多错根本跟踪不到,直接上代码。session

public class CommonSqlsession {
    private SqlSession sqlSession;

    @Resource(name = "sqlSessionFactory")
    public final void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }

    public final SqlSession getSqlSession() {
        return this.sqlSession;
    }
}
@Repository
public class UserDaoImpl extends CommonSqlsession implements UserDao {
    public int insert(User user) throws Exception {
        return getSqlSession().insert("com.elin4it.ssm.mapper.mybatis.UserMapper.insert", user);
    }
}
@Repository("seoFundDao")
public class SeoFundDaoImpl extends CommonSqlsession1 implements SeoFundDao {
    public int insert(TbFundSeoRecord seoRecord) throws Exception {
        return getSqlSession().insert("com.elin4it.ssm.mapper.test.TbFundSeoRecordMapper.insert", seoRecord);
    }
}
@Resource
private DataSourceTransactionManager transactionManager;

@Resource
private DataSourceTransactionManager transactionManager1;
/**
     * @param record
     * @param user
     * @return
     */
    public int insertFacade1(TbFundSeoRecord record, User user) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        TransactionStatus status = transactionManager.getTransaction(def);
        TransactionStatus status1 = transactionManager1.getTransaction(def);

        try {
            int count1 = userDao.insert(user);
//            int i = 1 / 0;
            int count2 = seoFundDao.insert(record);

            if (count1 > 0 && count2 > 0) {
                transactionManager.commit(status);
                transactionManager1.commit(status1);
            } else {
                throw new Exception("有一个未提交成功 count1=" + count1 + ",count2=" + count2);
            }
        } catch (Exception e) {
            e.printStackTrace();
            /**
             * spring提交事务按照stack排序,先入后出
             */
            transactionManager1.rollback(status1);
            transactionManager.rollback(status);
        }

        return 0;
    }

总的思路是这样的,两个事务管理器,而后从不一样数据源取session,这样spring在处理事务的时候会创建一个map在ThreadLocal中,而后设置两个key,分别为ds1 : con1,ds2:con2,可是这真的是能够实现分布式事务吗?mybatis

经过上面两个图,能够发现,若是在commit或者rollback的时候出问题,假设第一个事务提交成功,第二个事务提交的时候宕机了,那么就会致使第一个库中数据落地,第二个库没有数据。虽然commit很快,可是依然无法100%保证分布式事务提交。因此真的在工做中要操做多库的提交一致性仍是借助JTA这种分布式框架利用2PC来解决吧或者经过业务分类,保证一致性提交的表都在一个库中。架构

相关文章
相关标签/搜索