Skip to content

Commit 623d5dc

Browse files
committed
HHH-15848 completely new impl of DefaultDirtyCheckEventListener
this is definitely not perfect yet, but it's certainly a much better foundation than the ancient implementation which was bad and side-effecty in all sorts of ways
1 parent 54ed8db commit 623d5dc

File tree

12 files changed

+497
-273
lines changed

12 files changed

+497
-273
lines changed

hibernate-core/src/main/java/org/hibernate/Session.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,8 @@ public interface Session extends SharedSessionContract, EntityManager {
382382
* the database? In other words, would any DML operations be executed if
383383
* we flushed this session?
384384
*
385-
* @return {@code true} if the session contains pending changes; {@code false} otherwise.
386-
* @throws HibernateException could not perform dirtying checking
385+
* @return {@code true} if the session contains pending changes;
386+
* {@code false} otherwise.
387387
*/
388388
boolean isDirty();
389389

hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import org.hibernate.collection.spi.PersistentCollection;
1010
import org.hibernate.internal.CoreLogging;
1111
import org.hibernate.internal.CoreMessageLogger;
12-
import org.hibernate.internal.util.NullnessUtil;
1312
import org.hibernate.persister.collection.CollectionPersister;
1413

1514
import java.io.IOException;
@@ -20,6 +19,7 @@
2019

2120
import org.checkerframework.checker.nullness.qual.Nullable;
2221

22+
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
2323
import static org.hibernate.pretty.MessageHelper.collectionInfoString;
2424

2525
/**
@@ -68,11 +68,11 @@ public CollectionEntry(CollectionPersister persister, PersistentCollection<?> co
6868
// during flush shouldn't be ignored
6969
ignore = false;
7070

71-
collection.clearDirty(); //a newly wrapped collection is NOT dirty (or we get unnecessary version updates)
71+
// a newly wrapped collection is NOT dirty
72+
// (or we get unnecessary version updates)
73+
collection.clearDirty();
7274

73-
snapshot = persister.isMutable() ?
74-
collection.getSnapshot( persister ) :
75-
null;
75+
snapshot = persister.isMutable() ? collection.getSnapshot( persister ) : null;
7676
collection.setSnapshot( loadedKey, role, snapshot );
7777
}
7878

@@ -123,7 +123,7 @@ public CollectionEntry(PersistentCollection<?> collection, SessionFactoryImpleme
123123

124124
loadedKey = collection.getKey();
125125
role = collection.getRole();
126-
loadedPersister = factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( NullnessUtil.castNonNull( role ) );
126+
loadedPersister = factory.getMappingMetamodel().getCollectionDescriptor( castNonNull( role ) );
127127

128128
snapshot = collection.getStoredSnapshot();
129129
}
@@ -156,12 +156,11 @@ private void dirty(PersistentCollection<?> collection) throws HibernateException
156156
final CollectionPersister loadedPersister = getLoadedPersister();
157157
final boolean forceDirty =
158158
collection.wasInitialized()
159-
&& !collection.isDirty() //optimization
160-
&& loadedPersister != null
161-
&& loadedPersister.isMutable() //optimization
162-
&& ( collection.isDirectlyAccessible() || loadedPersister.getElementType().isMutable() ) //optimization
163-
&& !collection.equalsSnapshot( loadedPersister );
164-
159+
&& !collection.isDirty() //optimization
160+
&& loadedPersister != null
161+
&& loadedPersister.isMutable() //optimization
162+
&& ( collection.isDirectlyAccessible() || loadedPersister.getElementType().isMutable() ) //optimization
163+
&& !collection.equalsSnapshot( loadedPersister );
165164
if ( forceDirty ) {
166165
collection.dirty();
167166
}
@@ -174,23 +173,19 @@ public void preFlush(PersistentCollection<?> collection) throws HibernateExcepti
174173
}
175174

176175
final CollectionPersister loadedPersister = getLoadedPersister();
177-
boolean nonMutableChange = collection.isDirty()
178-
&& loadedPersister != null
179-
&& !loadedPersister.isMutable();
176+
final boolean nonMutableChange =
177+
collection.isDirty()
178+
&& loadedPersister != null
179+
&& !loadedPersister.isMutable();
180180
if ( nonMutableChange ) {
181-
throw new HibernateException(
182-
"changed an immutable collection instance: " +
183-
collectionInfoString( NullnessUtil.castNonNull( loadedPersister ).getRole(), getLoadedKey() )
184-
);
181+
throw new HibernateException( "changed an immutable collection instance: " +
182+
collectionInfoString( castNonNull( loadedPersister ).getRole(), getLoadedKey() ) );
185183
}
186184

187185
dirty( collection );
188186

189187
if ( LOG.isDebugEnabled() && collection.isDirty() && loadedPersister != null ) {
190-
LOG.debugf(
191-
"Collection dirty: %s",
192-
collectionInfoString( loadedPersister.getRole(), getLoadedKey() )
193-
);
188+
LOG.debug( "Collection dirty: " + collectionInfoString( loadedPersister.getRole(), getLoadedKey() ) );
194189
}
195190

196191
setReached( false );
@@ -208,9 +203,9 @@ public void postInitialize(PersistentCollection<?> collection, SharedSessionCont
208203
? collection.getSnapshot( loadedPersister )
209204
: null;
210205
collection.setSnapshot( loadedKey, role, snapshot );
211-
if ( loadedPersister != null && session.getLoadQueryInfluencers().effectivelyBatchLoadable( loadedPersister ) ) {
212-
session.getPersistenceContextInternal()
213-
.getBatchFetchQueue()
206+
if ( loadedPersister != null
207+
&& session.getLoadQueryInfluencers().effectivelyBatchLoadable( loadedPersister ) ) {
208+
session.getPersistenceContextInternal().getBatchFetchQueue()
214209
.removeBatchLoadableCollection( this );
215210
}
216211
}
@@ -236,12 +231,12 @@ public void afterAction(PersistentCollection<?> collection) {
236231
loadedKey = getCurrentKey();
237232
setLoadedPersister( getCurrentPersister() );
238233

239-
boolean resnapshot = collection.wasInitialized()
234+
final boolean resnapshot = collection.wasInitialized()
240235
&& ( isDoremove() || isDorecreate() || isDoupdate() );
241236
if ( resnapshot ) {
242-
snapshot = loadedPersister == null || !loadedPersister.isMutable() ?
243-
null :
244-
collection.getSnapshot( NullnessUtil.castNonNull( loadedPersister ) ); //re-snapshot
237+
snapshot = loadedPersister != null && loadedPersister.isMutable()
238+
? collection.getSnapshot( castNonNull( loadedPersister ) )
239+
: null; //re-snapshot
245240
}
246241

247242
collection.postAction();
@@ -271,13 +266,11 @@ public void afterAction(PersistentCollection<?> collection) {
271266
public void resetStoredSnapshot(PersistentCollection<?> collection, Serializable storedSnapshot) {
272267
LOG.debugf("Reset storedSnapshot to %s for %s", storedSnapshot, this);
273268

274-
if ( fromMerge ) {
275-
return; // EARLY EXIT!
269+
if ( !fromMerge ) {
270+
snapshot = storedSnapshot;
271+
collection.setSnapshot( loadedKey, role, snapshot );
272+
fromMerge = true;
276273
}
277-
278-
snapshot = storedSnapshot;
279-
collection.setSnapshot( loadedKey, role, snapshot );
280-
fromMerge = true;
281274
}
282275

283276
private void setLoadedPersister(@Nullable CollectionPersister persister) {
@@ -286,12 +279,9 @@ private void setLoadedPersister(@Nullable CollectionPersister persister) {
286279
}
287280

288281
void afterDeserialize(@Nullable SessionFactoryImplementor factory) {
289-
if ( factory == null ) {
290-
loadedPersister = null;
291-
}
292-
else {
293-
loadedPersister = factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( NullnessUtil.castNonNull( role ) );
294-
}
282+
loadedPersister = factory == null ? null
283+
: factory.getRuntimeMetamodels().getMappingMetamodel()
284+
.getCollectionDescriptor( castNonNull( role ) );
295285
}
296286

297287
public boolean wasDereferenced() {
@@ -379,13 +369,15 @@ public void setRole(@Nullable String role) {
379369

380370
@Override
381371
public String toString() {
382-
String result = "CollectionEntry" +
383-
collectionInfoString( role, loadedKey );
384-
if ( currentPersister != null ) {
385-
result += "->" +
386-
collectionInfoString( currentPersister.getRole(), currentKey );
372+
final StringBuilder result =
373+
new StringBuilder( "CollectionEntry" )
374+
.append( collectionInfoString( role, loadedKey ) );
375+
final CollectionPersister persister = currentPersister;
376+
if ( persister != null ) {
377+
result.append( "->" )
378+
.append( collectionInfoString( persister.getRole(), currentKey ) );
387379
}
388-
return result;
380+
return result.toString();
389381
}
390382

391383
/**
@@ -432,9 +424,8 @@ public void serialize(ObjectOutputStream oos) throws IOException {
432424
*
433425
* @return The deserialized CollectionEntry
434426
*/
435-
public static CollectionEntry deserialize(
436-
ObjectInputStream ois,
437-
SessionImplementor session) throws IOException, ClassNotFoundException {
427+
public static CollectionEntry deserialize(ObjectInputStream ois, SessionImplementor session)
428+
throws IOException, ClassNotFoundException {
438429
return new CollectionEntry(
439430
(String) ois.readObject(),
440431
(Serializable) ois.readObject(),

hibernate-core/src/main/java/org/hibernate/engine/spi/Status.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
/**
88
* Represents the status of an entity with respect to
99
* this session. These statuses are for internal
10-
* book-keeping only and are not intended to represent
11-
* any notion that is visible to the _application_.
10+
* bookkeeping only and are not intended to represent
11+
* any notion that is visible to the application
12+
* program.
1213
*/
1314
public enum Status {
1415
MANAGED,

hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -63,37 +63,30 @@ public abstract class AbstractFlushingEventListener {
6363
* @throws HibernateException Error flushing caches to execution queues.
6464
*/
6565
protected void flushEverythingToExecutions(FlushEvent event) throws HibernateException {
66-
6766
LOG.trace( "Flushing session" );
68-
6967
final EventSource session = event.getSession();
70-
7168
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
7269
preFlush( session, persistenceContext );
73-
7470
flushEverythingToExecutions( event, persistenceContext, session );
7571
}
7672

7773
protected void flushEverythingToExecutions(FlushEvent event, PersistenceContext persistenceContext, EventSource session) {
7874
persistenceContext.setFlushing( true );
7975
try {
80-
int entityCount = flushEntities( event, persistenceContext );
81-
int collectionCount = flushCollections( session, persistenceContext );
82-
76+
final int entityCount = flushEntities( event, persistenceContext );
77+
final int collectionCount = flushCollections( session, persistenceContext );
8378
event.setNumberOfEntitiesProcessed( entityCount );
8479
event.setNumberOfCollectionsProcessed( collectionCount );
8580
}
8681
finally {
8782
persistenceContext.setFlushing( false);
8883
}
89-
9084
//some statistics
9185
logFlushResults( event );
9286
}
9387

9488
protected void preFlush(EventSource session, PersistenceContext persistenceContext) {
9589
session.getInterceptor().preFlush( persistenceContext.managedEntitiesIterator() );
96-
9790
prepareEntityFlushes( session, persistenceContext );
9891
// we could move this inside if we wanted to
9992
// tolerate collection initializations during
@@ -134,9 +127,7 @@ protected void logFlushResults(FlushEvent event) {
134127
* and also apply orphan delete
135128
*/
136129
private void prepareEntityFlushes(EventSource session, PersistenceContext persistenceContext) throws HibernateException {
137-
138130
LOG.debug( "Processing flush-time cascades" );
139-
140131
final PersistContext context = PersistContext.create();
141132
// safe from concurrent modification because of how concurrentEntries() is implemented on IdentityMap
142133
for ( Map.Entry<Object,EntityEntry> me : persistenceContext.reentrantSafeEntityEntries() ) {
@@ -146,7 +137,6 @@ private void prepareEntityFlushes(EventSource session, PersistenceContext persis
146137
cascadeOnFlush( session, entry.getPersister(), me.getKey(), context );
147138
}
148139
}
149-
150140
checkForTransientReferences( session, persistenceContext );
151141
}
152142

@@ -194,14 +184,14 @@ private void cascadeOnFlush(EventSource session, EntityPersister persister, Obje
194184
* dirty check.
195185
*/
196186
private void prepareCollectionFlushes(PersistenceContext persistenceContext) throws HibernateException {
197-
198187
// Initialize dirty flags for arrays + collections with composite elements
199188
// and reset reached, doupdate, etc.
200-
201189
LOG.debug( "Dirty checking collections" );
202-
final Map<PersistentCollection<?>, CollectionEntry> collectionEntries = persistenceContext.getCollectionEntries();
190+
final Map<PersistentCollection<?>, CollectionEntry> collectionEntries =
191+
persistenceContext.getCollectionEntries();
203192
if ( collectionEntries != null ) {
204-
for ( Map.Entry<PersistentCollection<?>, CollectionEntry> entry : ( (IdentityMap<PersistentCollection<?>, CollectionEntry>) collectionEntries ).entryArray() ) {
193+
for ( Map.Entry<PersistentCollection<?>, CollectionEntry> entry :
194+
( (IdentityMap<PersistentCollection<?>, CollectionEntry>) collectionEntries ).entryArray() ) {
205195
entry.getValue().preFlush( entry.getKey() );
206196
}
207197
}
@@ -214,36 +204,28 @@ private void prepareCollectionFlushes(PersistenceContext persistenceContext) thr
214204
*/
215205
private int flushEntities(final FlushEvent event, final PersistenceContext persistenceContext)
216206
throws HibernateException {
217-
218207
LOG.trace( "Flushing entities and processing referenced collections" );
219208

220209
final EventSource source = event.getSession();
221210
final EventListenerGroup<FlushEntityEventListener> flushListeners =
222211
event.getFactory().getFastSessionServices().eventListenerGroup_FLUSH_ENTITY;
223212

224-
// Among other things, updateReachables() will recursively load all
225-
// collections that are moving roles. This might cause entities to
226-
// be loaded.
227-
213+
// Among other things, updateReachables() recursively loads all
214+
// collections that are changing roles. This might cause entities
215+
// to be loaded.
228216
// So this needs to be safe from concurrent modification problems.
229-
230217
final Map.Entry<Object,EntityEntry>[] entityEntries = persistenceContext.reentrantSafeEntityEntries();
231218
final int count = entityEntries.length;
232219

233220
FlushEntityEvent entityEvent = null; //allow reuse of the event as it's heavily allocated in certain use cases
234221
int eventGenerationId = 0; //Used to double-check the instance reuse won't cause problems
235-
236222
for ( Map.Entry<Object,EntityEntry> me : entityEntries ) {
237223
// Update the status of the object and if necessary, schedule an update
238-
239224
final EntityEntry entry = me.getValue();
240225
final Status status = entry.getStatus();
241-
242226
if ( status != Status.LOADING && status != Status.GONE ) {
243227
entityEvent = createOrReuseEventInstance( entityEvent, source, me.getKey(), entry );
244-
245228
entityEvent.setInstanceGenerationId( ++eventGenerationId );
246-
247229
flushListeners.fireEventOnEachListener( entityEvent, FlushEntityEventListener::onFlushEntity );
248230
entityEvent.setAllowedToReuse( true );
249231
assert entityEvent.getInstanceGenerationId() == eventGenerationId;
@@ -281,7 +263,6 @@ private FlushEntityEvent createOrReuseEventInstance(
281263
private int flushCollections(final EventSource session, final PersistenceContext persistenceContext)
282264
throws HibernateException {
283265
LOG.trace( "Processing unreferenced collections" );
284-
285266
final Map<PersistentCollection<?>, CollectionEntry> collectionEntries = persistenceContext.getCollectionEntries();
286267
final int count;
287268
if ( collectionEntries == null ) {
@@ -300,7 +281,6 @@ private int flushCollections(final EventSource session, final PersistenceContext
300281
// Schedule updates to collections:
301282

302283
LOG.trace( "Scheduling collection removes/(re)creates/updates" );
303-
304284
final ActionQueue actionQueue = session.getActionQueue();
305285
final Interceptor interceptor = session.getInterceptor();
306286
persistenceContext.forEachCollectionEntry(
@@ -407,7 +387,6 @@ protected void performExecutions(EventSource session) {
407387
* 3. call Interceptor.postFlush()
408388
*/
409389
protected void postFlush(SessionImplementor session) throws HibernateException {
410-
411390
LOG.trace( "Post flush" );
412391

413392
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
@@ -429,13 +408,12 @@ protected void postFlush(SessionImplementor session) throws HibernateException {
429408
}
430409
else {
431410
//otherwise recreate the mapping between the collection and its key
432-
final CollectionKey collectionKey = new CollectionKey(
433-
collectionEntry.getLoadedPersister(),
434-
key
435-
);
411+
final CollectionKey collectionKey =
412+
new CollectionKey( collectionEntry.getLoadedPersister(), key );
436413
persistenceContext.addCollectionByKey( collectionKey, persistentCollection );
437414
}
438-
}, true
415+
},
416+
true
439417
);
440418
}
441419

hibernate-core/src/main/java/org/hibernate/event/internal/AbstractVisitor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,19 @@ void processValue(int i, Object[] values, Type[] types) {
6060
}
6161

6262
boolean includeEntityProperty(Object[] values, int i) {
63-
return includeProperty(values, i);
63+
return includeProperty( values, i );
6464
}
6565

6666
boolean includeProperty(Object[] values, int i) {
67-
return values[i]!= LazyPropertyInitializer.UNFETCHED_PROPERTY;
67+
return values[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY;
6868
}
6969

7070
/**
7171
* Visit a component. Dispatch each property
7272
* to processValue().
7373
*/
7474
Object processComponent(Object component, CompositeType componentType) throws HibernateException {
75-
if ( component!=null ) {
75+
if ( component != null ) {
7676
processValues( componentType.getPropertyValues(component, session), componentType.getSubtypes() );
7777
}
7878
return null;

0 commit comments

Comments
 (0)