Spring @Transactional in @Bean declaration instead of class Implementation
I would like to set up "transactional" beans from my Spring class @Configuration
instead of annotating the class implementation itself with @Transactional
.
Kind of like the old school way, setting up transactional tips from an XML file, but no String reference to my class / method names for creating pointcuts.
The reason is that the bean implementation is in a different code base and the module it belongs to is independent of Spring. Read: I am not touching the source code of this bean, just creating it. The class is final, it cannot extend it either to add Spring annotations to the child class. Let's say that all methods should be transactional for simplicity.
Bean implementation:
/** This class has no Spring dependency... */
// @Transactional <- which means I can't use this here
public final class ComplexComponentImpl implements ComplexComponent {
private SomeRepository repo;
public ComplexComponentImpl(SomeRepository repository) { this.repo = repository }
public void saveEntities(SomeEntity e1, SomeEntity e2) {
repo.save(e1);
throw new IllegalStateException("Make the transaction fail");
}
What I want to do in my config class (and that doesn't work in my unit test):
@Configuration
@EnableTransactionManagement
public class ComplexComponentConfig {
@Bean
@Transactional // <- Make the bean transactional here
public ComplexComponent complexComponent() {
return new ComplexComponentImpl(repository());
}
// ...
}
The above example doesn't work, as nothing gets the transaction at runtime: the object is e1
retained even if an exception is thrown.
Please note that my transaction management setup works fine with the implementation class marked @Transactional
.
Question : Is it possible to declare a transaction @Bean
from a class @Configuration
, or is there any alternative taking into account the restrictions above?
source to share
Found something built in, this is the sum of @Mecon and @Erik Gillespie , with a limited template.
Spring already provides TransactionProxyFactoryBean
which just sets up a transactional proxy for any entity. Most of the settings can be refactored to some utility method:
@Configuration
@EnableTransactionManagement
public class ComplexComponentConfig {
/** NOT A @Bean, this object will be wrapped with a transactional proxy */
public ComplexComponent complexComponentImpl() {
return new ComplexComponentImpl(repository());
}
@Bean
public ComplexComponent complexComponent() {
TransactionProxyFactoryBean proxy = new TransactionProxyFactoryBean();
// Inject transaction manager here
proxy.setTransactionManager(txManager());
// Define wich object instance is to be proxied (your bean)
proxy.setTarget(complexComponentImpl());
// Programmatically setup transaction attributes
Properties transactionAttributes = new Properties();
transactionAttributes.put("*", "PROPAGATION_REQUIRED");
proxy.setTransactionAttributes(transactionAttributes);
// Finish FactoryBean setup
proxy.afterPropertiesSet();
return (ComplexComponent) proxy.getObject;
}
// ...
}
source to share
I think you probably cannot use @Transactional this way. One of spring's built-in PostProcessors should scan all classes (beans) that have this annotation and create Aspects accordingly.
On the alternatives: I would write an adapter class for every third class I have to use. And then make these spring Beans adapter classes.
source to share
You cannot use @Transactional
this way, but you can programmatically configure aspects with Spring.
Spring documentation for programmatically defining aspects:
- http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aspectj-programmatic
- http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html
The examples in the documentation are very simple. Defining aspects of transactions is likely to be more complex, and I wouldn't be surprised if you find it easier to just use the convenience of XML-based proxies or take @ Mecon's advice and write adapters.
source to share