Issue
I created several similar threads about particular issues, but let me summarize the problem I'm facing more globally:
GOAL
Need to intercept all Insert/Update/Delete DB Transactions in an application written in Spring 4.1.5, Hibernate 4.3.8. The app uses all styles of Hibernate usage:
- Object-based through Session, e.g.
sessionFactory.getCurrentSession().saveOrUpdate(obj);
- HQL executeUpdate, e.g.
Query q = "update Obj ..."; q.executeUpdate();
- Criteria API, e.g.
Criteria q = sessionFactory.getCurentSession().createCriteria(..); q.list();
Possible Approaches
- Interceptor: Implement an Interceptor.
public class MyInterceptor extends EmptyInterceptor {..}
Override either (1) Transaction-level methods or (2) specific action methods.
Transaction-level:
@Override
public void afterTransactionBegin(Transaction tx) {
super.afterTransactionBegin(tx);
}
Specific action level:
@Override
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
return super.onSave(entity, id, state, propertyNames, types);
}
@Override
public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
super.onDelete(entity, id, state, propertyNames, types);
}
EventListener: Implement a
PreUpdateEventListener
,PreInsertEventListener
and register it.@Component public class HibernateSaveUpdateEventListener implements PreUpdateEventListener, PreInsertEventListener { @Override public boolean onPreInsert(PreInsertEvent arg0) { //... } @Override public boolean onPreUpdate(PreUpdateEvent arg0) { //... } }
Problems
- Neither the
EventListener
norInterceptor
approach intercepts HQL executeUpdate(). But we have many instances of that in the app. Some other users have confirmed that:
https://forum.hibernate.org/viewtopic.php?f=1&t=1012054
https://coderanch.com/t/669945/java/Hibernate-interceptor-working-hibernate-query
Seems like the issue is that EventListener/Interceptor
only work with a Session
. But HQL executeUpdate is on the Query
object, not the Session
.
- Can't use the Transaction-level Interceptor approach of
afterTransactionBegin(Transaction tx)
either. The reason is, I can't distinguish Read-Only Select Transactions from Write Transactions.tx
doesn't tell me whether I'm in areadOnly=false
orreadOnly=true
transaction. So theafterTransactionBegin
override will fire for all transactions, and I need to limit it non-Read-Only ones only (Insert, Update, Delete).
@Transactional(readOnly = false)
@Transactional(readOnly = true)
So is there a solution to comprehensively intercept Insert/Update/Delete for a Hibernate app that uses both object operations and HQL operations?
Solution
There's an easy way to check for Read-Only/non-Read-Only Transactions with a Spring-level transaction listener, and this will include outside SQL/HQL (non-entity). On the Spring level, rather than Hibernate, there's a method called TransactionSynchronizationManager.isCurrentTransactionReadOnly()
that tells us if we're inside a read-only transaction we're intercepting. 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
}
}
Answered By - gene b.
Answer Checked By - Mary Flores (JavaFixing Volunteer)