/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.event.internal; import java.io.Serializable; import java.util.Map; import org.jboss.logging.Logger; import org.hibernate.HibernateException; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.spi.RefreshEvent; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.PersistentObjectException; import org.hibernate.UnresolvableObjectException; import org.hibernate.cache.spi.CacheKey; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.RefreshEventListener; import org.hibernate.internal.util.collections.IdentityMap; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; import org.hibernate.type.Type; /** * Defines the default refresh event listener used by hibernate for refreshing entities * in response to generated refresh events. * * @author Steve Ebersole */ public class DefaultRefreshEventListener implements RefreshEventListener { private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, DefaultRefreshEventListener.class.getName()); public void onRefresh(RefreshEvent event) throws HibernateException { onRefresh( event, IdentityMap.instantiate(10) ); } /** * Handle the given refresh event. * * @param event The refresh event to be handled. */ public void onRefresh(RefreshEvent event, Map refreshedAlready) { final EventSource source = event.getSession(); boolean isTransient = ! source.contains( event.getObject() ); if ( source.getPersistenceContext().reassociateIfUninitializedProxy( event.getObject() ) ) { if ( isTransient ) { source.setReadOnly( event.getObject(), source.isDefaultReadOnly() ); } return; } final Object object = source.getPersistenceContext().unproxyAndReassociate( event.getObject() ); if ( refreshedAlready.containsKey(object) ) { LOG.trace("Already refreshed"); return; } final EntityEntry e = source.getPersistenceContext().getEntry( object ); final EntityPersister persister; final Serializable id; if ( e == null ) { persister = source.getEntityPersister(event.getEntityName(), object); //refresh() does not pass an entityName id = persister.getIdentifier( object, event.getSession() ); if (LOG.isTraceEnabled()) LOG.trace("Refreshing transient " + MessageHelper.infoString(persister, id, source.getFactory())); final EntityKey key = source.generateEntityKey( id, persister ); if ( source.getPersistenceContext().getEntry(key) != null ) { throw new PersistentObjectException( "attempted to refresh transient instance when persistent instance was already associated with the Session: " + MessageHelper.infoString(persister, id, source.getFactory() ) ); } } else { if (LOG.isTraceEnabled()) LOG.trace("Refreshing " + MessageHelper.infoString(e.getPersister(), e.getId(), source.getFactory())); if ( !e.isExistsInDatabase() ) { throw new HibernateException( "this instance does not yet exist as a row in the database" ); } persister = e.getPersister(); id = e.getId(); } // cascade the refresh prior to refreshing this entity refreshedAlready.put(object, object); new Cascade( CascadingAction.REFRESH, Cascade.BEFORE_REFRESH, source) .cascade( persister, object, refreshedAlready ); if ( e != null ) { final EntityKey key = source.generateEntityKey( id, persister ); source.getPersistenceContext().removeEntity(key); if ( persister.hasCollections() ) new EvictVisitor( source ).process(object, persister); } if ( persister.hasCache() ) { final CacheKey ck = source.generateCacheKey( id, persister.getIdentifierType(), persister.getRootEntityName() ); persister.getCacheAccessStrategy().evict( ck ); } evictCachedCollections( persister, id, source.getFactory() ); String previousFetchProfile = source.getLoadQueryInfluencers().getInternalFetchProfile(); source.getLoadQueryInfluencers().setInternalFetchProfile( "refresh" ); Object result = persister.load( id, object, event.getLockOptions(), source ); // Keep the same read-only/modifiable setting for the entity that it had before refreshing; // If it was transient, then set it to the default for the source. if ( result != null ) { if ( ! persister.isMutable() ) { // this is probably redundant; it should already be read-only source.setReadOnly( result, true ); } else { source.setReadOnly( result, ( e == null ? source.isDefaultReadOnly() : e.isReadOnly() ) ); } } source.getLoadQueryInfluencers().setInternalFetchProfile(previousFetchProfile); UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() ); } private void evictCachedCollections(EntityPersister persister, Serializable id, SessionFactoryImplementor factory) { evictCachedCollections( persister.getPropertyTypes(), id, factory ); } private void evictCachedCollections(Type[] types, Serializable id, SessionFactoryImplementor factory) throws HibernateException { for ( Type type : types ) { if ( type.isCollectionType() ) { factory.getCache().evictCollection( ( (CollectionType) type ).getRole(), id ); } else if ( type.isComponentType() ) { CompositeType actype = (CompositeType) type; evictCachedCollections( actype.getSubtypes(), id, factory ); } } } }