I'm using Spring/JPA2/hibernate with this code:
class A {
@Autowired
B b;
@RequestMapping("/test")
public void test(final HttpServletRequest r1, HttpServletResponse r2) throws ... {
b.inner(); // Works
b.outer(); // javax.persistence.TransactionRequiredException:
// no transaction is in progress ... :|
}
@Component
class B {
@PersistenceContext
EntityManager em;
public void outer() { inner(); }
@Transactional
public void inner() { em.flush(); }
}
Why does inner()
only when called indirectly loose the transaction?
http://static.springsource.org/spring/docs/current/reference/transaction.html#transaction-declarative-annotations
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional.
Consider the use of AspectJ mode (see mode attribute in table below) if you expect self-invocations to be wrapped with transactions as well. In this case, there will not be a proxy in the first place; instead, the target class will be weaved (that is, its byte code will be modified) in order to turn @Transactional into runtime behavior on any kind of method.
The @Autowired
reference B b
(inside class A
), is wrapped with a Spring AOP transaction-aware proxy.
When b.inner()
is called, you are invoking it on the transaction-aware instance, and it is marked as @Transactional
. Thus a Spring managed transaction is started.
When b.outer()
is called, it is also on a transaction-aware instance, but it is not @Transactional
. Thus a Spring managed transaction is not started.
Once you are inside the invocation of outer()
the call to inner()
is not going through the transaction-aware proxy, it is being invoked directly. It is the same as this.inner()
. Since you are invoking it directly, and not through the proxy, it does not have the Spring transaction-aware semantics.
Since no transaction has been started, this results in the TransactionRequiredException
.
Possible solutions include making the method outer()
@Transactional
.
@Transactional
public void outer() { inner(); }
Or making the entire class @Transactional
.
@Transactional
@Component
class B {
@PersistenceContext
EntityManager em;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With