HHH-10289 - CPU performance regression in StatefulPersistenceContext.addEntry()
This commit is contained in:
parent
1568f89426
commit
da4593de1b
|
@ -42,7 +42,6 @@ import org.hibernate.engine.spi.CachedNaturalIdValueSource;
|
|||
import org.hibernate.engine.spi.CollectionEntry;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityEntryFactory;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.EntityUniqueKey;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
|
@ -485,13 +484,38 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
final boolean disableVersionIncrement) {
|
||||
final EntityEntry e;
|
||||
|
||||
if( (entity instanceof ManagedEntity) && ((ManagedEntity) entity).$$_hibernate_getEntityEntry() != null && status == Status.READ_ONLY) {
|
||||
e = ((ManagedEntity) entity).$$_hibernate_getEntityEntry();
|
||||
e.setStatus( status );
|
||||
/*
|
||||
IMPORTANT!!!
|
||||
|
||||
The following instanceof checks and castings are intentional.
|
||||
|
||||
DO NOT REFACTOR to make calls through the EntityEntryFactory interface, which would result
|
||||
in polymorphic call sites which will severely impact performance.
|
||||
|
||||
When a virtual method is called via an interface the JVM needs to resolve which concrete
|
||||
implementation to call. This takes CPU cycles and is a performance penalty. It also prevents method
|
||||
in-ling which further degrades performance. Casting to an implementation and making a direct method call
|
||||
removes the virtual call, and allows the methods to be in-lined. In this critical code path, it has a very
|
||||
large impact on performance to make virtual method calls.
|
||||
*/
|
||||
if (persister.getEntityEntryFactory() instanceof MutableEntityEntryFactory) {
|
||||
//noinspection RedundantCast
|
||||
e = ( (MutableEntityEntryFactory) persister.getEntityEntryFactory() ).createEntityEntry(
|
||||
status,
|
||||
loadedState,
|
||||
rowId,
|
||||
id,
|
||||
version,
|
||||
lockMode,
|
||||
existsInDatabase,
|
||||
persister,
|
||||
disableVersionIncrement,
|
||||
this
|
||||
);
|
||||
}
|
||||
else {
|
||||
final EntityEntryFactory entityEntryFactory = persister.getEntityEntryFactory();
|
||||
e = entityEntryFactory.createEntityEntry(
|
||||
//noinspection RedundantCast
|
||||
e = ( (ImmutableEntityEntryFactory) persister.getEntityEntryFactory() ).createEntityEntry(
|
||||
status,
|
||||
loadedState,
|
||||
rowId,
|
||||
|
@ -506,12 +530,22 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
}
|
||||
|
||||
entityEntryContext.addEntityEntry( entity, e );
|
||||
// entityEntries.put(entity, e);
|
||||
|
||||
setHasNonReadOnlyEnties( status );
|
||||
return e;
|
||||
}
|
||||
|
||||
public EntityEntry addReferenceEntry(
|
||||
final Object entity,
|
||||
final Status status) {
|
||||
|
||||
((ManagedEntity)entity).$$_hibernate_getEntityEntry().setStatus( status );
|
||||
entityEntryContext.addEntityEntry( entity, ((ManagedEntity)entity).$$_hibernate_getEntityEntry() );
|
||||
|
||||
setHasNonReadOnlyEnties( status );
|
||||
return ((ManagedEntity)entity).$$_hibernate_getEntityEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsCollection(PersistentCollection collection) {
|
||||
return collectionEntries.containsKey( collection );
|
||||
|
|
|
@ -20,14 +20,17 @@ 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.EventListenerGroup;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
|
@ -603,7 +606,21 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
}
|
||||
|
||||
CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure().destructure( ce, factory );
|
||||
Object entity = convertCacheEntryToEntity( entry, event.getEntityId(), persister, event, entityKey );
|
||||
final Object entity;
|
||||
if(entry.isReferenceEntry()) {
|
||||
if( event.getInstanceToLoad() != null ) {
|
||||
throw new HibernateException(
|
||||
String.format( "Attempt to load entity [%s] from cache using provided object instance, but cache " +
|
||||
"is storing references: "+ event.getEntityId()));
|
||||
}
|
||||
else {
|
||||
entity = convertCacheReferenceEntryToEntity( (ReferenceCacheEntryImpl) entry,
|
||||
event.getEntityId(), persister, event.getSession(), entityKey );
|
||||
}
|
||||
}
|
||||
else {
|
||||
entity = convertCacheEntryToEntity( entry, event.getEntityId(), persister, event, entityKey );
|
||||
}
|
||||
|
||||
if ( !persister.isInstance( entity ) ) {
|
||||
throw new WrongClassException(
|
||||
|
@ -616,6 +633,74 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
return entity;
|
||||
}
|
||||
|
||||
private Object convertCacheReferenceEntryToEntity(
|
||||
ReferenceCacheEntryImpl referenceCacheEntry,
|
||||
Serializable entityId,
|
||||
EntityPersister persister,
|
||||
EventSource session,
|
||||
EntityKey entityKey) {
|
||||
final Object entity = referenceCacheEntry.getReference();
|
||||
|
||||
if ( entity == null ) {
|
||||
throw new IllegalStateException(
|
||||
"Reference cache entry contained null : " + entityId);
|
||||
}
|
||||
else {
|
||||
makeEntityCircularReferenceSafe(referenceCacheEntry, entityId, session, entity, entityKey);
|
||||
//PostLoad is needed for EJB3
|
||||
EventListenerGroup<PostLoadEventListener> evenListenerGroup = getEvenListenerGroup(session);
|
||||
|
||||
if(!evenListenerGroup.isEmpty()) {
|
||||
postLoad(session, evenListenerGroup.listeners(), entity, entityId, persister);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
private void postLoad(EventSource session, Iterable<PostLoadEventListener> listeners,
|
||||
Object entity, Serializable entityId, EntityPersister persister) {
|
||||
PostLoadEvent postLoadEvent = new PostLoadEvent(session)
|
||||
.setEntity(entity)
|
||||
.setId(entityId)
|
||||
.setPersister(persister);
|
||||
|
||||
for (PostLoadEventListener listener : listeners) {
|
||||
listener.onPostLoad(postLoadEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeEntityCircularReferenceSafe(ReferenceCacheEntryImpl referenceCacheEntry,
|
||||
Serializable entityId,
|
||||
EventSource session,
|
||||
Object entity,
|
||||
EntityKey entityKey) {
|
||||
|
||||
final EntityPersister subclassPersister = referenceCacheEntry.getSubclassPersister();
|
||||
// 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,
|
||||
subclassPersister,
|
||||
LockMode.NONE,
|
||||
referenceCacheEntry.areLazyPropertiesUnfetched(),
|
||||
referenceCacheEntry.getVersion(),
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
subclassPersister.afterInitialize( entity, referenceCacheEntry.areLazyPropertiesUnfetched(), session );
|
||||
statefulPersistenceContext.initializeNonLazyCollections();
|
||||
}
|
||||
|
||||
private Object convertCacheEntryToEntity(
|
||||
CacheEntry entry,
|
||||
Serializable entityId,
|
||||
|
@ -636,38 +721,12 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
}
|
||||
|
||||
final Object entity;
|
||||
if ( entry.isReferenceEntry() ) {
|
||||
final Object optionalObject = event.getInstanceToLoad();
|
||||
if ( optionalObject != null ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"Attempt to load entity [%s] from cache using provided object instance, but cache " +
|
||||
"is storing references",
|
||||
MessageHelper.infoString( persister, entityId, factory )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
ReferenceCacheEntryImpl referenceCacheEntry = (ReferenceCacheEntryImpl) entry;
|
||||
entity = referenceCacheEntry.getReference();
|
||||
if ( entity == null ) {
|
||||
throw new IllegalStateException(
|
||||
"Reference cache entry contained null : " + MessageHelper.infoString(
|
||||
persister,
|
||||
entityId,
|
||||
factory
|
||||
)
|
||||
);
|
||||
}
|
||||
subclassPersister = referenceCacheEntry.getSubclassPersister();
|
||||
}
|
||||
else {
|
||||
subclassPersister = factory.getEntityPersister( entry.getSubclass() );
|
||||
final Object optionalObject = event.getInstanceToLoad();
|
||||
entity = optionalObject == null
|
||||
? session.instantiate( subclassPersister, entityId )
|
||||
: optionalObject;
|
||||
}
|
||||
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(
|
||||
|
@ -683,38 +742,32 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
final Object[] values;
|
||||
final Object version;
|
||||
final boolean isReadOnly;
|
||||
if ( entry.isReferenceEntry() ) {
|
||||
values = null;
|
||||
version = null;
|
||||
isReadOnly = true;
|
||||
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
isReadOnly = session.isDefaultReadOnly();
|
||||
}
|
||||
|
||||
persistenceContext.addEntry(
|
||||
|
@ -848,4 +901,14 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
|
|||
.getEventListenerGroup( EventType.POST_LOAD )
|
||||
.listeners();
|
||||
}
|
||||
|
||||
private EventListenerGroup<PostLoadEventListener> getEvenListenerGroup(EventSource session) {
|
||||
return session
|
||||
.getFactory()
|
||||
.getServiceRegistry()
|
||||
.getService( EventListenerRegistry.class)
|
||||
.getEventListenerGroup( EventType.POST_LOAD);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue