package org.hibernate.cache.internal;
import java.io.Serializable;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.action.internal.CollectionAction;
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
import org.hibernate.boot.Metadata;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostDeleteEvent;
import org.hibernate.event.spi.PostDeleteEventListener;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostInsertEventListener;
import org.hibernate.event.spi.PostUpdateEvent;
import org.hibernate.event.spi.PostUpdateEventListener;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.jboss.logging.Logger;
public class CollectionCacheInvalidator
implements Integrator, PostInsertEventListener, PostDeleteEventListener, PostUpdateEventListener {
private static final Logger LOG = Logger.getLogger( CollectionCacheInvalidator.class.getName() );
public static boolean PROPAGATE_EXCEPTION = false;
@Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
integrate( serviceRegistry, sessionFactory );
}
@Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
}
@Override
public void onPostInsert(PostInsertEvent event) {
evictCache( event.getEntity(), event.getPersister(), event.getSession(), null );
}
@Override
public boolean requiresPostCommitHanding(EntityPersister persister) {
return true;
}
@Override
public void onPostDelete(PostDeleteEvent event) {
evictCache( event.getEntity(), event.getPersister(), event.getSession(), null );
}
@Override
public void onPostUpdate(PostUpdateEvent event) {
evictCache( event.getEntity(), event.getPersister(), event.getSession(), event.getOldState() );
}
private void integrate(SessionFactoryServiceRegistry serviceRegistry, SessionFactoryImplementor sessionFactory) {
if ( !sessionFactory.getSessionFactoryOptions().isAutoEvictCollectionCache() ) {
return;
}
if ( !sessionFactory.getSessionFactoryOptions().isSecondLevelCacheEnabled() ) {
return;
}
EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
eventListenerRegistry.appendListeners( EventType.POST_INSERT, this );
eventListenerRegistry.appendListeners( EventType.POST_DELETE, this );
eventListenerRegistry.appendListeners( EventType.POST_UPDATE, this );
}
private void evictCache(Object entity, EntityPersister persister, EventSource session, Object[] oldState) {
try {
SessionFactoryImplementor factory = persister.getFactory();
Set<String> collectionRoles = factory.getMetamodel().getCollectionRolesByEntityParticipant( persister.getEntityName() );
if ( collectionRoles == null || collectionRoles.isEmpty() ) {
return;
}
for ( String role : collectionRoles ) {
final CollectionPersister collectionPersister = factory.getMetamodel().collectionPersister( role );
if ( !collectionPersister.hasCache() ) {
continue;
}
String mappedBy = collectionPersister.getMappedByProperty();
if ( !collectionPersister.isManyToMany() &&
mappedBy != null && !mappedBy.isEmpty() ) {
int i = persister.getEntityMetamodel().getPropertyIndex( mappedBy );
Serializable oldId = null;
if ( oldState != null ) {
oldId = getIdentifier( session, oldState[i] );
}
Object ref = persister.getPropertyValue( entity, i );
Serializable id = getIdentifier( session, ref );
if ( ( id != null && !id.equals( oldId ) ) || ( oldId != null && !oldId.equals( id ) ) ) {
if ( id != null ) {
evict( id, collectionPersister, session );
}
if ( oldId != null ) {
evict( oldId, collectionPersister, session );
}
}
}
else {
LOG.debug( "Evict CollectionRegion " + role );
final SoftLock softLock = collectionPersister.getCacheAccessStrategy().lockRegion();
session.getActionQueue().registerProcess( (success, session1) -> {
collectionPersister.getCacheAccessStrategy().unlockRegion( softLock );
} );
}
}
}
catch ( Exception e ) {
if ( PROPAGATE_EXCEPTION ) {
throw new IllegalStateException( e );
}
LOG.error( "", e );
}
}
private Serializable getIdentifier(EventSource session, Object obj) {
Serializable id = null;
if ( obj != null ) {
id = session.getContextEntityIdentifier( obj );
if ( id == null ) {
id = session.getSessionFactory().getMetamodel().entityPersister( obj.getClass() ).getIdentifier( obj, session );
}
}
return id;
}
private void evict(Serializable id, CollectionPersister collectionPersister, EventSource session) {
if ( LOG.isDebugEnabled() ) {
LOG.debug( "Evict CollectionRegion " + collectionPersister.getRole() + " for id " + id );
}
AfterTransactionCompletionProcess afterTransactionProcess = new CollectionEvictCacheAction(
collectionPersister,
null,
id,
session
).lockCache();
session.getActionQueue().registerProcess( afterTransactionProcess );
}
private static final class CollectionEvictCacheAction extends CollectionAction {
protected CollectionEvictCacheAction(
CollectionPersister persister,
PersistentCollection collection,
Serializable key,
SharedSessionContractImplementor session) {
super( persister, collection, key, session );
}
@Override
public void execute() throws HibernateException {
}
public AfterTransactionCompletionProcess lockCache() {
beforeExecutions();
return getAfterTransactionCompletionProcess();
}
}
}