Issue
I defined and successfully plugged in a Hibernate DB Interceptor which catches all Transactions.
public class HibernateTransactionInterceptor extends EmptyInterceptor {
@Override
public void afterTransactionBegin(Transaction tx) {
System.out.println("Intercepted");
// ...
super.afterTransactionBegin(tx);
}
}
applicationContext.xml:
<bean id="transactionInterceptor" class="myapp.interceptor.HibernateTransactionInterceptor" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- Plug into SessionFactory the Interceptor bean define above -->
<property name="entityInterceptor" ref="transactionInterceptor" />
...
</bean>
Now, the Interceptor fires on all @Transaction
service methods. But I need to only intercept @Transaction(readOnly=FALSE)
methods (i.e., filter out all Read-Only methods). Is there a way to configure that?
Solution
I found the solution. There's an easy way to check for Read-Only/non-Read-Only Transactions with a Spring-level transaction listener. On the Spring level, rather than Hibernate, there's a method called TransactionSynchronizationManager.isCurrentTransactionReadOnly()
. It can be called from the following implementation of TransactionSynchronization
which uses a pointcut to intercept all @Before
@Transactional
Spring processing.
@Component
@Aspect
public class TransactionSynchronizationAspect implements TransactionSynchronization {
@Before("@annotation(org.springframework.transaction.annotation.Transactional)")
public void registerTransactionSynchronization() {
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if (!isReadOnly) {
// ... logic goes here...
}
// Continue
TransactionSynchronizationManager.registerSynchronization(this);
}
@Override
public void afterCompletion(int status) {
// No custom code here
}
}
As for the original Hibernate Transaction Listener in the Original Post, that strategy can still be used, but requires a tx:advice
to restrict the Listener to specified (mapped) Service method names, like so:
<aop:config>
<aop:advisor id="managerTx" advice-ref="txAdvice"
pointcut="execution(* com.app.*.*.service.*.*(..))"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="customTransactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
</tx:attributes>
</tx:advice>
This XML file can be imported with @ImportResource
. With this strategy, all Write operations would be intercepted with the Listener, while Reads would always go through automatically.
Answered By - gene b.
Answer Checked By - Timothy Miller (JavaFixing Admin)