matthew inger opened SPR-9661 and commented

By embedding the transaction manager name in the @Transactional anotations, using base dao classes is cumbersome. Example:

@Transactional public class GenericDao { protected Class\ mappedClass; protected SessionFactory sessionFactory;

public GenericDao(Class<E> mappedClass, SessionFactory sessionFactory) {
    this.mappedClass = mappedClass;
    this.sessionFactory = sessionFactory;
}

@Transactional(readOnly=false)
public E getById(K key) {
    return sessionFactory.getCurrentSession().get(mappedClass, key);
}

public E merge(E entity) {
    return sessionFactory.getCurrentSession().merge(entity);
}

}

Now we have two subclasses, each of which will use different sessionfactory, and transaction manager (we don't need jta).

@Transactional("fooTxManager") public class FooDao extends GenericDao { ... }

@Transactional("barTxManager") public class FooDao extends GenericDao { ... }

The problem is that we'd have to override the getById() method otherwise that method will use the default transaction manager. Not a big deal with 1 method, but it is a big deal when there's a bunch of methods.

@Transactional("barTxManager") public class FooDao extends GenericDao { @Override @Transactional("barTxManager") public Foo getById(Long key) { return super.getById(key); } ... }

I think a better approach would be to either:

a) Allow the transaction manager name, when left blank, to fallback to the annotation at the class level. b) Separate the transaction semantics from the manager name with a new annotation:

@TransactionManager("barTxManager")


Affects: 3.1.1

Comment From: spring-projects-issues

matthew inger commented

ps: in the mean time, i've extended the AnnotationTransactionAttributeSource to override the getTransactionAttribute method. In this subclass, i'm first calling super.getTransactionAttribute(), then i'm looking for the @Transactional on the target class (or any superclass or meta-annotation) and overriding the supplied (or default) transaction manager:

TransactionAttribute attribute = super.getTransactionAttribute(method, targetClass);
 if (attribute != null) {
     Transactional txManager = findTxManager(targetClass);
     if (txManager != null) {
         RuleBasedTransactionAttribute att = (RuleBasedTransactionAttribute) attribute;
         att.setQualifier(txManager.value());
     }
 }
 return attribute;

And to activate this, I'm using a bean factory post processor and replacing all of the definitions of AnnotationTransactionAttributeSource with my extension of it.

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
        throws BeansException {
    String[] beanNames = beanFactory.getBeanNamesForType(AnnotationTransactionAttributeSource.class);
    for(String beanName : beanNames) {
        BeanDefinition def = beanFactory.getBeanDefinition(beanName);
        def.setBeanClassName(TxManagerAnnotationTransactionAttributeSource.class.getName());
    }
}

Comment From: spring-projects-issues

matthew inger commented

PS: When i said this: Allow the transaction manager name, when left blank, to fallback to the annotation at the class level

I mean let the actual targetClass be allowed to determine the transaction manager, instead of forcing it to the class where the method is implemented.

Comment From: spring-projects-issues

Bulk closing outdated, unresolved issues. Please, reopen if still relevant.

Comment From: elab

I have exact the same problem when trying to reuse methods from the base DAO class with transaction manager of the concrete DAO class. How to achieve that?

The workaround by @mattinger from 2012 is unfortunately not working anymore due to multiple changes in the Spring code.

Related: #14011