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 );
}
/**
* @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

@ -55,13 +55,49 @@ public class LockOptions implements Serializable {
* Represents {@link LockMode#NONE}, to which timeout and scope are
* not applicable.
*/
public static final LockOptions NONE = new LockOptions(true, LockMode.NONE);
public static final LockOptions NONE = new LockOptions( true, LockMode.NONE );
/**
* Represents {@link LockMode#READ}, to which timeout and scope are
* not applicable.
*/
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
@ -69,7 +105,7 @@ public class LockOptions implements Serializable {
* {@linkplain PessimisticLockScope#NORMAL no extension of the
* 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

View File

@ -92,18 +92,7 @@ public class CascadingActions {
LockOptions lockOptions,
boolean isCascadeDeleteEnabled) {
LOG.tracev( "Cascading to lock: {0}", entityName );
LockMode lockMode = LockMode.NONE;
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 );
session.lock( entityName, child, lockOptions );
}
@Override

View File

@ -189,7 +189,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
if ( currentLockMode.greaterThan( requestedLockMode ) ) {
// the requested lock-mode is less restrictive than the current one
// - 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
|| currentLockMode == LockMode.PESSIMISTIC_WRITE
|| currentLockMode == LockMode.PESSIMISTIC_READ ) {

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.event.spi;
import org.hibernate.AssertionFailure;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
@ -16,38 +15,20 @@ import org.hibernate.LockOptions;
* @author Steve Ebersole
*/
public class LoadEvent extends AbstractEvent {
public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE;
//Performance hotspot: avoid allocating unneeded LockOptions
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." );
}
};
// public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE;
// public static final LockOptions DEFAULT_LOCK_OPTIONS = DEFAULT_LOCK_MODE.toLockOptions();
private Object entityId;
private String entityClassName;
private Object instanceToLoad;
private LockOptions lockOptions;
private final LockOptions lockOptions;
private final boolean isAssociationFetch;
private Object result;
private PostLoadEvent postLoadEvent;
private 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) {
@ -59,7 +40,7 @@ public class LoadEvent extends AbstractEvent {
}
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(
@ -74,7 +55,7 @@ public class LoadEvent extends AbstractEvent {
entityId,
entityClassName,
instanceToLoad,
lockMode == DEFAULT_LOCK_MODE ? DEFAULT_LOCK_OPTIONS : new LockOptions().setLockMode( lockMode ),
lockMode.toLockOptions(),
isAssociationFetch,
source,
readOnly
@ -100,7 +81,7 @@ public class LoadEvent extends AbstractEvent {
throw new IllegalArgumentException("Invalid lock mode for loading");
}
else if ( lockOptions.getLockMode() == null ) {
lockOptions.setLockMode(DEFAULT_LOCK_MODE);
lockOptions.setLockMode( LockMode.NONE );
}
this.entityId = entityId;
@ -148,37 +129,10 @@ public class LoadEvent extends AbstractEvent {
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() {
return this.lockOptions.getTimeOut();
}
public void setLockScope(boolean cascade) {
if ( lockOptions.getScope() != cascade ) {
writingOnLockOptions();
this.lockOptions.setScope( cascade );
}
}
public boolean getLockScope() {
return this.lockOptions.getScope();
}

View File

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

View File

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

View File

@ -991,29 +991,7 @@ public class SessionImpl
@Override
public void load(Object object, Object id) throws HibernateException {
LoadEvent event = loadEvent;
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;
}
fireLoad( new LoadEvent( id, object, this, getReadOnlyFromLoadQueryInfluencers() ), LoadEventListener.RELOAD );
}
@Override @Deprecated
@ -1054,13 +1032,7 @@ public class SessionImpl
event = recycleEventInstance( event, id, entityName );
fireLoadNoChecks( event, LoadEventListener.IMMEDIATE_LOAD );
Object result = event.getResult();
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
finishWithEventInstance( event );
if ( result instanceof HibernateProxy ) {
return ( (HibernateProxy) result ).getHibernateLazyInitializer().getImplementation();
}
@ -1073,6 +1045,7 @@ public class SessionImpl
Object id,
boolean eager,
boolean nullable) {
final LoadType type = internalLoadType( eager, nullable );
final EffectiveEntityGraph effectiveEntityGraph = getLoadQueryInfluencers().getEffectiveEntityGraph();
final GraphSemantic semantic = effectiveEntityGraph.getSemantic();
final RootGraphImplementor<?> graph = effectiveEntityGraph.getGraph();
@ -1084,38 +1057,16 @@ public class SessionImpl
effectiveEntityGraph.clear();
}
}
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 = null;
event = recycleEventInstance( event, id, entityName );
fireLoadNoChecks( event, type );
Object result = event.getResult();
if ( !nullable ) {
UnresolvableObjectException.throwIfNull( result, id, entityName );
}
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
finishWithEventInstance( event );
return result;
}
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.
*/
@ -1136,13 +1098,20 @@ public class SessionImpl
event.setEntityClassName( entityName );
event.setEntityId( id );
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;
}
}
private void finishWithEventInstance(LoadEvent event) {
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
}
@Override @Deprecated
public <T> T load(Class<T> entityClass, Object id, LockMode lockMode) throws HibernateException {
return this.byId( entityClass ).with( new LockOptions( lockMode ) ).getReference( id );
@ -2123,9 +2092,9 @@ public class SessionImpl
private class LockRequestImpl implements LockRequest {
private final LockOptions lockOptions;
private LockRequestImpl(LockOptions lo) {
lockOptions = new LockOptions();
LockOptions.copy( lo, lockOptions );
private LockRequestImpl(LockOptions lockOptions) {
this.lockOptions = new LockOptions();
LockOptions.copy( lockOptions, this.lockOptions );
}
@Override

View File

@ -4356,7 +4356,7 @@ public abstract class AbstractEntityPersister
*/
@Override
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() ) {
hints.put( HINT_SPEC_LOCK_SCOPE, getLockOptions().getScope() );
hints.put( HINT_JAVAEE_LOCK_SCOPE, getLockOptions().getScope() );
hints.put( HINT_SPEC_LOCK_SCOPE, getLockOptions().getLockScope() );
hints.put( HINT_JAVAEE_LOCK_SCOPE, getLockOptions().getLockScope() );
}
if ( getLockOptions().hasAliasSpecificLockModes() ) {

View File

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

View File

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

View File

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