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 EntityKey key = source.generateEntityKey( id, persister);
|
||||||
final Object version = persister.getVersion(entity);
|
final Object version = persister.getVersion( entity );
|
||||||
|
|
||||||
// persistenceContext.checkUniqueness( key, 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 PersistenceContext persistenceContext = source.getPersistenceContextInternal();
|
||||||
final EntityEntry entityEntry = persistenceContext.addEntity(
|
final EntityEntry entityEntry = persistenceContext.addEntity(
|
||||||
entity,
|
entity,
|
||||||
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
|
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
|
||||||
persister.getValues(entity),
|
persister.getValues(entity),
|
||||||
key,
|
key,
|
||||||
version,
|
version,
|
||||||
LockMode.NONE,
|
LockMode.NONE,
|
||||||
true,
|
true,
|
||||||
persister,
|
persister,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
persister.afterReassociate(entity, source);
|
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(),
|
* Since Hibernate 7, if a detached instance is passed to remove(),
|
||||||
* and if there is already an existing managed entity with the same
|
* and if there is already an existing managed entity with the same
|
||||||
* id, flush and evict it, after checking that the versions match.
|
* 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) {
|
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 ) {
|
if ( existingEntity != null ) {
|
||||||
LOG.flushAndEvictOnRemove( key.getEntityName() );
|
if ( persistenceContext.getEntry( existingEntity ).getStatus().isDeletedOrGone() ) {
|
||||||
source.flush();
|
// already deleted, no work to do
|
||||||
if ( !persister.isVersioned()
|
return true;
|
||||||
|| persister.getVersionType()
|
|
||||||
.isEqual( version, persister.getVersion( existingEntity ) ) ) {
|
|
||||||
source.evict( existingEntity );
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new StaleObjectStateException( key.getEntityName(), key.getIdentifier(),
|
LOG.flushAndEvictOnRemove( key.getEntityName() );
|
||||||
"Persistence context contains a more recent version of the given entity" );
|
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(
|
private void deletePersistentInstance(
|
||||||
|
|
|
@ -56,6 +56,20 @@ public class DeleteDetachedTest {
|
||||||
});
|
});
|
||||||
scope.inTransaction(s -> assertNotNull(s.find(Thing.class, thing.id)));
|
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
|
@Entity
|
||||||
static class Thing {
|
static class Thing {
|
||||||
@GeneratedValue @Id long id;
|
@GeneratedValue @Id long id;
|
||||||
|
|
Loading…
Reference in New Issue