HHH-16047 Allow reusing of FlushEntityEvent instances
This commit is contained in:
parent
9f88b56099
commit
c570b11dcd
|
@ -203,15 +203,24 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
|||
final Map.Entry<Object,EntityEntry>[] entityEntries = persistenceContext.reentrantSafeEntityEntries();
|
||||
final int count = entityEntries.length;
|
||||
|
||||
FlushEntityEvent entityEvent = null; //allow reuse of the event as it's heavily allocated in certain use cases
|
||||
int eventGenerationId = 0; //Used to double-check the instance reuse won't cause problems
|
||||
|
||||
for ( Map.Entry<Object,EntityEntry> me : entityEntries ) {
|
||||
// Update the status of the object and if necessary, schedule an update
|
||||
|
||||
EntityEntry entry = me.getValue();
|
||||
Status status = entry.getStatus();
|
||||
|
||||
|
||||
if ( status != Status.LOADING && status != Status.GONE ) {
|
||||
final FlushEntityEvent entityEvent = new FlushEntityEvent( source, me.getKey(), entry );
|
||||
entityEvent = createOrReuseEventInstance( entityEvent, source, me.getKey(), entry );
|
||||
|
||||
entityEvent.setInstanceGenerationId( ++eventGenerationId );
|
||||
|
||||
flushListeners.fireEventOnEachListener( entityEvent, FlushEntityEventListener::onFlushEntity );
|
||||
entityEvent.setAllowedToReuse( true );
|
||||
assert entityEvent.getInstanceGenerationId() == eventGenerationId;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,6 +229,26 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
|||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reuses a FlushEntityEvent for a new purpose, if possible;
|
||||
* if not possible a new actual instance is returned.
|
||||
*/
|
||||
private FlushEntityEvent createOrReuseEventInstance(
|
||||
FlushEntityEvent possiblyValidExistingInstance,
|
||||
EventSource source,
|
||||
Object key,
|
||||
EntityEntry entry) {
|
||||
FlushEntityEvent entityEvent = possiblyValidExistingInstance;
|
||||
if ( entityEvent == null || (! entityEvent.isAllowedToReuse() ) ) {
|
||||
//need to create a new instance
|
||||
entityEvent = new FlushEntityEvent( source, key, entry );
|
||||
}
|
||||
else {
|
||||
entityEvent.resetAndReuseEventInstance( key, entry );
|
||||
}
|
||||
return entityEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* process any unreferenced collections and then inspect all known collections,
|
||||
* scheduling creates/removes/updates
|
||||
|
|
|
@ -12,15 +12,18 @@ import org.hibernate.engine.spi.EntityEntry;
|
|||
* @author Gavin King
|
||||
*/
|
||||
public class FlushEntityEvent extends AbstractEvent {
|
||||
private final Object entity;
|
||||
|
||||
private Object entity;
|
||||
private Object[] propertyValues;
|
||||
private Object[] databaseSnapshot;
|
||||
private int[] dirtyProperties;
|
||||
private boolean hasDirtyCollection;
|
||||
private boolean dirtyCheckPossible;
|
||||
private boolean dirtyCheckHandledByInterceptor;
|
||||
private final EntityEntry entityEntry;
|
||||
|
||||
private EntityEntry entityEntry;
|
||||
private boolean allowedToReuse;//allows this event instance to be reused for multiple events: special case to GC
|
||||
private int instanceGenerationId;//in support of event instance reuse: to double check no recursive/nested use is happening
|
||||
|
||||
public FlushEntityEvent(EventSource source, Object entity, EntityEntry entry) {
|
||||
super(source);
|
||||
this.entity = entity;
|
||||
|
@ -75,4 +78,45 @@ public class FlushEntityEvent extends AbstractEvent {
|
|||
public Object getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a terrible anti-pattern, but particular circumstances call for being
|
||||
* able to reuse the same event instance: this is otherwise allocated in hot loops
|
||||
* and since each event is escaping the scope it's actually causing allocation issues.
|
||||
* The flush event does not appear to be used recursively so this is currently safe to
|
||||
* do, nevertheless we add an allowedToReuse flag to ensure only instances whose
|
||||
* purpose has completed are being reused.
|
||||
* N.B. two out of three parameters from the constructor are reset: the same EventSource is implied
|
||||
* on reuse.
|
||||
* @param entity same as constructor parameter
|
||||
* @param entry same as constructor parameter
|
||||
*/
|
||||
public void resetAndReuseEventInstance(Object entity, EntityEntry entry) {
|
||||
this.entity = entity;
|
||||
this.entityEntry = entry;
|
||||
this.allowedToReuse = false;
|
||||
//and reset other fields to the default:
|
||||
this.propertyValues = null;
|
||||
this.databaseSnapshot = null;
|
||||
this.dirtyProperties = null;
|
||||
this.hasDirtyCollection = false;
|
||||
this.dirtyCheckPossible = false;
|
||||
this.dirtyCheckHandledByInterceptor = false;
|
||||
}
|
||||
|
||||
public boolean isAllowedToReuse() {
|
||||
return this.allowedToReuse;
|
||||
}
|
||||
|
||||
public void setAllowedToReuse(final boolean allowedToReuse) {
|
||||
this.allowedToReuse = allowedToReuse;
|
||||
}
|
||||
|
||||
public int getInstanceGenerationId() {
|
||||
return this.instanceGenerationId;
|
||||
}
|
||||
|
||||
public void setInstanceGenerationId(final int instanceGenerationId) {
|
||||
this.instanceGenerationId = instanceGenerationId;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue