Skip to content

Commit ec3be76

Browse files
committed
HHH-18553 handle case where managed entity was already removed
just short-circuit and abort the remove() Signed-off-by: Gavin King <[email protected]>
1 parent edf8130 commit ec3be76

File tree

2 files changed

+55
-27
lines changed

2 files changed

+55
-27
lines changed

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

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -176,51 +176,65 @@ private void deleteDetachedEntity(
176176
}
177177

178178
final EntityKey key = source.generateEntityKey( id, persister);
179-
final Object version = persister.getVersion(entity);
179+
final Object version = persister.getVersion( entity );
180180

181181
// persistenceContext.checkUniqueness( key, entity );
182-
flushAndEvictExistingEntity( key, version, persister, source );
182+
if ( !flushAndEvictExistingEntity( key, version, persister, source ) ) {
183183

184-
new OnUpdateVisitor( source, id, entity ).process( entity, persister );
184+
new OnUpdateVisitor( source, id, entity ).process( entity, persister );
185185

186-
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
187-
final EntityEntry entityEntry = persistenceContext.addEntity(
188-
entity,
189-
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
190-
persister.getValues(entity),
191-
key,
192-
version,
193-
LockMode.NONE,
194-
true,
195-
persister,
196-
false
197-
);
198-
persister.afterReassociate(entity, source);
186+
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
187+
final EntityEntry entityEntry = persistenceContext.addEntity(
188+
entity,
189+
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
190+
persister.getValues(entity),
191+
key,
192+
version,
193+
LockMode.NONE,
194+
true,
195+
persister,
196+
false
197+
);
198+
persister.afterReassociate( entity, source );
199199

200-
delete( event, transientEntities, source, entity, persister, id, version, entityEntry );
200+
delete( event, transientEntities, source, entity, persister, id, version, entityEntry );
201+
}
201202
}
202203

203204
/**
204205
* Since Hibernate 7, if a detached instance is passed to remove(),
205206
* and if there is already an existing managed entity with the same
206207
* id, flush and evict it, after checking that the versions match.
208+
*
209+
* @return true if the managed entity was already deleted
207210
*/
208-
private static void flushAndEvictExistingEntity(
211+
private static boolean flushAndEvictExistingEntity(
209212
EntityKey key, Object version, EntityPersister persister, EventSource source) {
210-
final Object existingEntity = source.getPersistenceContextInternal().getEntity( key );
213+
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
214+
final Object existingEntity = persistenceContext.getEntity( key );
211215
if ( existingEntity != null ) {
212-
LOG.flushAndEvictOnRemove( key.getEntityName() );
213-
source.flush();
214-
if ( !persister.isVersioned()
215-
|| persister.getVersionType()
216-
.isEqual( version, persister.getVersion( existingEntity ) ) ) {
217-
source.evict( existingEntity );
216+
if ( persistenceContext.getEntry( existingEntity ).getStatus().isDeletedOrGone() ) {
217+
// already deleted, no work to do
218+
return true;
218219
}
219220
else {
220-
throw new StaleObjectStateException( key.getEntityName(), key.getIdentifier(),
221-
"Persistence context contains a more recent version of the given entity" );
221+
LOG.flushAndEvictOnRemove( key.getEntityName() );
222+
source.flush();
223+
if ( !persister.isVersioned()
224+
|| persister.getVersionType()
225+
.isEqual( version, persister.getVersion( existingEntity ) ) ) {
226+
source.evict( existingEntity );
227+
return false;
228+
}
229+
else {
230+
throw new StaleObjectStateException( key.getEntityName(), key.getIdentifier(),
231+
"Persistence context contains a more recent version of the given entity" );
232+
}
222233
}
223234
}
235+
else {
236+
return false;
237+
}
224238
}
225239

226240
private void deletePersistentInstance(

hibernate-core/src/test/java/org/hibernate/orm/test/deletedetached/DeleteDetachedTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ public class DeleteDetachedTest {
5656
});
5757
scope.inTransaction(s -> assertNotNull(s.find(Thing.class, thing.id)));
5858
}
59+
@Test void testAlreadyRemoved(SessionFactoryScope scope) {
60+
Thing thing = new Thing();
61+
thing.stuff = "Some stuff about the thing";
62+
scope.inTransaction(s -> s.persist(thing));
63+
scope.inTransaction(s -> {
64+
Thing otherThing = s.find(Thing.class, thing.id);
65+
assertNotNull(otherThing);
66+
s.remove(otherThing);
67+
s.remove(thing);
68+
assertFalse(s.contains(thing));
69+
assertFalse(s.contains(otherThing));
70+
});
71+
scope.inTransaction(s -> assertNull(s.find(Thing.class, thing.id)));
72+
}
5973
@Entity
6074
static class Thing {
6175
@GeneratedValue @Id long id;

0 commit comments

Comments
 (0)