2
votes

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.

3
It won't skip the check as that is build into the interceptor. You should add a no-op interceptor when one shouldn't be enabled. Or don't use @Transactional and write your transactional expressions explicitly in java and make thos conditional as well. Using a no-op transaction manager is probably the easiests. However why if there is no tx-manager and no -session-factory why instantiate this bean? Shouldn't simply the whole module/part of the application should be ignored.M. Deinum
@M.Deinum In some situations for example in archive operations I set archive_mode setting and run app. In this mode some methods check settings and do plus operations on another datasource. I have a primary datasource in all modes for cruds functionality.Seyed Hossein Masbough

3 Answers

2
votes

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

I would treat it as a fallback scenario, where you have to recover after a failure. In this case, have a method calling method annotated with

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

in case of failure (or exception), call method annotated with

@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() {

}

Keep in mind, you must have both of them declared though.

-1
votes

You used the wrong annotation to name the bean. @Qualifier is used on the injection point to specify the name. You need to give the bean a name with @Bean. So your bean definition needs to look like that:

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