如何在Spring中配置事务管理以处理使用2个不同数据库的情况?

37

我有两个数据库(MySql和HSQLDB)。我配置了两个数据源和两个EntityManagerFactory bean。我还可以配置两个对应的JpaTransactionManager bean。

但是我不知道如何指定哪一个应该用于管理具体服务类的事务。我想使用@Transactional注释来实现这个目的,但我只能指定其中一个txManager:

<tx:annotation-driven transaction-manager="manager"/>

如何走出这种境地?


https://dev59.com/zHI-5IYBdhLWcg3wN1lD - Bozho
4个回答

19

声明您的<tx:annotation-driven>时不要使用transaction-manager属性,像这样为事务管理器声明限定符:

<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <qualifier value="txManager1"/>
</bean>

@Transactional中使用这个限定符作为value,以选择其中一个事务管理器:

@Transactional("txManager1")
或者,带有更多属性:
@Transactional(value = "txManager1", readOnly = true)   

1
这个解决方案是可行的。但是,如果您在@Transactional注释上有限定符,您必须使用配置了限定符元素的事务管理器来指定它。起初我以为默认的事务管理器会接管,但对我来说并非如此。它抱怨我没有在我的事务管理器bean上配置适当的限定符。 - Jorge
1
请注意,此适用于Spring 3及以上版本。 - java25

19

JpaTransactionManager的javadoc给出了一些建议:

这个事务管理器适用于使用单个JPA EntityManagerFactory进行事务数据访问的应用程序。在同一事务中访问多个事务资源时,需要使用JTA(通常通过JtaTransactionManager)。请注意,您需要相应地配置JPA提供程序,以便使其参与JTA事务。

换句话说,如果您有多个实体管理器,并且对应有多个tx管理器,则应考虑改用单个JtaTransactionManager。实体管理器应能够参与JTA事务,这将使您在两个实体管理器之间获得完整的事务性,而无需担心当前所在的实体管理器。

当然,JtaTransactionManager需要完全支持JTA的应用服务器,而不是像Tomcat这样的普通servlet引擎。


我可能没有理解清楚,但是我认为在Spring中使用的事务管理器基于数据库事务管理器,也就是说我们应该针对使用的每个不同的数据库创建一个tx-manager。这是错误的吗? - Roman
3
并不是那么简单。Spring TX 管理器只是对其他现有机制(如 JTA、JPA/Hibernate 或 JDBC 事务)的包装器。JTA 不仅适用于一个数据库,还是一个分布式多数据库事务管理器。使用 Spring 并没有真正改变底层机制的使用方式,只是使其更容易。 - skaffman
1
谢谢,这对我来说有点出乎意料,我会在Sun文档中寻找它。 - Roman
1
如果我在一个方法上使用两个@Transactional()注解,例如:@Transactional(transactionManager = "customerTransactionManager"), @Transactional(transactionManager = "orderTransactionManager"),会发生什么?我想知道如何处理一个方法中的两个数据库事务。 - Radu Toader

13

自从正确答案以来已经有很长时间了。

在使用JpaTransactionManager管理多个数据库方面,Skaffman的可用性可能是正确的。

但是,对于使用两个不同的JpaTransactionManager和两个不同的数据库存在解决方案。

  @Bean(name = "db2TransactionManager")
  public PlatformTransactionManager transactionManager2() throws NamingException {
    JpaTransactionManager txManager = new JpaTransactionManager(entityManagerFactory());
    return txManager;
  }

  @Bean
  @Primary
  public PlatformTransactionManager transactionManager() throws Exception {
     JpaTransactionManager txManager = new JpaTransactionManager(entityManagerFactory());
    txManager.setNestedTransactionAllowed(true);
    return txManager;
  }

@Primary 应该用于在 @Transactional 中不指定限定符名称的情况下。


6

您需要在application-context.xml文件中指定两个事务管理器如下:

<tx:annotation-driven transaction-manager="manager1"/>
<tx:annotation-driven transaction-manager="manager2"/>

@Transactional属性现在将使用其相关的事务管理器。


这对我起作用了,我不必在txManager bean或@Transactional注释中指定限定符。我还能够访问两个不同的session工厂。 - jonnysamps
是的,我也一样。我在使用 @Transactional 注解时也不需要指定限定符。 - Mital Pritmani
2
有趣的是,如果我查看处理 tx:annotation-drivenAnnotationDrivenBeanDefinitionParser,所有操作都只在每个上下文中执行一次!因此第二行完全没有用。 - Arne Burmeister
这对我很有用!可惜Spring官方文档没有提到设置多个DataSourceTransactionManager的技巧。 - tonga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接