HHH-18553 handle case where managed entity was already removed
just short-circuit and abort the remove() Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
edf813083e
commit
ec3be767e4
|
@ -176,51 +176,65 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
|
|||
}
|
||||
|
||||
final EntityKey key = source.generateEntityKey( id, persister);
|
||||
final Object version = persister.getVersion(entity);
|
||||
final Object version = persister.getVersion( entity );
|
||||
|
||||
// persistenceContext.checkUniqueness( key, entity );
|
||||
flushAndEvictExistingEntity( key, version, persister, source );
|
||||
if ( !flushAndEvictExistingEntity( key, version, persister, source ) ) {
|
||||
|
||||
new OnUpdateVisitor( source, id, entity ).process( entity, persister );
|
||||
new OnUpdateVisitor( source, id, entity ).process( entity, persister );
|
||||
|
||||
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
|
||||
final EntityEntry entityEntry = persistenceContext.addEntity(
|
||||
entity,
|
||||
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
|
||||
persister.getValues(entity),
|
||||
key,
|
||||
version,
|
||||
LockMode.NONE,
|
||||
true,
|
||||
persister,
|
||||
false
|
||||
);
|
||||
persister.afterReassociate(entity, source);
|
||||
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
|
||||
final EntityEntry entityEntry = persistenceContext.addEntity(
|
||||
entity,
|
||||
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
|
||||
persister.getValues(entity),
|
||||
key,
|
||||
version,
|
||||
LockMode.NONE,
|
||||
true,
|
||||
persister,
|
||||
false
|
||||
);
|
||||
persister.afterReassociate( entity, source );
|
||||
|
||||
delete( event, transientEntities, source, entity, persister, id, version, entityEntry );
|
||||
delete( event, transientEntities, source, entity, persister, id, version, entityEntry );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since Hibernate 7, if a detached instance is passed to remove(),
|
||||
* and if there is already an existing managed entity with the same
|
||||
* id, flush and evict it, after checking that the versions match.
|
||||
*
|
||||
* @return true if the managed entity was already deleted
|
||||
*/
|
||||
private static void flushAndEvictExistingEntity(
|
||||
private static boolean flushAndEvictExistingEntity(
|
||||
EntityKey key, Object version, EntityPersister persister, EventSource source) {
|
||||
final Object existingEntity = source.getPersistenceContextInternal().getEntity( key );
|
||||
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
|
||||
final Object existingEntity = persistenceContext.getEntity( key );
|
||||
if ( existingEntity != null ) {
|
||||
LOG.flushAndEvictOnRemove( key.getEntityName() );
|
||||
source.flush();
|
||||
if ( !persister.isVersioned()
|
||||
|| persister.getVersionType()
|
||||
.isEqual( version, persister.getVersion( existingEntity ) ) ) {
|
||||
source.evict( existingEntity );
|
||||
if ( persistenceContext.getEntry( existingEntity ).getStatus().isDeletedOrGone() ) {
|
||||
// already deleted, no work to do
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
throw new StaleObjectStateException( key.getEntityName(), key.getIdentifier(),
|
||||
"Persistence context contains a more recent version of the given entity" );
|
||||
LOG.flushAndEvictOnRemove( key.getEntityName() );
|
||||
source.flush();
|
||||
if ( !persister.isVersioned()
|
||||
|| persister.getVersionType()
|
||||
.isEqual( version, persister.getVersion( existingEntity ) ) ) {
|
||||
source.evict( existingEntity );
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
throw new StaleObjectStateException( key.getEntityName(), key.getIdentifier(),
|
||||
"Persistence context contains a more recent version of the given entity" );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void deletePersistentInstance(
|
||||
|
|
|
@ -56,6 +56,20 @@ public class DeleteDetachedTest {
|
|||
});
|
||||
scope.inTransaction(s -> assertNotNull(s.find(Thing.class, thing.id)));
|
||||
}
|
||||
@Test void testAlreadyRemoved(SessionFactoryScope scope) {
|
||||
Thing thing = new Thing();
|
||||
thing.stuff = "Some stuff about the thing";
|
||||
scope.inTransaction(s -> s.persist(thing));
|
||||
scope.inTransaction(s -> {
|
||||
Thing otherThing = s.find(Thing.class, thing.id);
|
||||
assertNotNull(otherThing);
|
||||
s.remove(otherThing);
|
||||
s.remove(thing);
|
||||
assertFalse(s.contains(thing));
|
||||
assertFalse(s.contains(otherThing));
|
||||
});
|
||||
scope.inTransaction(s -> assertNull(s.find(Thing.class, thing.id)));
|
||||
}
|
||||
@Entity
|
||||
static class Thing {
|
||||
@GeneratedValue @Id long id;
|
||||
|
|
Loading…
Reference in New Issue