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

org.hibernate.loader.entity.CacheEntityLoaderHelper 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.loader.entity;

import java.io.Serializable;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.WrongClassException;
import org.hibernate.cache.spi.access.EntityDataAccess;
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.internal.AbstractLockUpgradeEventListener;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.LoadEvent;
import org.hibernate.event.spi.LoadEventListener;
import org.hibernate.event.spi.PostLoadEvent;
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.stat.internal.StatsHelper;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper;

/**
 * @author Vlad Mihalcea
 */
public class CacheEntityLoaderHelper extends AbstractLockUpgradeEventListener {

	public static final CacheEntityLoaderHelper INSTANCE = new CacheEntityLoaderHelper();

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

	public enum EntityStatus {
		MANAGED,
		REMOVED_ENTITY_MARKER,
		INCONSISTENT_RTN_CLASS_MARKER
	}

	private CacheEntityLoaderHelper() {
	}

	/**
	 * 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. */ public PersistenceContextEntry 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 ) { LOG.debug( "Load request found matching entity in context, but it is scheduled for removal; returning null" ); return new PersistenceContextEntry( old, EntityStatus.REMOVED_ENTITY_MARKER ); } } if ( options.isAllowNulls() ) { final EntityPersister persister = event.getSession() .getFactory() .getEntityPersister( keyToLoad.getEntityName() ); if ( !persister.isInstance( old ) ) { LOG.debug( "Load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null" ); return new PersistenceContextEntry( old, EntityStatus.INCONSISTENT_RTN_CLASS_MARKER ); } } upgradeLock( old, oldEntry, event.getLockOptions(), event.getSession() ); } return new PersistenceContextEntry( old, EntityStatus.MANAGED ); } /** * 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. */ public 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 ( LOG.isTraceEnabled() ) { 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 ); session.getSessionFactory() .getFastSessionServices() .firePostLoadEvent( postLoadEvent ); return entity; } public static class PersistenceContextEntry { private final Object entity; private EntityStatus status; public PersistenceContextEntry(Object entity, EntityStatus status) { this.entity = entity; this.status = status; } public Object getEntity() { return entity; } public EntityStatus getStatus() { return status; } public boolean isManaged() { return EntityStatus.MANAGED == status; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy