【问题标题】:How to use Transactional annotation when config transactionManager conditionally in spring?在春季有条件地配置事务管理器时如何使用事务注释?
【发布时间】:2019-10-26 15:17:17
【问题描述】:

我在我的 spring 项目中使用了多个数据源。 我在项目启动时通过手动配置启用/禁用它们。 一次他们都可能是活跃的 所以 transactionManager bean 可能处于活动状态。

我在spring配置类中通过@Conditional注解实现。 当我在方法上使用禁用事务注释时,我有 NoSuchBeanDefinitionException。

当我有条件地定义 transactionManager bean 时,如何在方法上使用 transactional annotioan?

archiveTransactionManager bean 不是由 @Conditional 注释创建的,我希望 spring 跳过检查条件事务管理器的 bean 验证。

对于条件 sessionFactory bean,我将 Autowired 注释中的 'required' 参数设置为 false 以防止 spring 抛出 NoSuchBeanDefinitionException 但我应该为 @Transactional 做什么?

配置类

    @Bean("archiveTransactionManager")
    @Conditional(ArchiveCondition.class)
    public HibernateTransactionManager archiveTransactionManager() {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(archiveSessionFactory());
        return transactionManager;
    }

事务方法

    @Transactional(value = "archiveTransactionManager", readOnly = true)
    private List<DocumentItem> loadArchivedDocumentItem() {...}

用法

if(GeneralSetting.isArchive)
   documentService.loadArchivedDocumentItem();

当前结果:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'archiveTransactionManager' available: No matching PlatformTransactionManager bean found for qualifier 'archiveTransactionManager' - neither qualifier match nor bean name match!

我希望在它们不是由条件创建的某些情况下对条件 transactionManager bean 进行 spring 跳过 bean 验证

【问题讨论】:

  • 它不会跳过检查,因为它内置在拦截器中。当不应该启用一个无操作拦截器时,您应该添加一个无操作拦截器。或者不要使用 @Transactional 并在 java 中显式地编写事务表达式并使其成为有条件的。使用无操作事务管理器可能是最简单的。但是,如果没有 tx-manager 和 -session-factory 为什么要实例化这个 bean?不应该简单地忽略应用程序的整个模块/部分。
  • @M.Deinum 在某些情况下,例如在存档操作中,我设置 archive_mode 设置并运行 app.在这种模式下,一些方法会检查设置并对另一个数据源进行加号操作。我在所有模式下都有一个用于 cruds 功能的主要数据源。

标签: spring hibernate configuration spring-data spring-transactions


【解决方案1】:

我实现了一个无操作的 SessionFactory 并在 archive_mode 被禁用时在 spring 配置类中实例化它:

SpringConfig.java

    @Bean("archiveSessionFactory")
    public SessionFactory archiveSessionFactory() {
        return ServerConf.ARCHIVE_MODE ? createNormalSessionFactory() : new DummySessionFactory();
    }

    @Bean("archiveTransactionManager")
    public HibernateTransactionManager archiveTransactionManager() {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(archiveSessionFactory());
        return transactionManager;
    }

DummySessionFactory.java

public class DummySessionFactory implements SessionFactory {
...
    // These two methods call in spring initializing and we have to implement them
    @Override
    public Metamodel getMetamodel() {
       return new MetamodelImpl(null);
    }

    @Override
    public Map<String, Object> getProperties() {
       return null;
    }

    //Throw suitable exception in other methods
    @Override
    public Session getCurrentSession() throws HibernateException {
        throw new HibernateException("Desired mode is Disabled");
    }
...
}

【讨论】:

    【解决方案2】:

    我会将其视为后备方案,您必须在失败后恢复。在这种情况下,有一个方法调用方法用

    注释

    @Transactional(value = "archiveTransactionManager", readOnly = true)

    如果失败(或异常),调用带有注释的方法

    @Transactional(value = "alternativeTransactionManager", readOnly = true)

    public void doSomething() {
        try {
            tryFirst();
        } catch (Exception e) {
            tryAlternative();
        }
    }
    
    @Transactional(value = "archiveTransactionManager", readOnly = true)
    public void tryFirst() {
    
    }
    
    @Transactional(value = "alternativeTransactionManager", readOnly = true)
    public void tryAlternative() {
    
    }
    

    请记住,您必须同时声明它们。

    【讨论】:

      【解决方案3】:

      您使用了错误的注解来命名 bean。 @Qualifier 用于注入点指定名称。您需要使用@Bean 为bean 命名。所以你的 bean 定义需要看起来像这样:

      @Bean("archiveTransactionManager")
      @Conditional(ArchiveCondition.class)
      public HibernateTransactionManager archiveTransactionManager() {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(archiveSessionFactory());
        return transactionManager;
      }
      

      【讨论】:

      • 你的配置没问题,但这不是我的主要问题。我还是有同样的问题
      • 您是否还有其他事务管理器?您可以尝试完全不命名该 bean,而只用 @Primary 标记它,因此 Spring 在可用时更喜欢那个。
      • 我还有一个主事务管理器。在某些情况下(由开发人员配置),archiveTransactionManager bean 不是由 @Conditional 注释创建的,我希望在这些情况下 spring 跳过检查条件事务管理器的 bean 验证。
      • 如果没有 bean,这将无济于事。默认情况下,bean 的名称是方法的名称。所以做这样的事情不会增加任何东西。
      猜你喜欢
      • 2011-04-22
      • 1970-01-01
      • 2014-08-10
      • 2017-08-11
      • 1970-01-01
      • 2015-04-18
      • 2014-02-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多