attempt to reduce allocations of LockOptions

- Sanne says we were allocating too many of these, so add a static instance
  of LockOptions for each LockMode
- just generally rationalize the code that deals with defaulting LockOptions
- change the impl of CascadingActions.LOCK because lock scope has nothing
  to do with cascading, and I don't see any reason why the LockOptions should
  not simply propagate if cascading is explicitly turned on
This commit is contained in:
Gavin King 2022-11-08 16:45:42 +01:00
parent 39bef7bc70
commit 3f7133f80b
13 changed files with 121 additions and 151 deletions

View File

@ -208,4 +208,37 @@ public enum LockMode {
throw new IllegalArgumentException( "Unable to interpret LockMode reference from incoming external form : " + externalForm ); throw new IllegalArgumentException( "Unable to interpret LockMode reference from incoming external form : " + externalForm );
} }
/**
* @return an instance of {@link LockOptions} with this lock mode, and
* all other settings defaulted.
*/
public LockOptions toLockOptions() {
// we have to do this in a big switch to
// avoid circularities in the constructor
switch (this) {
case NONE:
return LockOptions.NONE;
case READ:
return LockOptions.READ;
case OPTIMISTIC:
return LockOptions.OPTIMISTIC;
case OPTIMISTIC_FORCE_INCREMENT:
return LockOptions.OPTIMISTIC_FORCE_INCREMENT;
case UPGRADE_NOWAIT:
return LockOptions.UPGRADE_NOWAIT;
case UPGRADE_SKIPLOCKED:
return LockOptions.UPGRADE_SKIPLOCKED;
case PESSIMISTIC_READ:
return LockOptions.PESSIMISTIC_READ;
case PESSIMISTIC_WRITE:
return LockOptions.PESSIMISTIC_WRITE;
case PESSIMISTIC_FORCE_INCREMENT:
return LockOptions.PESSIMISTIC_FORCE_INCREMENT;
case WRITE:
throw new UnsupportedOperationException("WRITE is not a valid LockMode as an argument");
default:
throw new IllegalStateException( "Unexpected value: " + this );
}
}
} }

View File

@ -63,13 +63,49 @@ public class LockOptions implements Serializable {
*/ */
public static final LockOptions READ = new LockOptions( true, LockMode.READ ); public static final LockOptions READ = new LockOptions( true, LockMode.READ );
/**
* Represents {@link LockMode#OPTIMISTIC}.
*/
static final LockOptions OPTIMISTIC = new LockOptions( true, LockMode.OPTIMISTIC );
/**
* Represents {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}, to which
* timeout and scope are not applicable.
*/
static final LockOptions OPTIMISTIC_FORCE_INCREMENT = new LockOptions( true, LockMode.OPTIMISTIC_FORCE_INCREMENT );
/**
* Represents {@link LockMode#PESSIMISTIC_READ}.
*/
static final LockOptions PESSIMISTIC_READ = new LockOptions( true, LockMode.PESSIMISTIC_READ );
/**
* Represents {@link LockMode#PESSIMISTIC_WRITE}.
*/
static final LockOptions PESSIMISTIC_WRITE = new LockOptions( true, LockMode.PESSIMISTIC_WRITE );
/**
* Represents {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}.
*/
static final LockOptions PESSIMISTIC_FORCE_INCREMENT = new LockOptions( true, LockMode.PESSIMISTIC_FORCE_INCREMENT );
/**
* Represents {@link LockMode#UPGRADE_NOWAIT}.
*/
static final LockOptions UPGRADE_NOWAIT = new LockOptions( true, LockMode.UPGRADE_NOWAIT );
/**
* Represents {@link LockMode#UPGRADE_SKIPLOCKED}.
*/
static final LockOptions UPGRADE_SKIPLOCKED = new LockOptions( true, LockMode.UPGRADE_SKIPLOCKED );
/** /**
* Represents {@link LockMode#PESSIMISTIC_WRITE} with * Represents {@link LockMode#PESSIMISTIC_WRITE} with
* {@linkplain #WAIT_FOREVER no timeout}, and * {@linkplain #WAIT_FOREVER no timeout}, and
* {@linkplain PessimisticLockScope#NORMAL no extension of the * {@linkplain PessimisticLockScope#NORMAL no extension of the
* lock to owned collections}. * lock to owned collections}.
*/ */
public static final LockOptions UPGRADE = new LockOptions(true, LockMode.PESSIMISTIC_WRITE); public static final LockOptions UPGRADE = PESSIMISTIC_WRITE;
/** /**
* Indicates that the database should not wait at all to acquire * Indicates that the database should not wait at all to acquire

View File

@ -92,18 +92,7 @@ public class CascadingActions {
LockOptions lockOptions, LockOptions lockOptions,
boolean isCascadeDeleteEnabled) { boolean isCascadeDeleteEnabled) {
LOG.tracev( "Cascading to lock: {0}", entityName ); LOG.tracev( "Cascading to lock: {0}", entityName );
LockMode lockMode = LockMode.NONE; session.lock( entityName, child, lockOptions );
LockOptions lr = new LockOptions();
if ( lockOptions != null ) {
lr.setTimeOut( lockOptions.getTimeOut() );
lr.setScope( lockOptions.getScope() );
lr.setFollowOnLocking( lockOptions.getFollowOnLocking() );
if ( lockOptions.getScope() ) {
lockMode = lockOptions.getLockMode();
}
}
lr.setLockMode( lockMode );
session.buildLockRequest( lr ).lock( entityName, child );
} }
@Override @Override

View File

@ -189,7 +189,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
if ( currentLockMode.greaterThan( requestedLockMode ) ) { if ( currentLockMode.greaterThan( requestedLockMode ) ) {
// the requested lock-mode is less restrictive than the current one // the requested lock-mode is less restrictive than the current one
// - pass along the current lock-mode (after accounting for WRITE) // - pass along the current lock-mode (after accounting for WRITE)
lockOptionsToUse = LockOptions.copy( event.getLockOptions(), new LockOptions() ); lockOptionsToUse = event.getLockOptions().makeCopy();
if ( currentLockMode == LockMode.WRITE if ( currentLockMode == LockMode.WRITE
|| currentLockMode == LockMode.PESSIMISTIC_WRITE || currentLockMode == LockMode.PESSIMISTIC_WRITE
|| currentLockMode == LockMode.PESSIMISTIC_READ ) { || currentLockMode == LockMode.PESSIMISTIC_READ ) {

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.event.spi; package org.hibernate.event.spi;
import org.hibernate.AssertionFailure;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
@ -16,38 +15,20 @@ import org.hibernate.LockOptions;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class LoadEvent extends AbstractEvent { public class LoadEvent extends AbstractEvent {
public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE; // public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE;
//Performance hotspot: avoid allocating unneeded LockOptions // public static final LockOptions DEFAULT_LOCK_OPTIONS = DEFAULT_LOCK_MODE.toLockOptions();
public static final LockOptions DEFAULT_LOCK_OPTIONS = new LockOptions() {
@Override
public LockOptions setLockMode(LockMode lockMode) {
throw new AssertionFailure( "Should not be invoked: DEFAULT_LOCK_OPTIONS needs to be treated as immutable." );
}
@Override
public LockOptions setAliasSpecificLockMode(String alias, LockMode lockMode) {
throw new AssertionFailure( "Should not be invoked: DEFAULT_LOCK_OPTIONS needs to be treated as immutable." );
}
@Override
public LockOptions setTimeOut(int timeout) {
throw new AssertionFailure( "Should not be invoked: DEFAULT_LOCK_OPTIONS needs to be treated as immutable." );
}
@Override
public LockOptions setScope(boolean scope) {
throw new AssertionFailure( "Should not be invoked: DEFAULT_LOCK_OPTIONS needs to be treated as immutable." );
}
};
private Object entityId; private Object entityId;
private String entityClassName; private String entityClassName;
private Object instanceToLoad; private Object instanceToLoad;
private LockOptions lockOptions; private final LockOptions lockOptions;
private final boolean isAssociationFetch; private final boolean isAssociationFetch;
private Object result; private Object result;
private PostLoadEvent postLoadEvent; private PostLoadEvent postLoadEvent;
private Boolean readOnly; private Boolean readOnly;
public LoadEvent(Object entityId, Object instanceToLoad, EventSource source, Boolean readOnly) { public LoadEvent(Object entityId, Object instanceToLoad, EventSource source, Boolean readOnly) {
this( entityId, null, instanceToLoad, DEFAULT_LOCK_OPTIONS, false, source, readOnly ); this( entityId, null, instanceToLoad, LockMode.NONE.toLockOptions(), false, source, readOnly );
} }
public LoadEvent(Object entityId, String entityClassName, LockMode lockMode, EventSource source, Boolean readOnly) { public LoadEvent(Object entityId, String entityClassName, LockMode lockMode, EventSource source, Boolean readOnly) {
@ -59,7 +40,7 @@ public class LoadEvent extends AbstractEvent {
} }
public LoadEvent(Object entityId, String entityClassName, boolean isAssociationFetch, EventSource source, Boolean readOnly) { public LoadEvent(Object entityId, String entityClassName, boolean isAssociationFetch, EventSource source, Boolean readOnly) {
this( entityId, entityClassName, null, DEFAULT_LOCK_OPTIONS, isAssociationFetch, source, readOnly ); this( entityId, entityClassName, null, LockMode.NONE.toLockOptions(), isAssociationFetch, source, readOnly );
} }
private LoadEvent( private LoadEvent(
@ -74,7 +55,7 @@ public class LoadEvent extends AbstractEvent {
entityId, entityId,
entityClassName, entityClassName,
instanceToLoad, instanceToLoad,
lockMode == DEFAULT_LOCK_MODE ? DEFAULT_LOCK_OPTIONS : new LockOptions().setLockMode( lockMode ), lockMode.toLockOptions(),
isAssociationFetch, isAssociationFetch,
source, source,
readOnly readOnly
@ -100,7 +81,7 @@ public class LoadEvent extends AbstractEvent {
throw new IllegalArgumentException("Invalid lock mode for loading"); throw new IllegalArgumentException("Invalid lock mode for loading");
} }
else if ( lockOptions.getLockMode() == null ) { else if ( lockOptions.getLockMode() == null ) {
lockOptions.setLockMode(DEFAULT_LOCK_MODE); lockOptions.setLockMode( LockMode.NONE );
} }
this.entityId = entityId; this.entityId = entityId;
@ -148,37 +129,10 @@ public class LoadEvent extends AbstractEvent {
return lockOptions.getLockMode(); return lockOptions.getLockMode();
} }
public void setLockMode(LockMode lockMode) {
if ( lockMode != lockOptions.getLockMode() ) {
writingOnLockOptions();
this.lockOptions.setLockMode( lockMode );
}
}
private void writingOnLockOptions() {
if ( lockOptions == DEFAULT_LOCK_OPTIONS ) {
this.lockOptions = new LockOptions();
}
}
public void setLockTimeout(int timeout) {
if ( timeout != lockOptions.getTimeOut() ) {
writingOnLockOptions();
this.lockOptions.setTimeOut( timeout );
}
}
public int getLockTimeout() { public int getLockTimeout() {
return this.lockOptions.getTimeOut(); return this.lockOptions.getTimeOut();
} }
public void setLockScope(boolean cascade) {
if ( lockOptions.getScope() != cascade ) {
writingOnLockOptions();
this.lockOptions.setScope( cascade );
}
}
public boolean getLockScope() { public boolean getLockScope() {
return this.lockOptions.getScope(); return this.lockOptions.getScope();
} }

View File

@ -33,7 +33,7 @@ public class LockEvent extends AbstractEvent {
public LockEvent(Object object, LockMode lockMode, EventSource source) { public LockEvent(Object object, LockMode lockMode, EventSource source) {
super(source); super(source);
this.object = object; this.object = object;
this.lockOptions = new LockOptions().setLockMode(lockMode); this.lockOptions = lockMode.toLockOptions();
} }
public LockEvent(Object object, LockOptions lockOptions, EventSource source) { public LockEvent(Object object, LockOptions lockOptions, EventSource source) {
@ -58,24 +58,12 @@ public class LockEvent extends AbstractEvent {
return lockOptions.getLockMode(); return lockOptions.getLockMode();
} }
public void setLockMode(LockMode lockMode) {
this.lockOptions.setLockMode(lockMode);
}
public void setLockTimeout(int timeout) {
this.lockOptions.setTimeOut(timeout);
}
public int getLockTimeout() { public int getLockTimeout() {
return this.lockOptions.getTimeOut(); return lockOptions.getTimeOut();
}
public void setLockScope(boolean cascade) {
this.lockOptions.setScope(cascade);
} }
public boolean getLockScope() { public boolean getLockScope() {
return this.lockOptions.getScope(); return lockOptions.getScope();
} }
public String getEntityName() { public String getEntityName() {

View File

@ -31,7 +31,7 @@ public class ResolveNaturalIdEvent extends AbstractEvent {
private Object entityId; private Object entityId;
public ResolveNaturalIdEvent(Map<String, Object> naturalIdValues, EntityPersister entityPersister, EventSource source) { public ResolveNaturalIdEvent(Map<String, Object> naturalIdValues, EntityPersister entityPersister, EventSource source) {
this( naturalIdValues, entityPersister, new LockOptions(), source ); this( naturalIdValues, entityPersister, LockOptions.NONE, source );
} }
public ResolveNaturalIdEvent( public ResolveNaturalIdEvent(

View File

@ -991,29 +991,7 @@ public class SessionImpl
@Override @Override
public void load(Object object, Object id) throws HibernateException { public void load(Object object, Object id) throws HibernateException {
LoadEvent event = loadEvent; fireLoad( new LoadEvent( id, object, this, getReadOnlyFromLoadQueryInfluencers() ), LoadEventListener.RELOAD );
loadEvent = null;
if ( event == null ) {
event = new LoadEvent( id, object, this, getReadOnlyFromLoadQueryInfluencers() );
}
else {
event.setEntityClassName( null );
event.setEntityId( id );
event.setInstanceToLoad( object );
event.setLockMode( LoadEvent.DEFAULT_LOCK_MODE );
event.setLockScope( LoadEvent.DEFAULT_LOCK_OPTIONS.getScope() );
event.setLockTimeout( LoadEvent.DEFAULT_LOCK_OPTIONS.getTimeOut() );
}
fireLoad( event, LoadEventListener.RELOAD );
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
} }
@Override @Deprecated @Override @Deprecated
@ -1054,13 +1032,7 @@ public class SessionImpl
event = recycleEventInstance( event, id, entityName ); event = recycleEventInstance( event, id, entityName );
fireLoadNoChecks( event, LoadEventListener.IMMEDIATE_LOAD ); fireLoadNoChecks( event, LoadEventListener.IMMEDIATE_LOAD );
Object result = event.getResult(); Object result = event.getResult();
if ( loadEvent == null ) { finishWithEventInstance( event );
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
if ( result instanceof HibernateProxy ) { if ( result instanceof HibernateProxy ) {
return ( (HibernateProxy) result ).getHibernateLazyInitializer().getImplementation(); return ( (HibernateProxy) result ).getHibernateLazyInitializer().getImplementation();
} }
@ -1073,6 +1045,7 @@ public class SessionImpl
Object id, Object id,
boolean eager, boolean eager,
boolean nullable) { boolean nullable) {
final LoadType type = internalLoadType( eager, nullable );
final EffectiveEntityGraph effectiveEntityGraph = getLoadQueryInfluencers().getEffectiveEntityGraph(); final EffectiveEntityGraph effectiveEntityGraph = getLoadQueryInfluencers().getEffectiveEntityGraph();
final GraphSemantic semantic = effectiveEntityGraph.getSemantic(); final GraphSemantic semantic = effectiveEntityGraph.getSemantic();
final RootGraphImplementor<?> graph = effectiveEntityGraph.getGraph(); final RootGraphImplementor<?> graph = effectiveEntityGraph.getGraph();
@ -1084,38 +1057,16 @@ public class SessionImpl
effectiveEntityGraph.clear(); effectiveEntityGraph.clear();
} }
} }
try { try {
final LoadType type;
if ( nullable ) {
type = LoadEventListener.INTERNAL_LOAD_NULLABLE;
}
else {
type = eager
? LoadEventListener.INTERNAL_LOAD_EAGER
: LoadEventListener.INTERNAL_LOAD_LAZY;
}
LoadEvent event = loadEvent; LoadEvent event = loadEvent;
loadEvent = null; loadEvent = null;
event = recycleEventInstance( event, id, entityName ); event = recycleEventInstance( event, id, entityName );
fireLoadNoChecks( event, type ); fireLoadNoChecks( event, type );
Object result = event.getResult(); Object result = event.getResult();
if ( !nullable ) { if ( !nullable ) {
UnresolvableObjectException.throwIfNull( result, id, entityName ); UnresolvableObjectException.throwIfNull( result, id, entityName );
} }
finishWithEventInstance( event );
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
return result; return result;
} }
finally { finally {
@ -1125,6 +1076,17 @@ public class SessionImpl
} }
} }
private static LoadType internalLoadType(boolean eager, boolean nullable) {
if ( nullable ) {
return LoadEventListener.INTERNAL_LOAD_NULLABLE;
}
else {
return eager
? LoadEventListener.INTERNAL_LOAD_EAGER
: LoadEventListener.INTERNAL_LOAD_LAZY;
}
}
/** /**
* Helper to avoid creating many new instances of LoadEvent: it's an allocation hot spot. * Helper to avoid creating many new instances of LoadEvent: it's an allocation hot spot.
*/ */
@ -1136,13 +1098,20 @@ public class SessionImpl
event.setEntityClassName( entityName ); event.setEntityClassName( entityName );
event.setEntityId( id ); event.setEntityId( id );
event.setInstanceToLoad( null ); event.setInstanceToLoad( null );
event.setLockMode( LoadEvent.DEFAULT_LOCK_MODE );
event.setLockScope( LoadEvent.DEFAULT_LOCK_OPTIONS.getScope() );
event.setLockTimeout( LoadEvent.DEFAULT_LOCK_OPTIONS.getTimeOut() );
return event; return event;
} }
} }
private void finishWithEventInstance(LoadEvent event) {
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
}
@Override @Deprecated @Override @Deprecated
public <T> T load(Class<T> entityClass, Object id, LockMode lockMode) throws HibernateException { public <T> T load(Class<T> entityClass, Object id, LockMode lockMode) throws HibernateException {
return this.byId( entityClass ).with( new LockOptions( lockMode ) ).getReference( id ); return this.byId( entityClass ).with( new LockOptions( lockMode ) ).getReference( id );
@ -2123,9 +2092,9 @@ public class SessionImpl
private class LockRequestImpl implements LockRequest { private class LockRequestImpl implements LockRequest {
private final LockOptions lockOptions; private final LockOptions lockOptions;
private LockRequestImpl(LockOptions lo) { private LockRequestImpl(LockOptions lockOptions) {
lockOptions = new LockOptions(); this.lockOptions = new LockOptions();
LockOptions.copy( lo, lockOptions ); LockOptions.copy( lockOptions, this.lockOptions );
} }
@Override @Override

View File

@ -4356,7 +4356,7 @@ public abstract class AbstractEntityPersister
*/ */
@Override @Override
public Object load(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session) { public Object load(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session) {
return load( id, optionalObject, new LockOptions().setLockMode( lockMode ), session ); return load( id, optionalObject, lockMode.toLockOptions(), session );
} }
/** /**

View File

@ -309,8 +309,8 @@ public abstract class AbstractQuery<R>
} }
if ( getLockOptions().getScope() ) { if ( getLockOptions().getScope() ) {
hints.put( HINT_SPEC_LOCK_SCOPE, getLockOptions().getScope() ); hints.put( HINT_SPEC_LOCK_SCOPE, getLockOptions().getLockScope() );
hints.put( HINT_JAVAEE_LOCK_SCOPE, getLockOptions().getScope() ); hints.put( HINT_JAVAEE_LOCK_SCOPE, getLockOptions().getLockScope() );
} }
if ( getLockOptions().hasAliasSpecificLockModes() ) { if ( getLockOptions().hasAliasSpecificLockModes() ) {

View File

@ -37,7 +37,7 @@ public abstract class QueryOptionsAdapter implements QueryOptions {
@Override @Override
public LockOptions getLockOptions() { public LockOptions getLockOptions() {
return new LockOptions(); return LockOptions.NONE;
} }
@Override @Override

View File

@ -304,7 +304,7 @@ public class MatchingIdSelectionHelper {
, ,
executionContext.getSession() executionContext.getSession()
); );
final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions(); final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions().makeCopy();
final LockMode lockMode = lockOptions.getLockMode(); final LockMode lockMode = lockOptions.getLockMode();
// Acquire a WRITE lock for the rows that are about to be modified // Acquire a WRITE lock for the rows that are about to be modified
lockOptions.setLockMode( LockMode.WRITE ); lockOptions.setLockMode( LockMode.WRITE );

View File

@ -119,9 +119,10 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess {
executionContext.getCallback().registerAfterLoadAction( executionContext.getCallback().registerAfterLoadAction(
(session, entity, persister) -> { (session, entity, persister) -> {
( (Session) session ).buildLockRequest( lockOptionsToUse ).lock( ( (Session) session ).lock(
persister.getEntityName(), persister.getEntityName(),
entity entity,
lockOptionsToUse
); );
} }
); );