HHH-14413 fix issue that EntityUpdateAction increments version despite veto on update
This commit is contained in:
parent
86ad83f0b5
commit
ad6af3af7d
|
@ -171,7 +171,9 @@ public class EntityUpdateAction extends EntityAction {
|
||||||
final SharedSessionContractImplementor session = getSession();
|
final SharedSessionContractImplementor session = getSession();
|
||||||
final Object instance = getInstance();
|
final Object instance = getInstance();
|
||||||
|
|
||||||
final boolean veto = preUpdate();
|
if ( preUpdate() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final SessionFactoryImplementor factory = session.getFactory();
|
final SessionFactoryImplementor factory = session.getFactory();
|
||||||
Object previousVersion = this.previousVersion;
|
Object previousVersion = this.previousVersion;
|
||||||
|
@ -196,27 +198,24 @@ public class EntityUpdateAction extends EntityAction {
|
||||||
else {
|
else {
|
||||||
ck = null;
|
ck = null;
|
||||||
}
|
}
|
||||||
|
persister.update(
|
||||||
if ( !veto ) {
|
id,
|
||||||
persister.update(
|
state,
|
||||||
id,
|
dirtyFields,
|
||||||
state,
|
hasDirtyCollection,
|
||||||
dirtyFields,
|
previousState,
|
||||||
hasDirtyCollection,
|
previousVersion,
|
||||||
previousState,
|
instance,
|
||||||
previousVersion,
|
rowId,
|
||||||
instance,
|
session
|
||||||
rowId,
|
);
|
||||||
session
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( instance );
|
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( instance );
|
||||||
if ( entry == null ) {
|
if ( entry == null ) {
|
||||||
throw new AssertionFailure( "possible nonthreadsafe access to session" );
|
throw new AssertionFailure( "possible non thread safe access to session" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
|
if ( entry.getStatus() == Status.MANAGED || persister.isVersionPropertyGenerated() ) {
|
||||||
// get the updated snapshot of the entity state by cloning current state;
|
// get the updated snapshot of the entity state by cloning current state;
|
||||||
// it is safe to copy in place, since by this time no-one else (should have)
|
// it is safe to copy in place, since by this time no-one else (should have)
|
||||||
// has a reference to the array
|
// has a reference to the array
|
||||||
|
@ -242,12 +241,12 @@ public class EntityUpdateAction extends EntityAction {
|
||||||
|
|
||||||
final StatisticsImplementor statistics = factory.getStatistics();
|
final StatisticsImplementor statistics = factory.getStatistics();
|
||||||
if ( persister.canWriteToCache() ) {
|
if ( persister.canWriteToCache() ) {
|
||||||
if ( persister.isCacheInvalidationRequired() || entry.getStatus()!= Status.MANAGED ) {
|
if ( persister.isCacheInvalidationRequired() || entry.getStatus() != Status.MANAGED ) {
|
||||||
persister.getCacheAccessStrategy().remove( session, ck);
|
persister.getCacheAccessStrategy().remove( session, ck );
|
||||||
}
|
}
|
||||||
else if ( session.getCacheMode().isPutEnabled() ) {
|
else if ( session.getCacheMode().isPutEnabled() ) {
|
||||||
//TODO: inefficient if that cache is just going to ignore the updated state!
|
//TODO: inefficient if that cache is just going to ignore the updated state!
|
||||||
final CacheEntry ce = persister.buildCacheEntry( instance,state, nextVersion, getSession() );
|
final CacheEntry ce = persister.buildCacheEntry( instance, state, nextVersion, getSession() );
|
||||||
cacheEntry = persister.getCacheEntryStructure().structure( ce );
|
cacheEntry = persister.getCacheEntryStructure().structure( ce );
|
||||||
|
|
||||||
final boolean put = cacheUpdate( persister, previousVersion, ck );
|
final boolean put = cacheUpdate( persister, previousVersion, ck );
|
||||||
|
@ -270,9 +269,10 @@ public class EntityUpdateAction extends EntityAction {
|
||||||
|
|
||||||
postUpdate();
|
postUpdate();
|
||||||
|
|
||||||
if ( statistics.isStatisticsEnabled() && !veto ) {
|
if ( statistics.isStatisticsEnabled() ) {
|
||||||
statistics.updateEntity( getPersister().getEntityName() );
|
statistics.updateEntity( getPersister().getEntityName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean cacheUpdate(EntityPersister persister, Object previousVersion, Object ck) {
|
protected boolean cacheUpdate(EntityPersister persister, Object previousVersion, Object ck) {
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
package org.hibernate.event;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Version;
|
||||||
|
|
||||||
|
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||||
|
import org.hibernate.event.spi.EventType;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
* @author Tassilo Karge
|
||||||
|
*/
|
||||||
|
@TestForIssue( jiraKey = "HHH-14413" )
|
||||||
|
public class PreUpdateEventListenerVetoTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
private static final Long EXAMPLE_ID_VALUE = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[] { ExampleEntity.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void afterSessionFactoryBuilt() {
|
||||||
|
super.afterSessionFactoryBuilt();
|
||||||
|
EventListenerRegistry registry = sessionFactory().getServiceRegistry().getService( EventListenerRegistry.class );
|
||||||
|
registry.appendListeners(
|
||||||
|
EventType.PRE_UPDATE,
|
||||||
|
event -> true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
ExampleEntity entity = new ExampleEntity();
|
||||||
|
entity.id = EXAMPLE_ID_VALUE;
|
||||||
|
entity.name = "old_name";
|
||||||
|
session.save( entity );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVersionNotChangedWhenPreUpdateEventVetoed() {
|
||||||
|
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
ExampleEntity entity = session.byId( ExampleEntity.class ).load( EXAMPLE_ID_VALUE );
|
||||||
|
|
||||||
|
entity.name = "new_name";
|
||||||
|
session.update( entity );
|
||||||
|
|
||||||
|
final Long versionBeforeFlush = entity.version;
|
||||||
|
|
||||||
|
session.flush();
|
||||||
|
|
||||||
|
final Long versionAfterFlush = entity.version;
|
||||||
|
|
||||||
|
assertEquals( "The entity version must not change when update is vetoed", versionBeforeFlush, versionAfterFlush );
|
||||||
|
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "ExampleEntity")
|
||||||
|
public static class ExampleEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
String name;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
Long version;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue