Search code examples
springhibernateconfigurationspring-dataspring-transactions

How to use Transactional annotation when config transactionManager conditionally in spring?


I use multiple datasources in my spring project. I enable/disable them by manual config in project startup time. At a time all of them may be active So a transactionManager bean maybe active or not.

I implement it by @Conditional annotation in spring configuration class. When I use a disable transactional annotation on methods I have NoSuchBeanDefinitionException.

When I define transactionManager bean conditionally how to use the transactional annotioan on methods?

The archiveTransactionManager bean doesn't create by @Conditional annotation and I want spring skip checking for bean validation of conditional transaction manager.

For conditional sessionFactory beans I set 'required' parameter in Autowired annotation to false for prevent spring to throw NoSuchBeanDefinitionException but what do I do for @Transactional ?

Configuration class

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

Transactional method

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

Usage

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

Current result:

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!

I want spring skip bean validation of conditional transactionManager beans on some situations that they don't create by conditions.


Solution

  • I implement a no-op SessionFactory and instantiate it in spring configuration class whenever archive_mode is disable :

    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");
        }
    ...
    }