package org.hibernate.engine.internal;
import java.io.Serializable;
import org.hibernate.AssertionFailure;
import org.hibernate.CacheMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.engine.profile.Fetch;
import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionEventListenerManager;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PostLoadEventListener;
import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.event.spi.PreLoadEventListener;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.stat.internal.StatsHelper;
import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper;
import org.jboss.logging.Logger;
public final class TwoPhaseLoad {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
TwoPhaseLoad.class.getName()
);
private TwoPhaseLoad() {
}
public static void postHydrate(
final EntityPersister persister,
final Serializable id,
final Object[] values,
final Object rowId,
final Object object,
final LockMode lockMode,
final SharedSessionContractImplementor session) {
final Object version = Versioning.getVersion( values, persister );
session.getPersistenceContext().addEntry(
object,
Status.LOADING,
values,
rowId,
id,
version,
lockMode,
true,
persister,
false
);
if ( version != null && LOG.isTraceEnabled() ) {
final String versionStr = persister.isVersioned()
? persister.getVersionType().toLoggableString( version, session.getFactory() )
: "null";
LOG.tracef( "Version: %s", versionStr );
}
}
public static void initializeEntity(
final Object entity,
final boolean readOnly,
final SharedSessionContractImplementor session,
final PreLoadEvent preLoadEvent) {
final PersistenceContext persistenceContext = session.getPersistenceContext();
final EntityEntry entityEntry = persistenceContext.getEntry( entity );
if ( entityEntry == null ) {
throw new AssertionFailure( "possible non-threadsafe access to the session" );
}
doInitializeEntity( entity, entityEntry, readOnly, session, preLoadEvent );
}
private static void doInitializeEntity(
final Object entity,
final EntityEntry entityEntry,
final boolean readOnly,
final SharedSessionContractImplementor session,
final PreLoadEvent preLoadEvent) throws HibernateException {
final PersistenceContext persistenceContext = session.getPersistenceContext();
final EntityPersister persister = entityEntry.getPersister();
final Serializable id = entityEntry.getId();
final Object[] hydratedState = entityEntry.getLoadedState();
final boolean debugEnabled = LOG.isDebugEnabled();
if ( debugEnabled ) {
LOG.debugf(
"Resolving associations for %s",
MessageHelper.infoString( persister, id, session.getFactory() )
);
}
String entityName = persister.getEntityName();
String[] propertyNames = persister.getPropertyNames();
final Type[] types = persister.getPropertyTypes();
for ( int i = 0; i < hydratedState.length; i++ ) {
final Object value = hydratedState[i];
Boolean overridingEager = getOverridingEager( session, entityName, propertyNames[i], types[i] );
if ( value == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
if ( types[i].isCollectionType() ) {
types[i].resolve( value, session, entity, overridingEager );
}
}
else if ( value != PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
hydratedState[i] = types[i].resolve( value, session, entity, overridingEager );
}
}
if ( session.isEventSource() ) {
preLoadEvent.setEntity( entity ).setState( hydratedState ).setId( id ).setPersister( persister );
final EventListenerGroup<PreLoadEventListener> listenerGroup = session
.getFactory()
.getServiceRegistry()
.getService( EventListenerRegistry.class )
.getEventListenerGroup( EventType.PRE_LOAD );
for ( PreLoadEventListener listener : listenerGroup.listeners() ) {
listener.onPreLoad( preLoadEvent );
}
}
persister.setPropertyValues( entity, hydratedState );
final SessionFactoryImplementor factory = session.getFactory();
if ( persister.canWriteToCache() && session.getCacheMode().isPutEnabled() ) {
if ( debugEnabled ) {
LOG.debugf(
"Adding entity to second-level cache: %s",
MessageHelper.infoString( persister, id, session.getFactory() )
);
}
final Object version = Versioning.getVersion( hydratedState, persister );
final CacheEntry entry = persister.buildCacheEntry( entity, hydratedState, version, session );
final EntityDataAccess cache = persister.getCacheAccessStrategy();
final Object cacheKey = cache.generateCacheKey( id, persister, factory, session.getTenantIdentifier() );
if ( session.getPersistenceContext().wasInsertedDuringTransaction( persister, id ) ) {
cache.update(
session,
cacheKey,
persister.getCacheEntryStructure().structure( entry ),
version,
version
);
}
else {
final SessionEventListenerManager eventListenerManager = session.getEventListenerManager();
try {
eventListenerManager.cachePutStart();
final boolean put = cache.putFromLoad(
session,
cacheKey,
persister.getCacheEntryStructure().structure( entry ),
version,
useMinimalPuts( session, entityEntry )
);
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatistics().entityCachePut(
StatsHelper.INSTANCE.getRootEntityRole( persister ),
cache.getRegion().getName()
);
}
}
finally {
eventListenerManager.cachePutEnd();
}
}
}
if ( persister.hasNaturalIdentifier() ) {
persistenceContext.getNaturalIdHelper().cacheNaturalIdCrossReferenceFromLoad(
persister,
id,
persistenceContext.getNaturalIdHelper().extractNaturalIdValues( hydratedState, persister )
);
}
boolean isReallyReadOnly = readOnly;
if ( !persister.isMutable() ) {
isReallyReadOnly = true;
}
else {
final Object proxy = persistenceContext.getProxy( entityEntry.getEntityKey() );
if ( proxy != null ) {
isReallyReadOnly = ( (HibernateProxy) proxy ).getHibernateLazyInitializer().isReadOnly();
}
}
if ( isReallyReadOnly ) {
persistenceContext.setEntryStatus( entityEntry, Status.READ_ONLY );
}
else {
TypeHelper.deepCopy(
hydratedState,
persister.getPropertyTypes(),
persister.getPropertyUpdateability(),
hydratedState,
session
);
persistenceContext.setEntryStatus( entityEntry, Status.MANAGED );
}
persister.afterInitialize( entity, session );
if ( debugEnabled ) {
LOG.debugf(
"Done materializing entity %s",
MessageHelper.infoString( persister, id, session.getFactory() )
);
}
if ( factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatistics().loadEntity( persister.getEntityName() );
}
}
private static Boolean getOverridingEager(
SharedSessionContractImplementor session,
String entityName,
String associationName,
Type type) {
if ( type.isAssociationType() || type.isCollectionType() ) {
Boolean overridingEager = isEagerFetchProfile( session, entityName + "." + associationName );
if ( LOG.isDebugEnabled() ) {
if ( overridingEager != null ) {
LOG.debugf(
"Overriding eager fetching using active fetch profile. EntityName: %s, associationName: %s, eager fetching: %s",
entityName,
associationName,
overridingEager
);
}
}
return overridingEager;
}
return null;
}
private static Boolean isEagerFetchProfile(SharedSessionContractImplementor session, String role) {
LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
for ( String fetchProfileName : loadQueryInfluencers.getEnabledFetchProfileNames() ) {
FetchProfile fp = session.getFactory().getFetchProfile( fetchProfileName );
Fetch fetch = fp.getFetchByRole( role );
if ( fetch != null && Fetch.Style.JOIN == fetch.getStyle() ) {
return true;
}
}
return null;
}
public static void postLoad(
final Object entity,
final SharedSessionContractImplementor session,
final PostLoadEvent postLoadEvent) {
if ( session.isEventSource() ) {
final PersistenceContext persistenceContext
= session.getPersistenceContext();
final EntityEntry entityEntry = persistenceContext.getEntry( entity );
postLoadEvent.setEntity( entity ).setId( entityEntry.getId() ).setPersister( entityEntry.getPersister() );
final EventListenerGroup<PostLoadEventListener> listenerGroup = session.getFactory()
.getServiceRegistry()
.getService( EventListenerRegistry.class )
.getEventListenerGroup( EventType.POST_LOAD );
for ( PostLoadEventListener listener : listenerGroup.listeners() ) {
listener.onPostLoad( postLoadEvent );
}
}
}
private static boolean useMinimalPuts(SharedSessionContractImplementor session, EntityEntry entityEntry) {
if ( session.getFactory().getSessionFactoryOptions().isMinimalPutsEnabled() ) {
return session.getCacheMode() != CacheMode.REFRESH;
}
else {
return entityEntry.getPersister().hasLazyProperties()
&& entityEntry.getPersister().isLazyPropertiesCacheable();
}
}
public static void addUninitializedEntity(
final EntityKey key,
final Object object,
final EntityPersister persister,
final LockMode lockMode,
final SharedSessionContractImplementor session) {
session.getPersistenceContext().addEntity(
object,
Status.LOADING,
null,
key,
null,
lockMode,
true,
persister,
false
);
}
public static void addUninitializedCachedEntity(
final EntityKey key,
final Object object,
final EntityPersister persister,
final LockMode lockMode,
final Object version,
final SharedSessionContractImplementor session) {
session.getPersistenceContext().addEntity(
object,
Status.LOADING,
null,
key,
version,
lockMode,
true,
persister,
false
);
}
}