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 Map.Entry<Object,EntityEntry>[] entityEntries = persistenceContext.reentrantSafeEntityEntries();
|
||||||
final int count = entityEntries.length;
|
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 ) {
|
for ( Map.Entry<Object,EntityEntry> me : entityEntries ) {
|
||||||
// Update the status of the object and if necessary, schedule an update
|
// Update the status of the object and if necessary, schedule an update
|
||||||
|
|
||||||
EntityEntry entry = me.getValue();
|
EntityEntry entry = me.getValue();
|
||||||
Status status = entry.getStatus();
|
Status status = entry.getStatus();
|
||||||
|
|
||||||
|
|
||||||
if ( status != Status.LOADING && status != Status.GONE ) {
|
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 );
|
flushListeners.fireEventOnEachListener( entityEvent, FlushEntityEventListener::onFlushEntity );
|
||||||
|
entityEvent.setAllowedToReuse( true );
|
||||||
|
assert entityEvent.getInstanceGenerationId() == eventGenerationId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +229,26 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
||||||
return count;
|
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,
|
* process any unreferenced collections and then inspect all known collections,
|
||||||
* scheduling creates/removes/updates
|
* scheduling creates/removes/updates
|
||||||
|
|
|
@ -12,15 +12,18 @@ import org.hibernate.engine.spi.EntityEntry;
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
*/
|
*/
|
||||||
public class FlushEntityEvent extends AbstractEvent {
|
public class FlushEntityEvent extends AbstractEvent {
|
||||||
private final Object entity;
|
|
||||||
|
private Object entity;
|
||||||
private Object[] propertyValues;
|
private Object[] propertyValues;
|
||||||
private Object[] databaseSnapshot;
|
private Object[] databaseSnapshot;
|
||||||
private int[] dirtyProperties;
|
private int[] dirtyProperties;
|
||||||
private boolean hasDirtyCollection;
|
private boolean hasDirtyCollection;
|
||||||
private boolean dirtyCheckPossible;
|
private boolean dirtyCheckPossible;
|
||||||
private boolean dirtyCheckHandledByInterceptor;
|
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) {
|
public FlushEntityEvent(EventSource source, Object entity, EntityEntry entry) {
|
||||||
super(source);
|
super(source);
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
|
@ -75,4 +78,45 @@ public class FlushEntityEvent extends AbstractEvent {
|
||||||
public Object getEntity() {
|
public Object getEntity() {
|
||||||
return entity;
|
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