HHH-16704 Avoid iterating a LinkedHashMap during ActionQueue processing
This commit is contained in:
parent
9e09c057c5
commit
c9457db5b6
|
@ -47,7 +47,6 @@ import org.hibernate.event.spi.EventSource;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
@ -105,125 +104,114 @@ public class ActionQueue {
|
||||||
private AfterTransactionCompletionProcessQueue afterTransactionProcesses;
|
private AfterTransactionCompletionProcessQueue afterTransactionProcesses;
|
||||||
private BeforeTransactionCompletionProcessQueue beforeTransactionProcesses;
|
private BeforeTransactionCompletionProcessQueue beforeTransactionProcesses;
|
||||||
|
|
||||||
/**
|
//Extract this as a constant to perform efficient iterations:
|
||||||
* A LinkedHashMap containing providers for all the ExecutableLists, inserted in execution order
|
//method values() otherwise allocates a new array on each invocation.
|
||||||
*/
|
private static final OrderedActions[] ORDERED_OPERATIONS = OrderedActions.values();
|
||||||
private static final LinkedHashMap<Class<? extends Executable>,ListProvider<?>> EXECUTABLE_LISTS_MAP;
|
|
||||||
static {
|
//The order of these operations is very important
|
||||||
EXECUTABLE_LISTS_MAP = CollectionHelper.linkedMapOfSize( 8 );
|
private enum OrderedActions {
|
||||||
/*
|
CollectionRemoveAction {
|
||||||
CollectionRemoveAction actions have to be executed before OrphanRemovalAction actions, to prevent a constraint violation
|
@Override
|
||||||
when deleting an orphan Entity that contains an ElementCollection (see HHH-15159)
|
public ExecutableList<?> getActions(ActionQueue instance) {
|
||||||
*/
|
|
||||||
EXECUTABLE_LISTS_MAP.put(
|
|
||||||
CollectionRemoveAction.class,
|
|
||||||
new ListProvider<CollectionRemoveAction>() {
|
|
||||||
ExecutableList<CollectionRemoveAction> get(ActionQueue instance) {
|
|
||||||
return instance.collectionRemovals;
|
return instance.collectionRemovals;
|
||||||
}
|
}
|
||||||
ExecutableList<CollectionRemoveAction> init(ActionQueue instance) {
|
@Override
|
||||||
return instance.collectionRemovals = new ExecutableList<>(
|
public void ensureInitialized(ActionQueue instance) {
|
||||||
instance.isOrderUpdatesEnabled()
|
if ( instance.collectionRemovals == null ) {
|
||||||
);
|
instance.collectionRemovals = new ExecutableList<>( instance.isOrderUpdatesEnabled() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
},
|
||||||
EXECUTABLE_LISTS_MAP.put(
|
OrphanRemovalAction {
|
||||||
OrphanRemovalAction.class,
|
@Override
|
||||||
new ListProvider<OrphanRemovalAction>() {
|
public ExecutableList<?> getActions(ActionQueue instance) {
|
||||||
ExecutableList<OrphanRemovalAction> get(ActionQueue instance) {
|
|
||||||
return instance.orphanRemovals;
|
return instance.orphanRemovals;
|
||||||
}
|
}
|
||||||
ExecutableList<OrphanRemovalAction> init(ActionQueue instance) {
|
@Override
|
||||||
// OrphanRemovalAction executables never require sorting.
|
public void ensureInitialized(ActionQueue instance) {
|
||||||
return instance.orphanRemovals = new ExecutableList<>( false );
|
if ( instance.orphanRemovals == null ) {
|
||||||
|
instance.orphanRemovals = new ExecutableList<>( false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
},
|
||||||
EXECUTABLE_LISTS_MAP.put(
|
EntityInsertAction {
|
||||||
AbstractEntityInsertAction.class,
|
@Override
|
||||||
new ListProvider<AbstractEntityInsertAction>() {
|
public ExecutableList<?> getActions(ActionQueue instance) {
|
||||||
ExecutableList<AbstractEntityInsertAction> get(ActionQueue instance) {
|
|
||||||
return instance.insertions;
|
return instance.insertions;
|
||||||
}
|
}
|
||||||
ExecutableList<AbstractEntityInsertAction> init(ActionQueue instance) {
|
@Override
|
||||||
if ( instance.isOrderInsertsEnabled() ) {
|
public void ensureInitialized(final ActionQueue instance) {
|
||||||
return instance.insertions = new ExecutableList<>(
|
if ( instance.insertions == null ) {
|
||||||
InsertActionSorter.INSTANCE
|
//Special case of initialization
|
||||||
);
|
instance.insertions = instance.isOrderInsertsEnabled()
|
||||||
}
|
? new ExecutableList<>( InsertActionSorter.INSTANCE )
|
||||||
else {
|
: new ExecutableList<>( false );
|
||||||
return instance.insertions = new ExecutableList<>(
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
EntityUpdateAction {
|
||||||
EXECUTABLE_LISTS_MAP.put(
|
@Override
|
||||||
EntityUpdateAction.class,
|
public ExecutableList<?> getActions(ActionQueue instance) {
|
||||||
new ListProvider<EntityUpdateAction>() {
|
|
||||||
ExecutableList<EntityUpdateAction> get(ActionQueue instance) {
|
|
||||||
return instance.updates;
|
return instance.updates;
|
||||||
}
|
}
|
||||||
ExecutableList<EntityUpdateAction> init(ActionQueue instance) {
|
@Override
|
||||||
return instance.updates = new ExecutableList<>(
|
public void ensureInitialized(ActionQueue instance) {
|
||||||
instance.isOrderUpdatesEnabled()
|
if ( instance.updates == null ) {
|
||||||
);
|
instance.updates = new ExecutableList<>( instance.isOrderUpdatesEnabled() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
},
|
||||||
EXECUTABLE_LISTS_MAP.put(
|
QueuedOperationCollectionAction {
|
||||||
QueuedOperationCollectionAction.class,
|
@Override
|
||||||
new ListProvider<QueuedOperationCollectionAction>() {
|
public ExecutableList<?> getActions(ActionQueue instance) {
|
||||||
ExecutableList<QueuedOperationCollectionAction> get(ActionQueue instance) {
|
|
||||||
return instance.collectionQueuedOps;
|
return instance.collectionQueuedOps;
|
||||||
}
|
}
|
||||||
ExecutableList<QueuedOperationCollectionAction> init(ActionQueue instance) {
|
@Override
|
||||||
return instance.collectionQueuedOps = new ExecutableList<>(
|
public void ensureInitialized(ActionQueue instance) {
|
||||||
instance.isOrderUpdatesEnabled()
|
if ( instance.collectionQueuedOps == null ) {
|
||||||
);
|
instance.collectionQueuedOps = new ExecutableList<>( instance.isOrderUpdatesEnabled() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
},
|
||||||
EXECUTABLE_LISTS_MAP.put(
|
CollectionUpdateAction {
|
||||||
CollectionUpdateAction.class,
|
@Override
|
||||||
new ListProvider<CollectionUpdateAction>() {
|
public ExecutableList<?> getActions(ActionQueue instance) {
|
||||||
ExecutableList<CollectionUpdateAction> get(ActionQueue instance) {
|
|
||||||
return instance.collectionUpdates;
|
return instance.collectionUpdates;
|
||||||
}
|
}
|
||||||
ExecutableList<CollectionUpdateAction> init(ActionQueue instance) {
|
@Override
|
||||||
return instance.collectionUpdates = new ExecutableList<>(
|
public void ensureInitialized(ActionQueue instance) {
|
||||||
instance.isOrderUpdatesEnabled()
|
if ( instance.collectionUpdates == null ) {
|
||||||
);
|
instance.collectionUpdates = new ExecutableList<>( instance.isOrderUpdatesEnabled() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
},
|
||||||
EXECUTABLE_LISTS_MAP.put(
|
CollectionRecreateAction {
|
||||||
CollectionRecreateAction.class,
|
@Override
|
||||||
new ListProvider<CollectionRecreateAction>() {
|
public ExecutableList<?> getActions(ActionQueue instance) {
|
||||||
ExecutableList<CollectionRecreateAction> get(ActionQueue instance) {
|
|
||||||
return instance.collectionCreations;
|
return instance.collectionCreations;
|
||||||
}
|
}
|
||||||
ExecutableList<CollectionRecreateAction> init(ActionQueue instance) {
|
@Override
|
||||||
return instance.collectionCreations = new ExecutableList<>(
|
public void ensureInitialized(ActionQueue instance) {
|
||||||
instance.isOrderUpdatesEnabled()
|
if ( instance.collectionCreations == null ) {
|
||||||
);
|
instance.collectionCreations = new ExecutableList<>( instance.isOrderUpdatesEnabled() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
},
|
||||||
EXECUTABLE_LISTS_MAP.put(
|
EntityDeleteAction {
|
||||||
EntityDeleteAction.class,
|
@Override
|
||||||
new ListProvider<EntityDeleteAction>() {
|
public ExecutableList<?> getActions(ActionQueue instance) {
|
||||||
ExecutableList<EntityDeleteAction> get(ActionQueue instance) {
|
|
||||||
return instance.deletions;
|
return instance.deletions;
|
||||||
}
|
}
|
||||||
ExecutableList<EntityDeleteAction> init(ActionQueue instance) {
|
@Override
|
||||||
// EntityDeleteAction executables never require sorting.
|
public void ensureInitialized(ActionQueue instance) {
|
||||||
return instance.deletions = new ExecutableList<>( false );
|
if ( instance.deletions == null ) {
|
||||||
|
instance.deletions = new ExecutableList<>( false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
|
|
||||||
|
public abstract ExecutableList<?> getActions(ActionQueue instance);
|
||||||
|
public abstract void ensureInitialized(ActionQueue instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,12 +225,12 @@ public class ActionQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
EXECUTABLE_LISTS_MAP.forEach( (k,listProvider) -> {
|
for ( OrderedActions value : ORDERED_OPERATIONS ) {
|
||||||
ExecutableList<?> l = listProvider.get( this );
|
final ExecutableList<?> list = value.getActions( this );
|
||||||
if ( l != null ) {
|
if ( list != null ) {
|
||||||
l.clear();
|
list.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} );
|
|
||||||
if ( unresolvedInsertions != null ) {
|
if ( unresolvedInsertions != null ) {
|
||||||
unresolvedInsertions.clear();
|
unresolvedInsertions.clear();
|
||||||
}
|
}
|
||||||
|
@ -291,7 +279,8 @@ public class ActionQueue {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG.trace( "Adding resolved non-early insert action." );
|
LOG.trace( "Adding resolved non-early insert action." );
|
||||||
addAction( AbstractEntityInsertAction.class, insert );
|
OrderedActions.EntityInsertAction.ensureInitialized( this );
|
||||||
|
this.insertions.add( insert );
|
||||||
}
|
}
|
||||||
if ( !insert.isVeto() ) {
|
if ( !insert.isVeto() ) {
|
||||||
insert.makeEntityManaged();
|
insert.makeEntityManaged();
|
||||||
|
@ -310,15 +299,6 @@ public class ActionQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends Executable & Comparable<? super T> & Serializable> void addAction(Class<T> executableClass, T action) {
|
|
||||||
listProvider( executableClass ).getOrInit( this ).add( action );
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <T extends Executable & Comparable<? super T> & Serializable> ListProvider<T> listProvider(Class<T> actionClass) {
|
|
||||||
return (ListProvider<T>) EXECUTABLE_LISTS_MAP.get(actionClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an entity (IDENTITY) insert action
|
* Adds an entity (IDENTITY) insert action
|
||||||
*
|
*
|
||||||
|
@ -335,7 +315,8 @@ public class ActionQueue {
|
||||||
* @param action The action representing the entity deletion
|
* @param action The action representing the entity deletion
|
||||||
*/
|
*/
|
||||||
public void addAction(EntityDeleteAction action) {
|
public void addAction(EntityDeleteAction action) {
|
||||||
addAction( EntityDeleteAction.class, action );
|
OrderedActions.EntityDeleteAction.ensureInitialized( this );
|
||||||
|
this.deletions.add( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -343,8 +324,9 @@ public class ActionQueue {
|
||||||
*
|
*
|
||||||
* @param action The action representing the orphan removal
|
* @param action The action representing the orphan removal
|
||||||
*/
|
*/
|
||||||
public void addAction(OrphanRemovalAction action) {
|
public void addAction(final OrphanRemovalAction action) {
|
||||||
addAction( OrphanRemovalAction.class, action );
|
OrderedActions.OrphanRemovalAction.ensureInitialized( this );
|
||||||
|
this.orphanRemovals.add( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -352,8 +334,9 @@ public class ActionQueue {
|
||||||
*
|
*
|
||||||
* @param action The action representing the entity update
|
* @param action The action representing the entity update
|
||||||
*/
|
*/
|
||||||
public void addAction(EntityUpdateAction action) {
|
public void addAction(final EntityUpdateAction action) {
|
||||||
addAction( EntityUpdateAction.class, action );
|
OrderedActions.EntityUpdateAction.ensureInitialized( this );
|
||||||
|
this.updates.add( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -361,8 +344,9 @@ public class ActionQueue {
|
||||||
*
|
*
|
||||||
* @param action The action representing the (re)creation of a collection
|
* @param action The action representing the (re)creation of a collection
|
||||||
*/
|
*/
|
||||||
public void addAction(CollectionRecreateAction action) {
|
public void addAction(final CollectionRecreateAction action) {
|
||||||
addAction( CollectionRecreateAction.class, action );
|
OrderedActions.CollectionRecreateAction.ensureInitialized( this );
|
||||||
|
this.collectionCreations.add( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -370,8 +354,9 @@ public class ActionQueue {
|
||||||
*
|
*
|
||||||
* @param action The action representing the removal of a collection
|
* @param action The action representing the removal of a collection
|
||||||
*/
|
*/
|
||||||
public void addAction(CollectionRemoveAction action) {
|
public void addAction(final CollectionRemoveAction action) {
|
||||||
addAction( CollectionRemoveAction.class, action );
|
OrderedActions.CollectionRemoveAction.ensureInitialized( this );
|
||||||
|
this.collectionRemovals.add( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -379,8 +364,9 @@ public class ActionQueue {
|
||||||
*
|
*
|
||||||
* @param action The action representing the update of a collection
|
* @param action The action representing the update of a collection
|
||||||
*/
|
*/
|
||||||
public void addAction(CollectionUpdateAction action) {
|
public void addAction(final CollectionUpdateAction action) {
|
||||||
addAction( CollectionUpdateAction.class, action );
|
OrderedActions.CollectionUpdateAction.ensureInitialized( this );
|
||||||
|
this.collectionUpdates.add( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -389,7 +375,8 @@ public class ActionQueue {
|
||||||
* @param action The action representing the queued operation
|
* @param action The action representing the queued operation
|
||||||
*/
|
*/
|
||||||
public void addAction(QueuedOperationCollectionAction action) {
|
public void addAction(QueuedOperationCollectionAction action) {
|
||||||
addAction( QueuedOperationCollectionAction.class, action );
|
OrderedActions.QueuedOperationCollectionAction.ensureInitialized( this );
|
||||||
|
this.collectionQueuedOps.add( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -484,12 +471,9 @@ public class ActionQueue {
|
||||||
throw new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." );
|
throw new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." );
|
||||||
}
|
}
|
||||||
|
|
||||||
EXECUTABLE_LISTS_MAP.forEach( (k,listProvider) -> {
|
for ( OrderedActions action : ORDERED_OPERATIONS ) {
|
||||||
ExecutableList<?> l = listProvider.get( this );
|
executeActions( action.getActions( this ) );
|
||||||
if ( l != null && !l.isEmpty() ) {
|
|
||||||
executeActions( l );
|
|
||||||
}
|
}
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -562,9 +546,9 @@ public class ActionQueue {
|
||||||
if ( tables.isEmpty() ) {
|
if ( tables.isEmpty() ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for ( ListProvider<?> listProvider : EXECUTABLE_LISTS_MAP.values() ) {
|
for ( OrderedActions action : ORDERED_OPERATIONS ) {
|
||||||
ExecutableList<?> l = listProvider.get( this );
|
final ExecutableList<?> list = action.getActions( this );
|
||||||
if ( areTablesToBeUpdated( l, tables ) ) {
|
if ( areTablesToBeUpdated( list, tables ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,6 +594,9 @@ public class ActionQueue {
|
||||||
*/
|
*/
|
||||||
private <E extends Executable & Comparable<? super E> & Serializable> void executeActions(ExecutableList<E> list)
|
private <E extends Executable & Comparable<? super E> & Serializable> void executeActions(ExecutableList<E> list)
|
||||||
throws HibernateException {
|
throws HibernateException {
|
||||||
|
if ( list == null || list.isEmpty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// todo : consider ways to improve the double iteration of Executables here:
|
// todo : consider ways to improve the double iteration of Executables here:
|
||||||
// 1) we explicitly iterate list here to perform Executable#execute()
|
// 1) we explicitly iterate list here to perform Executable#execute()
|
||||||
// 2) ExecutableList#getQuerySpaces also iterates the Executables to collect query spaces.
|
// 2) ExecutableList#getQuerySpaces also iterates the Executables to collect query spaces.
|
||||||
|
@ -908,8 +895,8 @@ public class ActionQueue {
|
||||||
}
|
}
|
||||||
unresolvedInsertions.serialize( oos );
|
unresolvedInsertions.serialize( oos );
|
||||||
|
|
||||||
for ( ListProvider<?> p : EXECUTABLE_LISTS_MAP.values() ) {
|
for ( OrderedActions action : ORDERED_OPERATIONS ) {
|
||||||
ExecutableList<?> l = p.get( this );
|
ExecutableList<?> l = action.getActions( this );
|
||||||
if ( l == null ) {
|
if ( l == null ) {
|
||||||
oos.writeBoolean( false );
|
oos.writeBoolean( false );
|
||||||
}
|
}
|
||||||
|
@ -939,12 +926,14 @@ public class ActionQueue {
|
||||||
|
|
||||||
rtn.unresolvedInsertions = UnresolvedEntityInsertActions.deserialize( ois, session );
|
rtn.unresolvedInsertions = UnresolvedEntityInsertActions.deserialize( ois, session );
|
||||||
|
|
||||||
for ( ListProvider<?> provider : EXECUTABLE_LISTS_MAP.values() ) {
|
for ( OrderedActions action : ORDERED_OPERATIONS ) {
|
||||||
ExecutableList<?> l = provider.get( rtn );
|
ExecutableList<?> l = action.getActions( rtn );
|
||||||
boolean notNull = ois.readBoolean();
|
boolean notNull = ois.readBoolean();
|
||||||
if ( notNull ) {
|
if ( notNull ) {
|
||||||
if ( l == null ) {
|
if ( l == null ) {
|
||||||
l = provider.init( rtn );
|
//sorry.. trying hard to avoid generic initializations mess.
|
||||||
|
action.ensureInitialized( rtn );
|
||||||
|
l = action.getActions( rtn );
|
||||||
}
|
}
|
||||||
l.readExternal( ois );
|
l.readExternal( ois );
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue