All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.event.internal.DefaultLoadEventListener Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.event.internal;

import java.io.Serializable;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.PersistentObjectException;
import org.hibernate.TypeMismatchException;
import org.hibernate.WrongClassException;
import org.hibernate.action.internal.DelayedPostInsertIdentifier;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.cache.spi.entry.ReferenceCacheEntryImpl;
import org.hibernate.cache.spi.entry.StandardCacheEntryImpl;
import org.hibernate.engine.internal.CacheHelper;
import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.LoadEvent;
import org.hibernate.event.spi.LoadEventListener;
import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PostLoadEventListener;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.stat.internal.StatsHelper;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.tuple.IdentifierProperty;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.EmbeddedComponentType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper;

/**
 * Defines the default load event listeners used by hibernate for loading entities
 * in response to generated load events.
 *
 * @author Steve Ebersole
 */
public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener implements LoadEventListener {
	public static final Object REMOVED_ENTITY_MARKER = new Object();
	public static final Object INCONSISTENT_RTN_CLASS_MARKER = new Object();
	public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE;

	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( DefaultLoadEventListener.class );

	private static final boolean traceEnabled = LOG.isTraceEnabled();

	/**
	 * Handle the given load event.
	 *
	 * @param event The load event to be handled.
	 */
	public void onLoad(
			final LoadEvent event,
			final LoadEventListener.LoadType loadType) throws HibernateException {

		final EntityPersister persister = getPersister( event );

		if ( persister == null ) {
			throw new HibernateException( "Unable to locate persister: " + event.getEntityClassName() );
		}

		final Class idClass = persister.getIdentifierType().getReturnedClass();
		if ( idClass != null &&
				!idClass.isInstance( event.getEntityId() ) &&
				!DelayedPostInsertIdentifier.class.isInstance( event.getEntityId() ) ) {
			checkIdClass( persister, event, loadType, idClass );
		}

		doOnLoad( persister, event, loadType );
	}

	protected EntityPersister getPersister(final LoadEvent event) {
		final Object instanceToLoad = event.getInstanceToLoad();
		if ( instanceToLoad != null ) {
			//the load() which takes an entity does not pass an entityName
			event.setEntityClassName( instanceToLoad.getClass().getName() );
			return event.getSession().getEntityPersister(
					null,
					instanceToLoad
			);
		}
		else {
			return event.getSession().getFactory().getMetamodel().entityPersister( event.getEntityClassName() );
		}
	}

	private void doOnLoad(
			final EntityPersister persister,
			final LoadEvent event,
			final LoadEventListener.LoadType loadType) {

		try {
			final EventSource session = event.getSession();
			final EntityKey keyToLoad = session.generateEntityKey( event.getEntityId(), persister );
			if ( loadType.isNakedEntityReturned() ) {
				//do not return a proxy!
				//(this option indicates we are initializing a proxy)
				event.setResult( load( event, persister, keyToLoad, loadType ) );
			}
			else {
				//return a proxy if appropriate
				if ( event.getLockMode() == LockMode.NONE ) {
					event.setResult( proxyOrLoad( event, persister, keyToLoad, loadType ) );
				}
				else {
					event.setResult( lockAndLoad( event, persister, keyToLoad, loadType, session ) );
				}
			}
		}
		catch (HibernateException e) {
			LOG.unableToLoadCommand( e );
			throw e;
		}
	}

	private void checkIdClass(
			final EntityPersister persister,
			final LoadEvent event,
			final LoadEventListener.LoadType loadType,
			final Class idClass) {
				// we may have the kooky jpa requirement of allowing find-by-id where
			// "id" is the "simple pk value" of a dependent objects parent.  This
			// is part of its generally goofy "derived identity" "feature"
		final IdentifierProperty identifierProperty = persister.getEntityMetamodel().getIdentifierProperty();
		if ( identifierProperty.isEmbedded() ) {
				final EmbeddedComponentType dependentIdType =
						(EmbeddedComponentType) identifierProperty.getType();
				if ( dependentIdType.getSubtypes().length == 1 ) {
					final Type singleSubType = dependentIdType.getSubtypes()[0];
					if ( singleSubType.isEntityType() ) {
						final EntityType dependentParentType = (EntityType) singleSubType;
						final SessionFactoryImplementor factory = event.getSession().getFactory();
						final Type dependentParentIdType = dependentParentType.getIdentifierOrUniqueKeyType( factory );
						if ( dependentParentIdType.getReturnedClass().isInstance( event.getEntityId() ) ) {
							// yep that's what we have...
							loadByDerivedIdentitySimplePkValue(
									event,
									loadType,
									persister,
									dependentIdType,
									factory.getMetamodel().entityPersister( dependentParentType.getAssociatedEntityName() )
							);
							return;
						}
					}
				}
			}
			throw new TypeMismatchException(
					"Provided id of the wrong type for class " + persister.getEntityName() + ". Expected: " + idClass
							+ ", got " + event.getEntityId().getClass()
			);
	}

	private void loadByDerivedIdentitySimplePkValue(
			LoadEvent event,
			LoadEventListener.LoadType options,
			EntityPersister dependentPersister,
			EmbeddedComponentType dependentIdType,
			EntityPersister parentPersister) {
		final EventSource session = event.getSession();
		final EntityKey parentEntityKey = session.generateEntityKey( event.getEntityId(), parentPersister );
		final Object parent = doLoad( event, parentPersister, parentEntityKey, options );

		final Serializable dependent = (Serializable) dependentIdType.instantiate( parent, session );
		dependentIdType.setPropertyValues( dependent, new Object[] {parent}, dependentPersister.getEntityMode() );
		final EntityKey dependentEntityKey = session.generateEntityKey( dependent, dependentPersister );
		event.setEntityId( dependent );

		event.setResult( doLoad( event, dependentPersister, dependentEntityKey, options ) );
	}

	/**
	 * Performs the load of an entity.
	 *
	 * @param event The initiating load request event
	 * @param persister The persister corresponding to the entity to be loaded
	 * @param keyToLoad The key of the entity to be loaded
	 * @param options The defined load options
	 *
	 * @return The loaded entity.
	 */
	private Object load(
			final LoadEvent event,
			final EntityPersister persister,
			final EntityKey keyToLoad,
			final LoadEventListener.LoadType options) {

		if ( event.getInstanceToLoad() != null ) {
			if ( event.getSession().getPersistenceContext().getEntry( event.getInstanceToLoad() ) != null ) {
				throw new PersistentObjectException(
						"attempted to load into an instance that was already associated with the session: " +
								MessageHelper.infoString(
										persister,
										event.getEntityId(),
										event.getSession().getFactory()
								)
				);
			}
			persister.setIdentifier( event.getInstanceToLoad(), event.getEntityId(), event.getSession() );
		}

		final Object entity = doLoad( event, persister, keyToLoad, options );

		boolean isOptionalInstance = event.getInstanceToLoad() != null;

		if ( entity == null && ( !options.isAllowNulls() || isOptionalInstance ) ) {
			event.getSession()
					.getFactory()
					.getEntityNotFoundDelegate()
					.handleEntityNotFound( event.getEntityClassName(), event.getEntityId() );
		}
		else if ( isOptionalInstance && entity != event.getInstanceToLoad() ) {
			throw new NonUniqueObjectException( event.getEntityId(), event.getEntityClassName() );
		}

		return entity;
	}

	/**
	 * Based on configured options, will either return a pre-existing proxy,
	 * generate a new proxy, or perform an actual load.
	 *
	 * @param event The initiating load request event
	 * @param persister The persister corresponding to the entity to be loaded
	 * @param keyToLoad The key of the entity to be loaded
	 * @param options The defined load options
	 *
	 * @return The result of the proxy/load operation.
	 */
	private Object proxyOrLoad(
			final LoadEvent event,
			final EntityPersister persister,
			final EntityKey keyToLoad,
			final LoadEventListener.LoadType options) {

		final EventSource session = event.getSession();
		final SessionFactoryImplementor factory = session.getFactory();

		if ( traceEnabled ) {
			LOG.tracev(
					"Loading entity: {0}",
					MessageHelper.infoString( persister, event.getEntityId(), factory )
			);
		}

		final PersistenceContext persistenceContext = session.getPersistenceContext();

		final boolean allowBytecodeProxy = factory
				.getSessionFactoryOptions()
				.isEnhancementAsProxyEnabled();

		final boolean entityHasHibernateProxyFactory = persister.getEntityMetamodel()
				.getTuplizer()
				.getProxyFactory() != null;

		// Check for the case where we can use the entity itself as a proxy
		if ( options.isAllowProxyCreation()
				&& allowBytecodeProxy
				&& persister.getEntityMetamodel().getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ) {
			// if there is already a managed entity instance associated with the PC, return it
			final Object managed = persistenceContext.getEntity( keyToLoad );
			if ( managed != null ) {
				if ( options.isCheckDeleted() ) {
					final EntityEntry entry = persistenceContext.getEntry( managed );
					final Status status = entry.getStatus();
					if ( status == Status.DELETED || status == Status.GONE ) {
						return null;
					}
				}
				return managed;
			}

			// if the entity defines a HibernateProxy factory, see if there is an
			// existing proxy associated with the PC - and if so, use it
			if ( entityHasHibernateProxyFactory ) {
				final Object proxy = persistenceContext.getProxy( keyToLoad );

				if ( proxy != null ) {
					LOG.trace( "Entity proxy found in session cache" );

					LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();

					if ( li.isUnwrap() || event.getShouldUnwrapProxy() ) {
						return li.getImplementation();
					}


					return persistenceContext.narrowProxy( proxy, persister, keyToLoad, null );
				}

				// specialized handling for entities with subclasses with a HibernateProxy factory
				if ( persister.getEntityMetamodel().hasSubclasses() ) {
					// entities with subclasses that define a ProxyFactory can create
					// a HibernateProxy so long as NO_PROXY was not specified.
					if ( event.getShouldUnwrapProxy() != null && event.getShouldUnwrapProxy() ) {
						LOG.debugf( "Ignoring NO_PROXY for to-one association with subclasses to honor laziness" );
					}
					return createProxy( event, persister, keyToLoad, persistenceContext );
				}
			}

			if ( keyToLoad.isBatchLoadable() ) {
				// Add a batch-fetch entry into the queue for this entity
				persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
			}

			// This is the crux of HHH-11147
			// create the (uninitialized) entity instance - has only id set
			return persister.getBytecodeEnhancementMetadata().createEnhancedProxy( keyToLoad, true, session );
		}
		else {
			if ( persister.hasProxy() ) {
				// look for a proxy
				Object proxy = persistenceContext.getProxy( keyToLoad );
				if ( proxy != null ) {
					return returnNarrowedProxy( event, persister, keyToLoad, options, persistenceContext, proxy );
				}

				if ( options.isAllowProxyCreation() ) {
					return createProxyIfNecessary( event, persister, keyToLoad, options, persistenceContext );
				}
			}
		}

		// return a newly loaded object
		return load( event, persister, keyToLoad, options );
	}


	/**
	 * Given a proxy, initialize it and/or narrow it provided either
	 * is necessary.
	 *
	 * @param event The initiating load request event
	 * @param persister The persister corresponding to the entity to be loaded
	 * @param keyToLoad The key of the entity to be loaded
	 * @param options The defined load options
	 * @param persistenceContext The originating session
	 * @param proxy The proxy to narrow
	 *
	 * @return The created/existing proxy
	 */
	private Object returnNarrowedProxy(
			final LoadEvent event,
			final EntityPersister persister,
			final EntityKey keyToLoad,
			final LoadEventListener.LoadType options,
			final PersistenceContext persistenceContext,
			final Object proxy) {
		if ( traceEnabled ) {
			LOG.trace( "Entity proxy found in session cache" );
		}

		LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();

		if ( li.isUnwrap() ) {
			return li.getImplementation();
		}

		Object impl = null;
		if ( !options.isAllowProxyCreation() ) {
			impl = load( event, persister, keyToLoad, options );
			if ( impl == null ) {
				event.getSession()
						.getFactory()
						.getEntityNotFoundDelegate()
						.handleEntityNotFound( persister.getEntityName(), keyToLoad.getIdentifier() );
			}
		}

		return persistenceContext.narrowProxy( proxy, persister, keyToLoad, impl );
	}

	/**
	 * If there is already a corresponding proxy associated with the
	 * persistence context, return it; otherwise create a proxy, associate it
	 * with the persistence context, and return the just-created proxy.
	 *
	 * @param event The initiating load request event
	 * @param persister The persister corresponding to the entity to be loaded
	 * @param keyToLoad The key of the entity to be loaded
	 * @param options The defined load options
	 * @param persistenceContext The originating session
	 *
	 * @return The created/existing proxy
	 */
	private Object createProxyIfNecessary(
			final LoadEvent event,
			final EntityPersister persister,
			final EntityKey keyToLoad,
			final LoadEventListener.LoadType options,
			final PersistenceContext persistenceContext) {
		Object existing = persistenceContext.getEntity( keyToLoad );
		final boolean traceEnabled = LOG.isTraceEnabled();
		if ( existing != null ) {
			// return existing object or initialized proxy (unless deleted)
			if ( traceEnabled ) {
				LOG.trace( "Entity found in session cache" );
			}
			if ( options.isCheckDeleted() ) {
				EntityEntry entry = persistenceContext.getEntry( existing );
				Status status = entry.getStatus();
				if ( status == Status.DELETED || status == Status.GONE ) {
					return null;
				}
			}
			return existing;
		}
		if ( traceEnabled ) {
			LOG.trace( "Creating new proxy for entity" );
		}
		return createProxy( event, persister, keyToLoad, persistenceContext );
	}

	private Object createProxy(
			LoadEvent event,
			EntityPersister persister,
			EntityKey keyToLoad,
			PersistenceContext persistenceContext) {
		// return new uninitialized proxy
		Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
		persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
		persistenceContext.addProxy( keyToLoad, proxy );
		return proxy;
	}

	/**
	 * If the class to be loaded has been configured with a cache, then lock
	 * given id in that cache and then perform the load.
	 *
	 * @param event The initiating load request event
	 * @param persister The persister corresponding to the entity to be loaded
	 * @param keyToLoad The key of the entity to be loaded
	 * @param options The defined load options
	 * @param source The originating session
	 *
	 * @return The loaded entity
	 */
	private Object lockAndLoad(
			final LoadEvent event,
			final EntityPersister persister,
			final EntityKey keyToLoad,
			final LoadEventListener.LoadType options,
			final SessionImplementor source) {
		SoftLock lock = null;
		final Object ck;
		final EntityDataAccess cache = persister.getCacheAccessStrategy();
		final boolean canWriteToCache = persister.canWriteToCache();
		if ( canWriteToCache ) {
			ck = cache.generateCacheKey(
					event.getEntityId(),
					persister,
					source.getFactory(),
					source.getTenantIdentifier()
			);
			lock = cache.lockItem( source, ck, null );
		}
		else {
			ck = null;
		}

		Object entity;
		try {
			entity = load( event, persister, keyToLoad, options );
		}
		finally {
			if ( canWriteToCache ) {
				cache.unlockItem( source, ck, lock );
			}
		}

		return event.getSession().getPersistenceContext().proxyFor( persister, keyToLoad, entity );
	}


	/**
	 * Coordinates the efforts to load a given entity.  First, an attempt is
	 * made to load the entity from the session-level cache.  If not found there,
	 * an attempt is made to locate it in second-level cache.  Lastly, an
	 * attempt is made to load it directly from the datasource.
	 *
	 * @param event The load event
	 * @param persister The persister for the entity being requested for load
	 * @param keyToLoad The EntityKey representing the entity to be loaded.
	 * @param options The load options.
	 *
	 * @return The loaded entity, or null.
	 */
	private Object doLoad(
			final LoadEvent event,
			final EntityPersister persister,
			final EntityKey keyToLoad,
			final LoadEventListener.LoadType options) {

		final EventSource session = event.getSession();
		final boolean traceEnabled = LOG.isTraceEnabled();
		if ( traceEnabled ) {
			LOG.tracev(
					"Attempting to resolve: {0}",
					MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
			);
		}

		Object entity = loadFromSessionCache( event, keyToLoad, options );
		if ( entity == REMOVED_ENTITY_MARKER ) {
			LOG.debug( "Load request found matching entity in context, but it is scheduled for removal; returning null" );
			return null;
		}
		if ( entity == INCONSISTENT_RTN_CLASS_MARKER ) {
			LOG.debug(
					"Load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null"
			);
			return null;
		}
		if ( entity != null ) {
			if ( traceEnabled ) {
				LOG.tracev(
						"Resolved object in session cache: {0}",
						MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
				);
			}
			return entity;
		}

		entity = loadFromSecondLevelCache( event, persister, keyToLoad );
		if ( entity != null ) {
			if ( traceEnabled ) {
				LOG.tracev(
						"Resolved object in second-level cache: {0}",
						MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
				);
			}
		}
		else {
			if ( traceEnabled ) {
				LOG.tracev(
						"Object not resolved in any cache: {0}",
						MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
				);
			}
			entity = loadFromDatasource( event, persister );
		}

		if ( entity != null && persister.hasNaturalIdentifier() ) {
			final PersistenceContext persistenceContext = session.getPersistenceContext();
			final PersistenceContext.NaturalIdHelper naturalIdHelper = persistenceContext.getNaturalIdHelper();
			naturalIdHelper.cacheNaturalIdCrossReferenceFromLoad(
					persister,
					event.getEntityId(),
					naturalIdHelper.extractNaturalIdValues(
							entity,
							persister
					)
			);
		}

		return entity;
	}

	/**
	 * Performs the process of loading an entity from the configured
	 * underlying datasource.
	 *
	 * @param event The load event
	 * @param persister The persister for the entity being requested for load
	 *
	 * @return The object loaded from the datasource, or null if not found.
	 */
	@SuppressWarnings("WeakerAccess")
	protected Object loadFromDatasource(
			final LoadEvent event,
			final EntityPersister persister) {
		Object entity = persister.load(
				event.getEntityId(),
				event.getInstanceToLoad(),
				event.getLockOptions(),
				event.getSession()
		);

		final StatisticsImplementor statistics = event.getSession().getFactory().getStatistics();
		if ( event.isAssociationFetch() && statistics.isStatisticsEnabled() ) {
			statistics.fetchEntity( event.getEntityClassName() );
		}

		return entity;
	}

	/**
	 * Attempts to locate the entity in the session-level cache.
	 * 

* If allowed to return nulls, then if the entity happens to be found in * the session cache, we check the entity type for proper handling * of entity hierarchies. *

* If checkDeleted was set to true, then if the entity is found in the * session-level cache, it's current status within the session cache * is checked to see if it has previously been scheduled for deletion. * * @param event The load event * @param keyToLoad The EntityKey representing the entity to be loaded. * @param options The load options. * * @return The entity from the session-level cache, or null. * * @throws HibernateException Generally indicates problems applying a lock-mode. */ protected Object loadFromSessionCache( final LoadEvent event, final EntityKey keyToLoad, final LoadEventListener.LoadType options) throws HibernateException { SessionImplementor session = event.getSession(); Object old = session.getEntityUsingInterceptor( keyToLoad ); if ( old != null ) { // this object was already loaded EntityEntry oldEntry = session.getPersistenceContext().getEntry( old ); if ( options.isCheckDeleted() ) { Status status = oldEntry.getStatus(); if ( status == Status.DELETED || status == Status.GONE ) { return REMOVED_ENTITY_MARKER; } } if ( options.isAllowNulls() ) { final EntityPersister persister = event.getSession() .getFactory() .getEntityPersister( keyToLoad.getEntityName() ); if ( !persister.isInstance( old ) ) { return INCONSISTENT_RTN_CLASS_MARKER; } } upgradeLock( old, oldEntry, event.getLockOptions(), event.getSession() ); } return old; } /** * Attempts to load the entity from the second-level cache. * * @param event The load event * @param persister The persister for the entity being requested for load * * @return The entity from the second-level cache, or null. */ private Object loadFromSecondLevelCache( final LoadEvent event, final EntityPersister persister, final EntityKey entityKey) { final SessionImplementor source = event.getSession(); final boolean useCache = persister.canReadFromCache() && source.getCacheMode().isGetEnabled() && event.getLockMode().lessThan( LockMode.READ ); if ( !useCache ) { // we can't use cache here return null; } final Object ce = getFromSharedCache( event, persister, source ); if ( ce == null ) { // nothing was found in cache return null; } return processCachedEntry( event, persister, ce, source, entityKey ); } private Object processCachedEntry( final LoadEvent event, final EntityPersister persister, final Object ce, final SessionImplementor source, final EntityKey entityKey) { CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure().destructure( ce, source.getFactory() ); if(entry.isReferenceEntry()) { if( event.getInstanceToLoad() != null ) { throw new HibernateException( "Attempt to load entity [%s] from cache using provided object instance, but cache " + "is storing references: "+ event.getEntityId()); } else { return convertCacheReferenceEntryToEntity( (ReferenceCacheEntryImpl) entry, event.getSession(), entityKey); } } else { Object entity = convertCacheEntryToEntity( entry, event.getEntityId(), persister, event, entityKey ); if ( !persister.isInstance( entity ) ) { throw new WrongClassException( "loaded object was of wrong class " + entity.getClass(), event.getEntityId(), persister.getEntityName() ); } return entity; } } private Object getFromSharedCache( final LoadEvent event, final EntityPersister persister, SessionImplementor source ) { final EntityDataAccess cache = persister.getCacheAccessStrategy(); final SessionFactoryImplementor factory = source.getFactory(); final Object ck = cache.generateCacheKey( event.getEntityId(), persister, factory, source.getTenantIdentifier() ); final Object ce = CacheHelper.fromSharedCache( source, ck, persister.getCacheAccessStrategy() ); final StatisticsImplementor statistics = factory.getStatistics(); if ( statistics.isStatisticsEnabled() ) { if ( ce == null ) { statistics.entityCacheMiss( StatsHelper.INSTANCE.getRootEntityRole( persister ), cache.getRegion().getName() ); } else { statistics.entityCacheHit( StatsHelper.INSTANCE.getRootEntityRole( persister ), cache.getRegion().getName() ); } } return ce; } private Object convertCacheReferenceEntryToEntity( ReferenceCacheEntryImpl referenceCacheEntry, EventSource session, EntityKey entityKey) { final Object entity = referenceCacheEntry.getReference(); if ( entity == null ) { throw new IllegalStateException( "Reference cache entry contained null : " + referenceCacheEntry.toString()); } else { makeEntityCircularReferenceSafe( referenceCacheEntry, session, entity, entityKey ); return entity; } } private void makeEntityCircularReferenceSafe(ReferenceCacheEntryImpl referenceCacheEntry, EventSource session, Object entity, EntityKey entityKey) { // make it circular-reference safe final StatefulPersistenceContext statefulPersistenceContext = (StatefulPersistenceContext) session.getPersistenceContext(); if ( (entity instanceof ManagedEntity) ) { statefulPersistenceContext.addReferenceEntry( entity, Status.READ_ONLY ); } else { TwoPhaseLoad.addUninitializedCachedEntity( entityKey, entity, referenceCacheEntry.getSubclassPersister(), LockMode.NONE, referenceCacheEntry.getVersion(), session ); } statefulPersistenceContext.initializeNonLazyCollections(); } private Object convertCacheEntryToEntity( CacheEntry entry, Serializable entityId, EntityPersister persister, LoadEvent event, EntityKey entityKey) { final EventSource session = event.getSession(); final SessionFactoryImplementor factory = session.getFactory(); final EntityPersister subclassPersister; if ( traceEnabled ) { LOG.tracef( "Converting second-level cache entry [%s] into entity : %s", entry, MessageHelper.infoString( persister, entityId, factory ) ); } final Object entity; subclassPersister = factory.getEntityPersister( entry.getSubclass() ); final Object optionalObject = event.getInstanceToLoad(); entity = optionalObject == null ? session.instantiate( subclassPersister, entityId ) : optionalObject; // make it circular-reference safe TwoPhaseLoad.addUninitializedCachedEntity( entityKey, entity, subclassPersister, LockMode.NONE, entry.getVersion(), session ); final PersistenceContext persistenceContext = session.getPersistenceContext(); final Object[] values; final Object version; final boolean isReadOnly; final Type[] types = subclassPersister.getPropertyTypes(); // initializes the entity by (desired) side-effect values = ( (StandardCacheEntryImpl) entry ).assemble( entity, entityId, subclassPersister, session.getInterceptor(), session ); if ( ( (StandardCacheEntryImpl) entry ).isDeepCopyNeeded() ) { TypeHelper.deepCopy( values, types, subclassPersister.getPropertyUpdateability(), values, session ); } version = Versioning.getVersion( values, subclassPersister ); LOG.tracef( "Cached Version : %s", version ); final Object proxy = persistenceContext.getProxy( entityKey ); if ( proxy != null ) { // there is already a proxy for this impl // only set the status to read-only if the proxy is read-only isReadOnly = ( (HibernateProxy) proxy ).getHibernateLazyInitializer().isReadOnly(); } else { isReadOnly = session.isDefaultReadOnly(); } persistenceContext.addEntry( entity, ( isReadOnly ? Status.READ_ONLY : Status.MANAGED ), values, null, entityId, version, LockMode.NONE, true, subclassPersister, false ); subclassPersister.afterInitialize( entity, session ); persistenceContext.initializeNonLazyCollections(); //PostLoad is needed for EJB3 PostLoadEvent postLoadEvent = event.getPostLoadEvent() .setEntity( entity ) .setId( entityId ) .setPersister( persister ); for ( PostLoadEventListener listener : postLoadEventListeners( session ) ) { listener.onPostLoad( postLoadEvent ); } return entity; } private Iterable postLoadEventListeners(EventSource session) { return session .getFactory() .getServiceRegistry() .getService( EventListenerRegistry.class ) .getEventListenerGroup( EventType.POST_LOAD ) .listeners(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy