Declarative Transaction Management in Spring behaving unpredictably
I have a Spring+Hibernate application with declarative transaction management.
I have a service (FooService
) which has 2 public methods MethodA
and MethodB
. The client will call
the MethodA
which in turn will call the MethodB
.
Client -> MethodA -> MethodB
I want the transaction to start only from MethodB onwards. This is a snippet from my spring application-context:
<bean id="FooService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="FooServiceTarget" /开发者_开发百科>
<property name="transactionAttributes">
<props>
<prop key="MethodB">PROPAGATION_REQUIRED,-FooException</prop>
</props>
</property>
</bean>
However, when I invoke the MethodA
from my client, it doesn't create a transaction proxy when MethodB
is to be called.
If I add MethodA
also to the bean configuration in application-context, the transaction proxy is invoked (starting MethodA
, as expected).
Why is this so? Can I achieve transaction being created only from MethodB onwards?
Client -> MethodA -> MethodB
I want the transaction to start only from MethodB onwards
This can't work. Method A and Method B are inside the same proxy.
The only proper thing to do is to move method B into a different Bean.
BTW: this has been asked many times before, here are some previous answers of mine:
- Understanding Spring transactions - What happens when a transactional method calls another transactional method?
- Spring @Transactional method - participating transaction
- One Service method invoke inner multiple method for Spring transaction
Can I achieve transaction being created only from MethodB onwards?
Only if you use AspectJ bytecode weaving with Spring.
Why is this so?
Spring's default AOP mechanism is JDK dynamic proxies, which creates a separate Proxy instance that implements your service interface. This proxy is injected into other bean in place of your service, and all calls that go through it will do the transaction stuff before delegating to your service. Since a call from your service to itself doesn't go through the proxy, no transaction can or will be started. With AspectJ bytecode weaving, the transaction code will be woven directly into your service, and it will work fine. If you find yourself needing it for this purpose, though, it's a good bet that you need to refactor your "service" into at least two, separate objects because it's a signal that you've mixed up concerns and/or crossed abstraction layers in one class.
精彩评论