org.hibernate.event.internal.DefaultLoadEventListener Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* 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();
}
}