【问题标题】:@EnableTransactionManagement annotation with 2 transaction managers带有 2 个事务管理器的 @EnableTransactionManagement 注释
【发布时间】:2011-12-24 09:52:21
【问题描述】:

我使用@Configuration 注释来配置spring 而不是xml 文件。我正在使用不同的会话工厂和不同的事务管理器配置 2 个数据源。我在这里遇到了@EnableTransactionManagement 注释的问题。我在其文档中读到,

@EnableTransactionManagement 更灵活;它会回落到一个 按类型查找任何PlatformTransactionManager bean 中的 容器。因此名称可以是“txManager”、“transactionManager”或 “tm”:根本没关系。

这意味着无论我给方法起什么名字,当我有 2 个事务管理器时,它总是会搜索返回 PlatformTransactionManager 对象的方法。现在的问题是,当我测试这个类时,它给了我错误:

org.springframework.beans.factory.NoSuchBeanDefinitionException:没有定义类型为 [org.springframework.transaction.PlatformTransactionManager] 的唯一 bean:预期单个 bean,但找到了 2

我什至尝试拥有 2 个不同的配置类,但徒劳无功。在 xml 配置中,情况并非如此。我用两个<tx:annotation-driven transaction-manager="" /> 标签注册了我的两个事务管理器,它运行良好。但不能在这里对注释做同样的事情。

如果我想在Spring注解的配置类中用2个不同的事务管理器来配置2个数据源怎么办?

【问题讨论】:

  • 这可能是一个错误,因为 Spring 3.1 仍处于 beta/rc 阶段。

标签: java spring spring-3 spring-annotations


【解决方案1】:

在你的配置类中,使用@EnableTransactionManagement注解。

在这个类中定义一个事务管理器为:

    @Bean(name="txName")
    public HibernateTransactionManager txName() throws IOException{
        HibernateTransactionManager txName= new HibernateTransactionManager();
        txName.setSessionFactory(...);
        txName.setDataSource(...);
        return txName;
   }

在那里,在您执行事务性作业的类/方法中,注释如下:

@Transactional("txName")

@Transactional(value = "txName")

这就是您将名称合格的事务管理器绑定到您需要的任何地方的方式。您现在可以拥有任意数量的事务管理器,并在任何需要的地方相应地使用它。

【讨论】:

  • 它为我节省了几个小时!谢谢
  • 这很有帮助。谢谢。 :-)
【解决方案2】:

以防万一有人遇到这个问题,我找到了解决方案:

@Configuration
@EnableTransactionManagement
@DependsOn("myTxManager")
@ImportResource("classpath:applicationContext.xml")
public class AppConfig implements TransactionManagementConfigurer {

@Autowired
private PlatformTransactionManager myTxManager;

...

@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
    return this.myTxManager;
}

通过这种方式,您可以使用在 xml 配置中定义的特定 txManager。

如果您想定义在服务级别使用的 txManager,您应该从@Configuration 类中删除 @EnableTransactionManagement 注释并指定 txManager@Transactional 注释中,例如

@Service
@Transactional(value="myTxManager", readOnly = true)
public class MyServiceImpl implements MyService { ... }

【讨论】:

  • 您可以在 XML 中配置任意数量的 txManagers,并且您可以使用 @DependsOn()@Transactional() 在应用程序或服务级别上使用任何已配置的 txManagers上述注释
【解决方案3】:

来自java doc

对于那些希望在
@EnableTransactionManagement 和要使用的确切事务管理器 bean 之间建立更直接关系的用户, TransactionManagementConfigurer 回调接口可能会被实现 - 注意 implements 子句和下面的@Override-annotated 方法:

您的@Configuration 类需要实现TransactionManagementConfigurer 接口 - 实现annotationDrivenTransactionManager,它将返回对应该使用的transactionManager 的引用。

【讨论】:

  • 我也读过这个。但这里的问题是,实现的方法调用 transactionmanager 方法并返回相同的事务管理器对象,而我想在我的配置类中有 2 个事务管理器。而且由于@EnableTransactionManagement 没有看到方法名,只是看哪个是PlatformTransactionManagement 对象,它不会允许配置2 个事务管理器。
  • 只需从 annotationDrivenTransactionManager 方法返回对@EnableTransactionManagement 使用的引用
  • 这就是问题所在。将使用哪个事务管理器并不固定,因为它取决于模型对象。我有来自 2 个不同数据库的 2 个模型,但它们都同时完成了条目。现在,无论如何,数据无法插入到 1 个表中,另一个条目也应该被丢弃,因此事务回滚可以在任何地方发生。
  • 您不能对同一个请求/事务使用两个事务管理器。该设施被提供用于两个不同的请求。如果你想要一个跨越多个请求的事务,你应该使用一个 JTATransactionManager。 xml命名空间版本也不支持您现在提出的问题(tx:annotation-driven)
【解决方案4】:

我不确定您为什么使用两个 TransactionManagers 。您可以考虑通过 AbstractRoutingDataSource 对多个数据源使用相同的 TransactionManager。请参考

http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/

关于它的使用示例。

【讨论】:

  • 例如分离 TransactionManager DB 和 JMS。
【解决方案5】:

我必须在一个项目中使用 JPA 和 Reactive Mongo。最后起作用的是:

  • 创建@Configuraition 类以显式创建JPA 事务管理器,例如here
    private Environment env;
        @Bean
        @Primary
        public LocalContainerEntityManagerFactoryBean dbEntityManager() {
            LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
            em.setDataSource(dbDatasource());
            em.setPackagesToScan(new String[]{"projectone.mysql"});
            em.setPersistenceUnitName("dbEntityManager");
            HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
            em.setJpaVendorAdapter(vendorAdapter);

            HashMap<String, Object> properties = new HashMap<>();

            properties.put("hibernate.dialect",env.getProperty("hibernate.dialect"));
            properties.put("hibernate.show-sql",env.getProperty("jdbc.show-sql"));


            em.setJpaPropertyMap(properties);
            return em;
        }
        @Primary
        @Bean
        public DataSource dbDatasource() {
            DriverManagerDataSource dataSource
                    = new DriverManagerDataSource();
            dataSource.setDriverClassName(
                    env.getProperty("spring.datasource.driverClassName"));
            dataSource.setUrl(env.getProperty("spring.datasource.url"));
            dataSource.setUsername(env.getProperty("spring.datasource.username"));
            dataSource.setPassword(env.getProperty("spring.datasource.password"));
            return dataSource;
        }
        @Primary
        @Bean
        public PlatformTransactionManager jpaTransactionManager() {
            JpaTransactionManager transactionManager
                    = new JpaTransactionManager();
            transactionManager.setEntityManagerFactory(
                    dbEntityManager().getObject());
            return transactionManager;
        }

}

注意 bean 名称 jpaTransactionManager,这将是 JPA 中使用的 txManager 名称 @Transactional

  • 创建 MongoConfiguration 以显式创建 Mongo 事务管理器(需要定义很多 bean)
  • @Transactional,用名字称呼他们。默认的transactionManger 将不起作用。您必须区分,例如 jpaTransactionManagerreactiveMongoTransactionManger
@Transactional(value="jpaTransactionManager")
public void xxx() {
    ...
}

请注意,JPA 事务方法不能将 Reactor 类型作为返回值(Mono/Flux)。 Spring 会强制返回 Mono/Flux 的方法使用 ReactiveTransactionManager,这会造成混乱。

【讨论】:

    【解决方案6】:

    其他一些答案暗示使用两个事务管理器在某种程度上是错误的;但是,Spring 的 XML 配置允许使用在线文档(如下)中所述的多个事务管理器。不幸的是,似乎没有办法让@EnableTransactionManagement 注释以类似的方式工作。因此,我只需使用@ImportResource 注释来加载包含&lt;tx:annotation-driven/&gt; 行的XML 文件。这允许您为大多数事情获取 Java 配置,但仍然可以使用 @Transactional 和可选的事务管理器限定符。

    http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/transaction.html

    大多数 Spring 应用程序只需要一个事务管理器,但在某些情况下,您可能需要在单个应用程序中使用多个独立的事务管理器。 @Transactional 注释的 value 属性可用于选择性地指定要使用的 PlatformTransactionManager 的标识。这可以是 bean 名称或事务管理器 bean 的限定符值。例如,使用限定符表示法,以下 Java 代码

    【讨论】:

      【解决方案7】:

      尝试使用chained TransactionalManager

      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.data.transaction.ChainedTransactionManager;
      import org.springframework.transaction.PlatformTransactionManager;
      
      @Configuration
      public class ChainedDBConfig {
      
          @Bean("chainedTransactionManager")
          public PlatformTransactionManager transactionManager(
                  @Qualifier("database1TransactionManager") final PlatformTransactionManager db1PlatformTransactionManager,
                  @Qualifier("database2TransactionManager") final PlatformTransactionManager db2PlatformTransactionManager) {
      
              return new ChainedTransactionManager(db1PlatformTransactionManager, db2PlatformTransactionManager);
          }
      
      }
      

      并在您的服务类上放置以下注释:

      @Transactional(transactionManager = "chainedTransactionManager")
      public class AggregateMessagesJobIntegrationTest {
         ...
      }
      

      您也可以在集成测试中使用它:

      @RunWith(SpringRunner.class)
      @Transactional(transactionManager = "chainedRawAndAggregatedTransactionManager")
      @Rollback
      public class ExampleIntegrationTest extends AbstractIntegrationTest {
          ....
      }
      

      它会对两个数据库事务管理器进行回滚。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-20
        • 1970-01-01
        • 2016-01-29
        • 2023-02-16
        • 1970-01-01
        • 1970-01-01
        • 2019-01-09
        • 1970-01-01
        相关资源
        最近更新 更多