`
45808916
  • 浏览: 5711 次
文章分类
社区版块
存档分类
最新评论

Spring中实现多数据源事务管理

 
阅读更多

Spring中实现多数据源事务管理

前言

由于项目中引入了多个数据源,并且需要对多个数据源进行写操作,那么多数据源的事务管理自然成了不可避免的问题,这也让我对@Transactional注解有了进一步的理解(但实际上也并不是非常深入)

然而这是一个演进的过程,刚开始项目中并没有使用@Transactional指定具体的TransactionManager,所以新增一个数据源后,对原有的事务产生了影响了,这也是偶尔在一次测试报错而结果没有回滚之后才发现的,遂对于@Transactional注解的一些参数项进行了了解。

研究

由于容器中存在两个TransactionManager,那么被@Transactional注解的方法到底使用了哪个TransactionManager来进行事务管理,抑或是同时使用了两个TransactionManager来进行事务管理都是我们需要搞清楚的问题。
首先我们先看看@Transactional注解上有没有提供配置项来指定TransactionManager,果不其然,发现value属性就是用来指定具体TransactionManager的,通过id或者name来指定唯一一个TransactionManager,那么对于只需要一个事务管理的方法,问题就简单多了:

    @Transactional(value = "database2TransactionManager")
    public void test(String a) {
        // business operation
    }

关于不指定TransactionManager时会使用哪一个TransactionManager,有兴趣的童鞋可以参考另一篇文章,讲的比较清晰:http://blog.sina.com.cn/s/blog_8f61307b0100ynfb.html

好了,回到我们研究的问题,那么对于需要写入多个数据源的业务方法该怎么办呢?

进一步研究

看来@Transactional是没有提供这种功能了,那么就自己写了一个吧。我记得Spring中的事务管理分编程式事务和声明式事务。我们平时使用的@Transactional就是声明式事务,它的好处其实也就是灵活度更高、代码的耦合性更低,最终的事务管理实现还是一样的,只不过将具体逻辑都剥离到了切面中。所以我们可以手写一个切面来写一次“编程式事务”,当然在具体应用时,还是声明式的。

Java中一般编程式事务的写法:

public class UserServiceImpl implements UserService {
    @Resource
    private TransactionManager txManager;
    @Resource
    private UserDao userDao;
    @Resource
    private AddressDao addressDao;

    public boolean saveUser(User user) {
        TransactionDefinition txDefinition = new TransactionDefinition();
        TransactionStatus txStatus = txManager.getTransaction(txDefinition);
        boolean result = false;
        try {
            result = userDao.save(user);
            if(!result){
                return false;
            }
            result = addressDao.save(user.getId(), user.getAddress());
            txManager.commit(txStatus);
        } catch (Exception e) {
            result = false;
            txManager.rollback(txStatus);
        }
        return result;
    }
}

我们借用这个逻辑将事务管理相关提取到切面中,并在进入目标方法之前,让多个TransactionManager都开启事务,并在成功执行后一并提交或失败后一并回滚,具体代码:

/**
 * @author Zhu
 * @date 2015-7-15
 * @version 0.0.1
 * @description
 */
public class MultiTransactionalAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    public Object around(ProceedingJoinPoint pjp,
            MultiTransactional multiTransactional) throws Throwable {

        Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();
        Stack<TransactionStatus> transactionStatuStack = new Stack<TransactionStatus>();

        try {

            if (!openTransaction(dataSourceTransactionManagerStack,
                    transactionStatuStack, multiTransactional)) {
                return null;
            }

            Object ret = pjp.proceed();

            commit(dataSourceTransactionManagerStack, transactionStatuStack);

            return ret;
        } catch (Throwable e) {

            rollback(dataSourceTransactionManagerStack, transactionStatuStack);

            logger.error(String.format(
                    "MultiTransactionalAspect, method:%s-%s occors error:", pjp
                            .getTarget().getClass().getSimpleName(), pjp
                            .getSignature().getName()), e);
            throw e;
        }
    }

    /**
     * @author Zhu
     * @date 2015-7-25下午7:55:46
     * @description
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     * @param values
     */
    private boolean openTransaction(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuStack,
            MultiTransactional multiTransactional) {

        String[] transactionMangerNames = multiTransactional.values();
        if (ArrayUtils.isEmpty(multiTransactional.values())) {
            return false;
        }

        for (String beanName : transactionMangerNames) {
            DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) ContextHolder
                    .getBean(beanName);
            TransactionStatus transactionStatus = dataSourceTransactionManager
                    .getTransaction(new DefaultTransactionDefinition());
            transactionStatuStack.push(transactionStatus);
            dataSourceTransactionManagerStack
                    .push(dataSourceTransactionManager);
        }
        return true;
    }

    /**
     * @author Zhu
     * @date 2015-7-25下午7:56:39
     * @description
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void commit(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuStack) {
        while (!dataSourceTransactionManagerStack.isEmpty()) {
            dataSourceTransactionManagerStack.pop().commit(
                    transactionStatuStack.pop());
        }
    }

    /**
     * @author Zhu
     * @date 2015-7-25下午7:56:42
     * @description
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void rollback(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuStack) {
        while (!dataSourceTransactionManagerStack.isEmpty()) {
            dataSourceTransactionManagerStack.pop().rollback(
                    transactionStatuStack.pop());
        }
    }

整体结构很清晰:
1. 首先根据指定的多个TransactionManager依次开启事务,这个次序不影响,因为其实大家都是平等的。
2. 其次就是调用目标方法执行具体的业务逻辑
3. 若是成功返回则提交每个事务,若中途报错,那么就回滚每个事务

其中为什么要用Stack来保存TransactionManagerTransactionStatus呢?那是因为Spring的事务处理是按照LIFO/stack behavior的方式进行的。如若顺序有误,则会报错:

java.lang.IllegalStateException: Cannot deactivate transaction synchronization - not active
        at org.springframework.transaction.support.TransactionSynchronizationManager.clearSynchronization(TransactionSynchronizationManager.java:313)
        at org.springframework.transaction.support.TransactionSynchronizationManager.clear(TransactionSynchronizationManager.java:451)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:986)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:782)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactio

题外话

刚开始碰到这个问题的时候,先想到的是分布式事务管理,也去看了JTA相关的文章,但是好像比较麻烦,而且都是一些老文章,于是想试试自己实现,最后也实现了。所以想知道JTA TransactionManager究竟有什么用呢?

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
分享到:
评论

相关推荐

    spring+jotm 多数据源事务管理(二)hibernate

    在Spring框架中,多数据源事务管理是一项关键任务,特别是在大型企业级应用中,往往需要同时操作多个数据库。本篇文章将聚焦于如何结合Spring和JOTM(Java Open Transaction Manager)来实现多数据源的事务管理,...

    Spring Boot多数据源(支持Spring声明式事务切换和回滚).pdf

    6. **多数据源事务回滚**: - 提供了跨数据源的事务回滚能力,简化了系统设计,降低了处理多数据源时的复杂性。在除了分布式事务外的大多数情况下,只需考虑这个机制,就能保证事务的正确性。 在多租户SaaS架构中...

    spring+druid+AtomikosDataSource实现多数据源切换及分布式事务控制

    AtomikosDataSource支持JTA事务,这意味着我们可以在同一个事务中操作多个数据源。Spring的`@Transactional`注解可以用来开启和管理这些分布式事务,确保事务的ACID特性。 7. **代码示例** 创建多数据源的配置...

    Springboot 动态多数据源 jta分布式事务

    本资源针对的是Spring Boot动态多数据源和JTA(Java Transaction API)分布式事务的实现,对于初学者来说非常实用。下面我们将深入探讨这些知识点。 首先,让我们了解一下Spring Boot的多数据源。在许多业务场景下...

    Spring多数据源分布式事务管理

    在大型分布式系统中,往往需要处理多个数据源,这就涉及到了Spring多数据源的配置和管理。同时,为了保证数据的一致性,分布式事务的管理也是必不可少的。在这个场景下,Atomikos作为一款开源的JTA(Java ...

    springboot实现多数据源而且加上事务不会使aop切换数据源失效

    本示例主要讲解如何使用Spring Boot结合MyBatis实现多数据源切换,并确保AOP事务管理仍然有效。 首先,我们需要配置多数据源。在Spring Boot中,可以使用`DataSource`接口的实现类,如`HikariCP`或`Druid`,创建两...

    springboot整合jta实现多数据源事务管理

    在Spring Boot应用中,整合JTA(Java Transaction API)实现多数据源事务管理是一个常见的需求,特别是在分布式系统中,为了确保数据的一致性和完整性。本文将深入探讨如何配置和使用Spring Boot与JTA来管理多个...

    spring+jotm 多数据源事务管理(三)JNDI+Tomcat

    ### Spring + JOTM 多数据源事务管理详解(三):JNDI + Tomcat 在本篇文章中,我们将深入探讨如何利用Spring框架结合JOTM(Java Open Transaction Manager)来实现多数据源下的分布式事务管理。我们将通过具体实例...

    spring 多数据源事务案例

    由于网上的多数据源事务的帖子大多是2010年以前的,现在spring都已经到4.X了,有些类已经弃用了。 原先很多都是用jotm实现的,但是由于spring的升级,totm的本地化实例那个类已经找不到了,所以我使用了atomikos。 ...

    spring boot 2多数据源,里面有hibernate和mybatis的多数据源代码

    在Spring Boot 2框架中,实现多数据源的配置是一项重要的任务,特别是在大型企业级应用中,可能需要连接到不同的数据库来满足不同业务的需求。在这个项目中,我们有两个主要的数据访问技术:Hibernate和MyBatis,...

    spring、mybatis、atomikos实现多数据源事务demo

    在IT行业中,尤其是在分布式系统和企业级应用领域,多数据源事务管理...通过这个demo,开发者可以学习到如何在Spring、MyBatis和Atomikos的环境中实现多数据源事务,这对于构建高可用、高扩展性的分布式系统至关重要。

    Springcloud 多数库 多数据源整合,查询动态切换数据库

    Spring的PlatformTransactionManager接口可以扩展以支持多数据源事务。我们可以使用`AbstractRoutingDataSource`作为基础,创建一个动态路由数据源,根据业务逻辑决定事务所涉及的数据源。 7. **数据库读写分离**:...

    spring整合mybatis多数据源

    在提供的"spring整合mybatis多数据源"的demo实例中,你可能能看到上述各个步骤的具体实现,包括配置文件、路由类、以及相关的业务代码。这个实例可以帮助你快速理解并应用到自己的项目中。 总的来说,Spring整合...

    springboot多数据源即分布式事务解决方案

    总之,SpringBoot通过其灵活的配置和强大的事务管理能力,为开发者提供了实现多数据源和分布式事务的有效工具。在实际项目中,我们需要根据具体业务场景选择合适的数据源策略和事务管理方式,确保系统的稳定性和数据...

    spring boot AOP注解方式实现多数据源

    在Spring Boot中,AOP(面向切面编程)和多数据源的整合是常见的应用场景,尤其是在大型企业级项目中,为了实现数据的隔离或者优化数据库访问,常常需要配置多个数据源。本文将深入探讨如何使用Spring Boot的AOP注解...

    Spring+Hibernate多数据源

    本示例将探讨如何在Spring和Hibernate环境中实现多数据源的配置与管理。 首先,我们来看标题"Spring+Hibernate多数据源",这意味着我们要在一个项目中同时配置和使用两个数据源。通常,这涉及到创建两个不同的...

    Spring动态切换多数据源Demo

    总的来说,"Spring动态切换多数据源Demo"是一个实战教程,旨在教你如何在Spring应用程序中实现数据源的动态切换,以及如何处理相关的配置和事务管理。通过学习这个Demo,你可以掌握在Spring环境中处理多数据库连接的...

    springboot多数据源即分布式事务解决方案,添加对多线程的支持

    本教程将深入探讨如何在Spring Boot环境下实现多数据源操作及分布式事务管理,并加入对多线程的支持。 首先,我们来理解多数据源的概念。在大型系统中,往往需要连接多个数据库,如主库、从库、测试库等。Spring ...

    SpringBoot-SpringData-多数据源

    **四、多数据源事务管理** 1. **声明事务管理器** 为每个数据源创建对应的事务管理器,例如 `PrimaryTransactionManager` 和 `SecondaryTransactionManager`,并使用 `@EnableTransactionManagement` 开启事务管理。...

Global site tag (gtag.js) - Google Analytics