minor code cleanups to Actions

This commit is contained in:
Gavin King 2022-09-29 23:20:15 +02:00
parent a11ebdeefc
commit 8f9b998894
13 changed files with 361 additions and 349 deletions

View File

@ -113,7 +113,7 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
* @see #makeEntityManaged() * @see #makeEntityManaged()
*/ */
protected final void nullifyTransientReferencesIfNotAlready() { protected final void nullifyTransientReferencesIfNotAlready() {
if ( ! areTransientReferencesNullified ) { if ( !areTransientReferencesNullified ) {
new ForeignKeys.Nullifier( getInstance(), false, isEarlyInsert(), getSession(), getPersister() ) new ForeignKeys.Nullifier( getInstance(), false, isEarlyInsert(), getSession(), getPersister() )
.nullifyTransientReferences( getState() ); .nullifyTransientReferences( getState() );
new Nullability( getSession() ).checkNullability( getState(), getPersister(), false ); new Nullability( getSession() ).checkNullability( getState(), getPersister(), false );
@ -210,28 +210,25 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
*/ */
public void handleNaturalIdPostSaveNotifications(Object generatedId) { public void handleNaturalIdPostSaveNotifications(Object generatedId) {
final NaturalIdMapping naturalIdMapping = getPersister().getNaturalIdMapping(); final NaturalIdMapping naturalIdMapping = getPersister().getNaturalIdMapping();
if ( naturalIdMapping == null ) { if ( naturalIdMapping != null ) {
return; final Object naturalIdValues = naturalIdMapping.extractNaturalIdFromEntityState( state, getSession() );
} if ( isEarlyInsert() ) {
// with early insert, we still need to add a local (transactional) natural id cross-reference
final Object naturalIdValues = naturalIdMapping.extractNaturalIdFromEntityState( state, getSession() ); getSession().getPersistenceContextInternal().getNaturalIdResolutions().manageLocalResolution(
generatedId,
if ( isEarlyInsert() ) { naturalIdValues,
// with early insert, we still need to add a local (transactional) natural id cross-reference getPersister(),
getSession().getPersistenceContextInternal().getNaturalIdResolutions().manageLocalResolution( CachedNaturalIdValueSource.INSERT
);
}
// after save, we need to manage the shared cache entries
getSession().getPersistenceContextInternal().getNaturalIdResolutions().manageSharedResolution(
generatedId, generatedId,
naturalIdValues, naturalIdValues,
null,
getPersister(), getPersister(),
CachedNaturalIdValueSource.INSERT CachedNaturalIdValueSource.INSERT
); );
} }
// after save, we need to manage the shared cache entries
getSession().getPersistenceContextInternal().getNaturalIdResolutions().manageSharedResolution(
generatedId,
naturalIdValues,
null,
getPersister(),
CachedNaturalIdValueSource.INSERT
);
} }
} }

View File

@ -46,6 +46,7 @@ import org.hibernate.sql.ast.tree.insert.InsertStatement;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class BulkOperationCleanupAction implements Executable, Serializable { public class BulkOperationCleanupAction implements Executable, Serializable {
private final Serializable[] affectedTableSpaces; private final Serializable[] affectedTableSpaces;
private final Set<EntityCleanup> entityCleanups = new HashSet<>(); private final Set<EntityCleanup> entityCleanups = new HashSet<>();
@ -63,7 +64,6 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
* @param affectedQueryables The affected entity persisters. * @param affectedQueryables The affected entity persisters.
*/ */
public BulkOperationCleanupAction(SharedSessionContractImplementor session, EntityPersister... affectedQueryables) { public BulkOperationCleanupAction(SharedSessionContractImplementor session, EntityPersister... affectedQueryables) {
final SessionFactoryImplementor factory = session.getFactory();
final LinkedHashSet<String> spacesList = new LinkedHashSet<>(); final LinkedHashSet<String> spacesList = new LinkedHashSet<>();
for ( EntityPersister persister : affectedQueryables ) { for ( EntityPersister persister : affectedQueryables ) {
Collections.addAll( spacesList, (String[]) persister.getQuerySpaces() ); Collections.addAll( spacesList, (String[]) persister.getQuerySpaces() );
@ -81,17 +81,14 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
); );
} }
final MappingMetamodelImplementor mappingMetamodel = factory.getRuntimeMetamodels().getMappingMetamodel(); final MappingMetamodelImplementor mappingMetamodel = session.getFactory().getRuntimeMetamodels().getMappingMetamodel();
final Set<String> roles = mappingMetamodel.getCollectionRolesByEntityParticipant( persister.getEntityName() ); final Set<String> roles = mappingMetamodel.getCollectionRolesByEntityParticipant( persister.getEntityName() );
if ( roles != null ) { if ( roles != null ) {
for ( String role : roles ) { for ( String role : roles ) {
final CollectionPersister collectionPersister = mappingMetamodel.getCollectionDescriptor( role ); final CollectionPersister collectionPersister = mappingMetamodel.getCollectionDescriptor( role );
if ( collectionPersister.hasCache() ) { if ( collectionPersister.hasCache() ) {
collectionCleanups.add( collectionCleanups.add(
new CollectionCleanup( new CollectionCleanup( collectionPersister.getCacheAccessStrategy(), session )
collectionPersister.getCacheAccessStrategy(),
session
)
); );
} }
} }
@ -103,11 +100,12 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
/** /**
* Constructs an action to cleanup "affected cache regions" based on a * Constructs an action to cleanup "affected cache regions" based on a
* set of affected table spaces. This differs from {@link #BulkOperationCleanupAction(SharedSessionContractImplementor, EntityPersister[])} * set of affected table spaces. This differs from
* in that here we have the affected <strong>table names</strong>. From those * {@link #BulkOperationCleanupAction(SharedSessionContractImplementor, EntityPersister[])}
* we deduce the entity persisters which are affected based on the defined * in that here we have the affected <strong>table names</strong>. From
* {@link EntityPersister#getQuerySpaces() table spaces}; and from there, we * those we deduce the entity persisters which are affected based on the
* determine the affected collection regions based on any collections * defined {@link EntityPersister#getQuerySpaces() table spaces}. Finally,
* we determine the affected collection regions based on any collections
* in which those entity persisters participate as elements/keys/etc. * in which those entity persisters participate as elements/keys/etc.
* *
* @param session The session to which this request is tied. * @param session The session to which this request is tied.
@ -116,8 +114,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
public BulkOperationCleanupAction(SharedSessionContractImplementor session, Set<String> tableSpaces) { public BulkOperationCleanupAction(SharedSessionContractImplementor session, Set<String> tableSpaces) {
final LinkedHashSet<String> spacesList = new LinkedHashSet<>( tableSpaces ); final LinkedHashSet<String> spacesList = new LinkedHashSet<>( tableSpaces );
final SessionFactoryImplementor factory = session.getFactory(); final MappingMetamodelImplementor metamodel = session.getFactory().getRuntimeMetamodels().getMappingMetamodel();
final MappingMetamodelImplementor metamodel = factory.getRuntimeMetamodels().getMappingMetamodel();
metamodel.forEachEntityDescriptor( (entityDescriptor) -> { metamodel.forEachEntityDescriptor( (entityDescriptor) -> {
final String[] entitySpaces = (String[]) entityDescriptor.getQuerySpaces(); final String[] entitySpaces = (String[]) entityDescriptor.getQuerySpaces();
if ( affectedEntity( tableSpaces, entitySpaces ) ) { if ( affectedEntity( tableSpaces, entitySpaces ) ) {

View File

@ -28,6 +28,7 @@ import org.hibernate.pretty.MessageHelper;
* @author Gavin King * @author Gavin King
*/ */
public abstract class CollectionAction implements Executable, Serializable, Comparable<CollectionAction> { public abstract class CollectionAction implements Executable, Serializable, Comparable<CollectionAction> {
private transient CollectionPersister persister; private transient CollectionPersister persister;
private transient SharedSessionContractImplementor session; private transient SharedSessionContractImplementor session;
private final PersistentCollection<?> collection; private final PersistentCollection<?> collection;

View File

@ -41,7 +41,6 @@ public final class CollectionRecreateAction extends CollectionAction {
// this method is called when a new non-null collection is persisted // this method is called when a new non-null collection is persisted
// or when an existing (non-null) collection is moved to a new owner // or when an existing (non-null) collection is moved to a new owner
final PersistentCollection<?> collection = getCollection(); final PersistentCollection<?> collection = getCollection();
preRecreate(); preRecreate();
final SharedSessionContractImplementor session = getSession(); final SharedSessionContractImplementor session = getSession();
getPersister().recreate( collection, getKey(), session); getPersister().recreate( collection, getKey(), session);
@ -56,9 +55,9 @@ public final class CollectionRecreateAction extends CollectionAction {
} }
private void preRecreate() { private void preRecreate() {
getFastSessionServices() getFastSessionServices().eventListenerGroup_PRE_COLLECTION_RECREATE
.eventListenerGroup_PRE_COLLECTION_RECREATE .fireLazyEventOnEachListener( this::newPreCollectionRecreateEvent,
.fireLazyEventOnEachListener( this::newPreCollectionRecreateEvent, PreCollectionRecreateEventListener::onPreRecreateCollection ); PreCollectionRecreateEventListener::onPreRecreateCollection );
} }
private PreCollectionRecreateEvent newPreCollectionRecreateEvent() { private PreCollectionRecreateEvent newPreCollectionRecreateEvent() {
@ -66,9 +65,9 @@ public final class CollectionRecreateAction extends CollectionAction {
} }
private void postRecreate() { private void postRecreate() {
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_COLLECTION_RECREATE
.eventListenerGroup_POST_COLLECTION_RECREATE .fireLazyEventOnEachListener( this::newPostCollectionRecreateEvent,
.fireLazyEventOnEachListener( this::newPostCollectionRecreateEvent, PostCollectionRecreateEventListener::onPostRecreateCollection ); PostCollectionRecreateEventListener::onPostRecreateCollection );
} }
private PostCollectionRecreateEvent newPostCollectionRecreateEvent() { private PostCollectionRecreateEvent newPostCollectionRecreateEvent() {

View File

@ -21,6 +21,7 @@ import org.hibernate.stat.spi.StatisticsImplementor;
* The action for removing a collection * The action for removing a collection
*/ */
public final class CollectionRemoveAction extends CollectionAction { public final class CollectionRemoveAction extends CollectionAction {
private final Object affectedOwner; private final Object affectedOwner;
private final boolean emptySnapshot; private final boolean emptySnapshot;
@ -101,9 +102,7 @@ public final class CollectionRemoveAction extends CollectionAction {
@Override @Override
public void execute() throws HibernateException { public void execute() throws HibernateException {
preRemove(); preRemove();
final SharedSessionContractImplementor session = getSession(); final SharedSessionContractImplementor session = getSession();
if ( !emptySnapshot ) { if ( !emptySnapshot ) {
// an existing collection that was either nonempty or uninitialized // an existing collection that was either nonempty or uninitialized
// is replaced by null or a different collection // is replaced by null or a different collection
@ -111,12 +110,10 @@ public final class CollectionRemoveAction extends CollectionAction {
// knowing if the collection is actually empty without querying the db) // knowing if the collection is actually empty without querying the db)
getPersister().remove( getKey(), session ); getPersister().remove( getKey(), session );
} }
final PersistentCollection<?> collection = getCollection(); final PersistentCollection<?> collection = getCollection();
if ( collection != null ) { if ( collection != null ) {
session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection ); session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection );
} }
evict(); evict();
postRemove(); postRemove();
@ -127,9 +124,9 @@ public final class CollectionRemoveAction extends CollectionAction {
} }
private void preRemove() { private void preRemove() {
getFastSessionServices() getFastSessionServices().eventListenerGroup_PRE_COLLECTION_REMOVE
.eventListenerGroup_PRE_COLLECTION_REMOVE .fireLazyEventOnEachListener( this::newPreCollectionRemoveEvent,
.fireLazyEventOnEachListener( this::newPreCollectionRemoveEvent, PreCollectionRemoveEventListener::onPreRemoveCollection ); PreCollectionRemoveEventListener::onPreRemoveCollection );
} }
private PreCollectionRemoveEvent newPreCollectionRemoveEvent() { private PreCollectionRemoveEvent newPreCollectionRemoveEvent() {
@ -142,9 +139,9 @@ public final class CollectionRemoveAction extends CollectionAction {
} }
private void postRemove() { private void postRemove() {
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_COLLECTION_REMOVE
.eventListenerGroup_POST_COLLECTION_REMOVE .fireLazyEventOnEachListener( this::newPostCollectionRemoveEvent,
.fireLazyEventOnEachListener( this::newPostCollectionRemoveEvent, PostCollectionRemoveEventListener::onPostRemoveCollection ); PostCollectionRemoveEventListener::onPostRemoveCollection );
} }
private PostCollectionRemoveEvent newPostCollectionRemoveEvent() { private PostCollectionRemoveEvent newPostCollectionRemoveEvent() {

View File

@ -22,6 +22,7 @@ import org.hibernate.stat.spi.StatisticsImplementor;
* The action for updating a collection * The action for updating a collection
*/ */
public final class CollectionUpdateAction extends CollectionAction { public final class CollectionUpdateAction extends CollectionAction {
private final boolean emptySnapshot; private final boolean emptySnapshot;
/** /**
@ -68,9 +69,8 @@ public final class CollectionUpdateAction extends CollectionAction {
} }
else if ( collection.needsRecreate( persister ) ) { else if ( collection.needsRecreate( persister ) ) {
if ( affectedByFilters ) { if ( affectedByFilters ) {
throw new HibernateException( throw new HibernateException( "cannot recreate collection while filter is enabled: "
"cannot recreate collection while filter is enabled: " + + MessageHelper.collectionInfoString( persister, collection, id, session )
MessageHelper.collectionInfoString( persister, collection, id, session )
); );
} }
if ( !emptySnapshot ) { if ( !emptySnapshot ) {
@ -95,9 +95,9 @@ public final class CollectionUpdateAction extends CollectionAction {
} }
private void preUpdate() { private void preUpdate() {
getFastSessionServices() getFastSessionServices().eventListenerGroup_PRE_COLLECTION_UPDATE
.eventListenerGroup_PRE_COLLECTION_UPDATE .fireLazyEventOnEachListener( this::newPreCollectionUpdateEvent,
.fireLazyEventOnEachListener( this::newPreCollectionUpdateEvent, PreCollectionUpdateEventListener::onPreUpdateCollection ); PreCollectionUpdateEventListener::onPreUpdateCollection );
} }
private PreCollectionUpdateEvent newPreCollectionUpdateEvent() { private PreCollectionUpdateEvent newPreCollectionUpdateEvent() {
@ -109,9 +109,9 @@ public final class CollectionUpdateAction extends CollectionAction {
} }
private void postUpdate() { private void postUpdate() {
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_COLLECTION_UPDATE
.eventListenerGroup_POST_COLLECTION_UPDATE .fireLazyEventOnEachListener( this::newPostCollectionUpdateEvent,
.fireLazyEventOnEachListener( this::newPostCollectionUpdateEvent, PostCollectionUpdateEventListener::onPostUpdateCollection ); PostCollectionUpdateEventListener::onPostUpdateCollection );
} }
private PostCollectionUpdateEvent newPostCollectionUpdateEvent() { private PostCollectionUpdateEvent newPostCollectionUpdateEvent() {

View File

@ -106,9 +106,7 @@ public abstract class EntityAction
} }
public final DelayedPostInsertIdentifier getDelayedId() { public final DelayedPostInsertIdentifier getDelayedId() {
return id instanceof DelayedPostInsertIdentifier return id instanceof DelayedPostInsertIdentifier ? (DelayedPostInsertIdentifier) id : null;
? (DelayedPostInsertIdentifier) id
: null;
} }
/** /**
@ -157,13 +155,9 @@ public abstract class EntityAction
public int compareTo(EntityAction action) { public int compareTo(EntityAction action) {
//sort first by entity name //sort first by entity name
final int roleComparison = entityName.compareTo( action.entityName ); final int roleComparison = entityName.compareTo( action.entityName );
if ( roleComparison != 0 ) { return roleComparison != 0 ? roleComparison
return roleComparison; //then by id
} : persister.getIdentifierType().compare( id, action.id );
else {
//then by id
return persister.getIdentifierType().compare( id, action.id );
}
} }
/** /**
@ -180,7 +174,7 @@ public abstract class EntityAction
// guard against NullPointerException // guard against NullPointerException
if ( session != null ) { if ( session != null ) {
this.session = session; this.session = session;
this.persister = session.getFactory().getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor( entityName ); this.persister = session.getFactory().getMappingMetamodel().getEntityDescriptor( entityName );
this.instance = session.getPersistenceContext().getEntity( session.generateEntityKey( id, persister ) ); this.instance = session.getPersistenceContext().getEntity( session.generateEntityKey( id, persister ) );
} }
} }

View File

@ -61,7 +61,7 @@ public class EntityDeleteAction extends EntityAction {
this.isCascadeDeleteEnabled = isCascadeDeleteEnabled; this.isCascadeDeleteEnabled = isCascadeDeleteEnabled;
this.state = state; this.state = state;
NaturalIdMapping naturalIdMapping = persister.getNaturalIdMapping(); final NaturalIdMapping naturalIdMapping = persister.getNaturalIdMapping();
if ( naturalIdMapping != null ) { if ( naturalIdMapping != null ) {
naturalIdValues = session.getPersistenceContextInternal().getNaturalIdResolutions() naturalIdValues = session.getPersistenceContextInternal().getNaturalIdResolutions()
.removeLocalResolution( .removeLocalResolution(
@ -79,10 +79,7 @@ public class EntityDeleteAction extends EntityAction {
* @param persister The entity persister * @param persister The entity persister
* @param session The session * @param session The session
*/ */
public EntityDeleteAction( public EntityDeleteAction(final Object id, final EntityPersister persister, final SessionImplementor session) {
final Object id,
final EntityPersister persister,
final SessionImplementor session) {
super( session, id, null, persister ); super( session, id, null, persister );
version = null; version = null;
isCascadeDeleteEnabled = false; isCascadeDeleteEnabled = false;
@ -128,15 +125,7 @@ public class EntityDeleteAction extends EntityAction {
final boolean veto = isInstanceLoaded() && preDelete(); final boolean veto = isInstanceLoaded() && preDelete();
final Object ck; final Object ck = lockCacheItem();
if ( persister.canWriteToCache() ) {
final EntityDataAccess cache = persister.getCacheAccessStrategy();
ck = cache.generateCacheKey( id, persister, session.getFactory(), session.getTenantIdentifier() );
lock = cache.lockItem( session, ck, version );
}
else {
ck = null;
}
if ( !isCascadeDeleteEnabled && !veto ) { if ( !isCascadeDeleteEnabled && !veto ) {
persister.delete( id, version, instance, session ); persister.delete( id, version, instance, session );
@ -182,17 +171,11 @@ public class EntityDeleteAction extends EntityAction {
throw new AssertionFailure( "possible non-threadsafe access to session" ); throw new AssertionFailure( "possible non-threadsafe access to session" );
} }
entry.postDelete(); entry.postDelete();
EntityKey key = entry.getEntityKey(); EntityKey key = entry.getEntityKey();
persistenceContext.removeEntity( key ); persistenceContext.removeEntity( key );
persistenceContext.removeProxy( key ); persistenceContext.removeProxy( key );
removeCacheItem( ck );
if ( persister.canWriteToCache() ) {
persister.getCacheAccessStrategy().remove( session, ck );
}
persistenceContext.getNaturalIdResolutions().removeSharedResolution( id, naturalIdValues, persister ); persistenceContext.getNaturalIdResolutions().removeSharedResolution( id, naturalIdValues, persister );
postDelete(); postDelete();
} }
@ -203,27 +186,27 @@ public class EntityDeleteAction extends EntityAction {
throw new AssertionFailure( "deleted proxy should be for an unloaded entity: " + key ); throw new AssertionFailure( "deleted proxy should be for an unloaded entity: " + key );
} }
persistenceContext.removeProxy( key ); persistenceContext.removeProxy( key );
if ( persister.canWriteToCache() ) { removeCacheItem( ck );
persister.getCacheAccessStrategy().remove( session, ck );
}
} }
protected boolean preDelete() { protected boolean preDelete() {
final EventListenerGroup<PreDeleteEventListener> listenerGroup = getFastSessionServices().eventListenerGroup_PRE_DELETE; final EventListenerGroup<PreDeleteEventListener> listenerGroup
= getFastSessionServices().eventListenerGroup_PRE_DELETE;
if ( listenerGroup.isEmpty() ) { if ( listenerGroup.isEmpty() ) {
return false; return false;
} }
final PreDeleteEvent event = new PreDeleteEvent( getInstance(), getId(), state, getPersister(), eventSource() ); else {
boolean veto = false; final PreDeleteEvent event = new PreDeleteEvent( getInstance(), getId(), state, getPersister(), eventSource() );
for ( PreDeleteEventListener listener : listenerGroup.listeners() ) { boolean veto = false;
veto |= listener.onPreDelete( event ); for ( PreDeleteEventListener listener : listenerGroup.listeners() ) {
veto |= listener.onPreDelete( event );
}
return veto;
} }
return veto;
} }
protected void postDelete() { protected void postDelete() {
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_DELETE
.eventListenerGroup_POST_DELETE
.fireLazyEventOnEachListener( this::newPostDeleteEvent, PostDeleteEventListener::onPostDelete ); .fireLazyEventOnEachListener( this::newPostDeleteEvent, PostDeleteEventListener::onPostDelete );
} }
@ -238,8 +221,8 @@ public class EntityDeleteAction extends EntityAction {
} }
protected void postCommitDelete(boolean success) { protected void postCommitDelete(boolean success) {
final EventListenerGroup<PostDeleteEventListener> eventListeners = getFastSessionServices() final EventListenerGroup<PostDeleteEventListener> eventListeners
.eventListenerGroup_POST_COMMIT_DELETE; = getFastSessionServices().eventListenerGroup_POST_COMMIT_DELETE;
if (success) { if (success) {
eventListeners.fireLazyEventOnEachListener( this::newPostDeleteEvent, PostDeleteEventListener::onPostDelete ); eventListeners.fireLazyEventOnEachListener( this::newPostDeleteEvent, PostDeleteEventListener::onPostDelete );
} }
@ -260,29 +243,53 @@ public class EntityDeleteAction extends EntityAction {
@Override @Override
public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws HibernateException { public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws HibernateException {
EntityPersister entityPersister = getPersister(); unlockCacheItem();
if ( entityPersister.canWriteToCache() ) {
EntityDataAccess cache = entityPersister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey(
getId(),
entityPersister,
session.getFactory(),
session.getTenantIdentifier()
);
cache.unlockItem( session, ck, lock );
}
postCommitDelete( success ); postCommitDelete( success );
} }
@Override @Override
protected boolean hasPostCommitEventListeners() { protected boolean hasPostCommitEventListeners() {
final EventListenerGroup<PostDeleteEventListener> group = getFastSessionServices().eventListenerGroup_POST_COMMIT_DELETE; for ( PostDeleteEventListener listener: getFastSessionServices().eventListenerGroup_POST_COMMIT_DELETE.listeners() ) {
for ( PostDeleteEventListener listener : group.listeners() ) {
if ( listener.requiresPostCommitHandling( getPersister() ) ) { if ( listener.requiresPostCommitHandling( getPersister() ) ) {
return true; return true;
} }
} }
return false; return false;
} }
private Object lockCacheItem() {
final EntityPersister persister = getPersister();
if ( persister.canWriteToCache() ) {
final EntityDataAccess cache = persister.getCacheAccessStrategy();
final SharedSessionContractImplementor session = getSession();
Object ck = cache.generateCacheKey( getId(), persister, session.getFactory(), session.getTenantIdentifier() );
lock = cache.lockItem( session, ck, getCurrentVersion() );
return ck;
}
else {
return null;
}
}
private void unlockCacheItem() {
final EntityPersister persister = getPersister();
if ( persister.canWriteToCache() ) {
final EntityDataAccess cache = persister.getCacheAccessStrategy();
final SharedSessionContractImplementor session = getSession();
final Object ck = cache.generateCacheKey(
getId(),
persister,
session.getFactory(),
session.getTenantIdentifier()
);
cache.unlockItem( session, ck, lock );
}
}
private void removeCacheItem(Object ck) {
final EntityPersister persister = getPersister();
if ( persister.canWriteToCache() ) {
persister.getCacheAccessStrategy().remove( getSession(), ck );
}
}
} }

View File

@ -45,12 +45,12 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
* @throws HibernateException Indicates an illegal state * @throws HibernateException Indicates an illegal state
*/ */
public EntityIdentityInsertAction( public EntityIdentityInsertAction(
Object[] state, final Object[] state,
Object instance, final Object instance,
EntityPersister persister, final EntityPersister persister,
boolean isVersionIncrementDisabled, final boolean isVersionIncrementDisabled,
SharedSessionContractImplementor session, final SharedSessionContractImplementor session,
boolean isDelayed) { final boolean isDelayed) {
super( super(
isDelayed ? generateDelayedPostInsertIdentifier() : null, isDelayed ? generateDelayedPostInsertIdentifier() : null,
state, state,
@ -90,7 +90,6 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
persistenceContext.checkUniqueness( entityKey, getInstance() ); persistenceContext.checkUniqueness( entityKey, getInstance() );
} }
//TODO: this bit actually has to be called after all cascades! //TODO: this bit actually has to be called after all cascades!
// but since identity insert is called *synchronously*, // but since identity insert is called *synchronously*,
// instead of asynchronously as other actions, it isn't // instead of asynchronously as other actions, it isn't
@ -117,13 +116,13 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
@Override @Override
protected boolean hasPostCommitEventListeners() { protected boolean hasPostCommitEventListeners() {
final EventListenerGroup<PostInsertEventListener> group = getFastSessionServices().eventListenerGroup_POST_COMMIT_INSERT; final EventListenerGroup<PostInsertEventListener> group
= getFastSessionServices().eventListenerGroup_POST_COMMIT_INSERT;
for ( PostInsertEventListener listener : group.listeners() ) { for ( PostInsertEventListener listener : group.listeners() ) {
if ( listener.requiresPostCommitHandling( getPersister() ) ) { if ( listener.requiresPostCommitHandling( getPersister() ) ) {
return true; return true;
} }
} }
return false; return false;
} }
@ -139,10 +138,10 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
protected void postInsert() { protected void postInsert() {
if ( isDelayed ) { if ( isDelayed ) {
eventSource().getPersistenceContextInternal().replaceDelayedEntityIdentityInsertKeys( delayedEntityKey, generatedId ); eventSource().getPersistenceContextInternal()
.replaceDelayedEntityIdentityInsertKeys( delayedEntityKey, generatedId );
} }
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_INSERT
.eventListenerGroup_POST_INSERT
.fireLazyEventOnEachListener( this::newPostInsertEvent, PostInsertEventListener::onPostInsert ); .fireLazyEventOnEachListener( this::newPostInsertEvent, PostInsertEventListener::onPostInsert );
} }
@ -157,9 +156,9 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
} }
protected void postCommitInsert(boolean success) { protected void postCommitInsert(boolean success) {
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_COMMIT_INSERT
.eventListenerGroup_POST_COMMIT_INSERT .fireLazyEventOnEachListener( this::newPostInsertEvent,
.fireLazyEventOnEachListener( this::newPostInsertEvent, success ? PostInsertEventListener::onPostInsert : this::postCommitInsertOnFailure ); success ? PostInsertEventListener::onPostInsert : this::postCommitInsertOnFailure );
} }
private void postCommitInsertOnFailure(PostInsertEventListener listener, PostInsertEvent event) { private void postCommitInsertOnFailure(PostInsertEventListener listener, PostInsertEvent event) {
@ -173,17 +172,20 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
} }
protected boolean preInsert() { protected boolean preInsert() {
final EventListenerGroup<PreInsertEventListener> listenerGroup = getFastSessionServices().eventListenerGroup_PRE_INSERT; final EventListenerGroup<PreInsertEventListener> listenerGroup
= getFastSessionServices().eventListenerGroup_PRE_INSERT;
if ( listenerGroup.isEmpty() ) { if ( listenerGroup.isEmpty() ) {
// NO_VETO // NO_VETO
return false; return false;
} }
boolean veto = false; else {
final PreInsertEvent event = new PreInsertEvent( getInstance(), null, getState(), getPersister(), eventSource() ); final PreInsertEvent event = new PreInsertEvent( getInstance(), null, getState(), getPersister(), eventSource() );
for ( PreInsertEventListener listener : listenerGroup.listeners() ) { boolean veto = false;
veto |= listener.onPreInsert( event ); for ( PreInsertEventListener listener : listenerGroup.listeners() ) {
veto |= listener.onPreInsert( event );
}
return veto;
} }
return veto;
} }
/** /**
@ -218,9 +220,11 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
} }
protected EntityKey generateDelayedEntityKey() { protected EntityKey generateDelayedEntityKey() {
if ( !isDelayed ) { if ( isDelayed ) {
return getSession().generateEntityKey( getDelayedId(), getPersister() );
}
else {
throw new AssertionFailure( "cannot request delayed entity-key for early-insert post-insert-id generation" ); throw new AssertionFailure( "cannot request delayed entity-key for early-insert post-insert-id generation" );
} }
return getSession().generateEntityKey( getDelayedId(), getPersister() );
} }
} }

View File

@ -33,6 +33,7 @@ import org.hibernate.stat.spi.StatisticsImplementor;
* @see EntityIdentityInsertAction * @see EntityIdentityInsertAction
*/ */
public class EntityInsertAction extends AbstractEntityInsertAction { public class EntityInsertAction extends AbstractEntityInsertAction {
private Object version; private Object version;
private Object cacheEntry; private Object cacheEntry;
@ -47,13 +48,13 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
* @param session The session * @param session The session
*/ */
public EntityInsertAction( public EntityInsertAction(
Object id, final Object id,
Object[] state, final Object[] state,
Object instance, final Object instance,
Object version, final Object version,
EntityPersister persister, final EntityPersister persister,
boolean isVersionIncrementDisabled, final boolean isVersionIncrementDisabled,
SharedSessionContractImplementor session) { final SharedSessionContractImplementor session) {
super( id, state, instance, isVersionIncrementDisabled, persister, session ); super( id, state, instance, isVersionIncrementDisabled, persister, session );
this.version = version; this.version = version;
} }
@ -88,67 +89,31 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
public void execute() throws HibernateException { public void execute() throws HibernateException {
nullifyTransientReferencesIfNotAlready(); nullifyTransientReferencesIfNotAlready();
final EntityPersister persister = getPersister();
final SharedSessionContractImplementor session = getSession();
final Object instance = getInstance();
final Object id = getId();
final boolean veto = preInsert();
// Don't need to lock the cache here, since if someone // Don't need to lock the cache here, since if someone
// else inserted the same pk first, the insert would fail // else inserted the same pk first, the insert would fail
final SharedSessionContractImplementor session = getSession();
final Object id = getId();
final boolean veto = preInsert();
if ( !veto ) { if ( !veto ) {
final EntityPersister persister = getPersister();
final Object instance = getInstance();
persister.insert( id, getState(), instance, session ); persister.insert( id, getState(), instance, session );
PersistenceContext persistenceContext = session.getPersistenceContextInternal(); PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityEntry entry = persistenceContext.getEntry( instance ); final EntityEntry entry = persistenceContext.getEntry( instance );
if ( entry == null ) { if ( entry == null ) {
throw new AssertionFailure( "possible non-threadsafe access to session" ); throw new AssertionFailure( "possible non-threadsafe access to session" );
} }
entry.postInsert( getState() ); entry.postInsert( getState() );
handleGeneratedProperties( entry );
if ( persister.hasInsertGeneratedProperties() ) {
persister.processInsertGeneratedProperties( id, instance, getState(), session );
if ( persister.isVersionPropertyGenerated() ) {
version = Versioning.getVersion( getState(), persister );
}
entry.postUpdate( instance, getState(), version );
}
persistenceContext.registerInsertedKey( persister, getId() ); persistenceContext.registerInsertedKey( persister, getId() );
addCollectionsByKeyToPersistenceContext( persistenceContext, getState() ); addCollectionsByKeyToPersistenceContext( persistenceContext, getState() );
} }
putCacheIfNecessary();
final SessionFactoryImplementor factory = session.getFactory();
final StatisticsImplementor statistics = factory.getStatistics();
if ( isCachePutEnabled( persister, session ) ) {
final CacheEntry ce = persister.buildCacheEntry(
instance,
getState(),
version,
session
);
cacheEntry = persister.getCacheEntryStructure().structure( ce );
final EntityDataAccess cache = persister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey( id, persister, factory, session.getTenantIdentifier() );
final boolean put = cacheInsert( persister, ck );
if ( put && statistics.isStatisticsEnabled() ) {
statistics.entityCachePut(
StatsHelper.INSTANCE.getRootEntityRole( persister ),
cache.getRegion().getName()
);
}
}
handleNaturalIdPostSaveNotifications( id ); handleNaturalIdPostSaveNotifications( id );
postInsert(); postInsert();
final StatisticsImplementor statistics = session.getFactory().getStatistics();
if ( statistics.isStatisticsEnabled() && !veto ) { if ( statistics.isStatisticsEnabled() && !veto ) {
statistics.insertEntity( getPersister().getEntityName() ); statistics.insertEntity( getPersister().getEntityName() );
} }
@ -156,11 +121,44 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
markExecuted(); markExecuted();
} }
private void handleGeneratedProperties(EntityEntry entry) {
final EntityPersister persister = getPersister();
if ( persister.hasInsertGeneratedProperties() ) {
final Object instance = getInstance();
persister.processInsertGeneratedProperties( getId(), instance, getState(), getSession() );
if ( persister.isVersionPropertyGenerated() ) {
version = Versioning.getVersion( getState(), persister);
}
entry.postUpdate( instance, getState(), version );
}
}
private void putCacheIfNecessary() {
final EntityPersister persister = getPersister();
final SharedSessionContractImplementor session = getSession();
if ( isCachePutEnabled( persister, session ) ) {
final SessionFactoryImplementor factory = session.getFactory();
final CacheEntry ce = persister.buildCacheEntry( getInstance(), getState(), version, session );
cacheEntry = persister.getCacheEntryStructure().structure( ce );
final EntityDataAccess cache = persister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey( getId(), persister, factory, session.getTenantIdentifier() );
final boolean put = cacheInsert( persister, ck );
final StatisticsImplementor statistics = factory.getStatistics();
if ( put && statistics.isStatisticsEnabled() ) {
statistics.entityCachePut(
StatsHelper.INSTANCE.getRootEntityRole( persister ),
cache.getRegion().getName()
);
}
}
}
protected boolean cacheInsert(EntityPersister persister, Object ck) { protected boolean cacheInsert(EntityPersister persister, Object ck) {
SharedSessionContractImplementor session = getSession(); SharedSessionContractImplementor session = getSession();
try { try {
session.getEventListenerManager().cachePutStart(); session.getEventListenerManager().cachePutStart();
return persister.getCacheAccessStrategy().insert( session, ck, cacheEntry, version); return persister.getCacheAccessStrategy().insert( session, ck, cacheEntry, version );
} }
finally { finally {
session.getEventListenerManager().cachePutEnd(); session.getEventListenerManager().cachePutEnd();
@ -184,9 +182,9 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
} }
protected void postCommitInsert(boolean success) { protected void postCommitInsert(boolean success) {
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_COMMIT_INSERT
.eventListenerGroup_POST_COMMIT_INSERT .fireLazyEventOnEachListener( this::newPostInsertEvent,
.fireLazyEventOnEachListener( this::newPostInsertEvent, success ? PostInsertEventListener::onPostInsert : this::postCommitOnFailure ); success ? PostInsertEventListener::onPostInsert : this::postCommitOnFailure );
} }
private void postCommitOnFailure(PostInsertEventListener listener, PostInsertEvent event) { private void postCommitOnFailure(PostInsertEventListener listener, PostInsertEvent event) {
@ -202,7 +200,8 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
protected boolean preInsert() { protected boolean preInsert() {
boolean veto = false; boolean veto = false;
final EventListenerGroup<PreInsertEventListener> listenerGroup = getFastSessionServices().eventListenerGroup_PRE_INSERT; final EventListenerGroup<PreInsertEventListener> listenerGroup
= getFastSessionServices().eventListenerGroup_PRE_INSERT;
if ( listenerGroup.isEmpty() ) { if ( listenerGroup.isEmpty() ) {
return veto; return veto;
} }
@ -247,7 +246,8 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
@Override @Override
protected boolean hasPostCommitEventListeners() { protected boolean hasPostCommitEventListeners() {
final EventListenerGroup<PostInsertEventListener> group = getFastSessionServices().eventListenerGroup_POST_COMMIT_INSERT; final EventListenerGroup<PostInsertEventListener> group
= getFastSessionServices().eventListenerGroup_POST_COMMIT_INSERT;
for ( PostInsertEventListener listener : group.listeners() ) { for ( PostInsertEventListener listener : group.listeners() ) {
if ( listener.requiresPostCommitHandling( getPersister() ) ) { if ( listener.requiresPostCommitHandling( getPersister() ) ) {
return true; return true;
@ -258,8 +258,8 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
protected boolean isCachePutEnabled(EntityPersister persister, SharedSessionContractImplementor session) { protected boolean isCachePutEnabled(EntityPersister persister, SharedSessionContractImplementor session) {
return persister.canWriteToCache() return persister.canWriteToCache()
&& !persister.isCacheInvalidationRequired() && !persister.isCacheInvalidationRequired()
&& session.getCacheMode().isPutEnabled(); && session.getCacheMode().isPutEnabled();
} }
} }

View File

@ -15,7 +15,6 @@ import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.CachedNaturalIdValueSource; import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionEventListenerManager;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -37,6 +36,7 @@ import org.hibernate.type.TypeHelper;
* The action for performing entity updates. * The action for performing entity updates.
*/ */
public class EntityUpdateAction extends EntityAction { public class EntityUpdateAction extends EntityAction {
private final Object[] state; private final Object[] state;
private final Object[] previousState; private final Object[] previousState;
private final Object previousVersion; private final Object previousVersion;
@ -91,7 +91,7 @@ public class EntityUpdateAction extends EntityAction {
previousNaturalIdValues = null; previousNaturalIdValues = null;
} }
else { else {
this.previousNaturalIdValues = determinePreviousNaturalIdValues( persister, naturalIdMapping, id, previousState, session ); previousNaturalIdValues = determinePreviousNaturalIdValues( persister, naturalIdMapping, id, previousState, session );
session.getPersistenceContextInternal().getNaturalIdResolutions().manageLocalResolution( session.getPersistenceContextInternal().getNaturalIdResolutions().manageLocalResolution(
id, id,
naturalIdMapping.extractNaturalIdFromEntityState( state, session ), naturalIdMapping.extractNaturalIdFromEntityState( state, session ),
@ -107,12 +107,9 @@ public class EntityUpdateAction extends EntityAction {
Object id, Object id,
Object[] previousState, Object[] previousState,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); return previousState == null
if ( previousState != null ) { ? session.getPersistenceContextInternal().getNaturalIdSnapshot( id, persister )
return naturalIdMapping.extractNaturalIdFromEntityState( previousState, session ); : naturalIdMapping.extractNaturalIdFromEntityState( previousState, session );
}
return persistenceContext.getNaturalIdSnapshot( id, persister );
} }
public Object[] getState() { public Object[] getState() {
@ -129,56 +126,73 @@ public class EntityUpdateAction extends EntityAction {
@Override @Override
public void execute() throws HibernateException { public void execute() throws HibernateException {
final Object id = getId(); if ( !preUpdate() ) {
final EntityPersister persister = getPersister();
final SharedSessionContractImplementor session = getSession();
final Object id = getId();
final Object instance = getInstance();
final Object previousVersion = getPreviousVersion();
final Object ck = lockCacheItem( previousVersion );
persister.update( id, state, dirtyFields, hasDirtyCollection, previousState, previousVersion, instance, rowId, session );
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( instance );
if ( entry == null ) {
throw new AssertionFailure( "possible non thread safe access to session" );
}
handleGeneratedProperties( entry );
updateCacheItem( previousVersion, ck, entry );
handleNaturalIdResolutions( persister, session, id );
postUpdate();
final StatisticsImplementor statistics = session.getFactory().getStatistics();
if ( statistics.isStatisticsEnabled() ) {
statistics.updateEntity( getPersister().getEntityName() );
}
}
}
private void handleNaturalIdResolutions(EntityPersister persister, SharedSessionContractImplementor session, Object id) {
if ( naturalIdMapping != null ) {
session.getPersistenceContextInternal().getNaturalIdResolutions().manageSharedResolution(
id,
naturalIdMapping.extractNaturalIdFromEntityState( state, session),
previousNaturalIdValues,
persister,
CachedNaturalIdValueSource.UPDATE
);
}
}
private void updateCacheItem(Object previousVersion, Object ck, EntityEntry entry) {
final EntityPersister persister = getPersister();
if ( persister.canWriteToCache() ) {
final SharedSessionContractImplementor session = getSession();
if ( persister.isCacheInvalidationRequired() || entry.getStatus() != Status.MANAGED ) {
persister.getCacheAccessStrategy().remove( session, ck );
}
else if ( session.getCacheMode().isPutEnabled() ) {
//TODO: inefficient if that cache is just going to ignore the updated state!
final CacheEntry ce = persister.buildCacheEntry( getInstance(), state, nextVersion, getSession() );
cacheEntry = persister.getCacheEntryStructure().structure( ce );
final boolean put = updateCache( persister, previousVersion, ck );
final StatisticsImplementor statistics = session.getFactory().getStatistics();
if ( put && statistics.isStatisticsEnabled() ) {
statistics.entityCachePut(
StatsHelper.INSTANCE.getRootEntityRole( persister ),
getPersister().getCacheAccessStrategy().getRegion().getName()
);
}
}
}
}
private void handleGeneratedProperties(EntityEntry entry) {
final EntityPersister persister = getPersister(); final EntityPersister persister = getPersister();
final SharedSessionContractImplementor session = getSession();
final Object instance = getInstance(); final Object instance = getInstance();
if ( preUpdate() ) {
return;
}
final SessionFactoryImplementor factory = session.getFactory();
Object previousVersion = this.previousVersion;
if ( persister.isVersionPropertyGenerated() ) {
// we need to grab the version value from the entity, otherwise
// we have issues with generated-version entities that may have
// multiple actions queued during the same flush
previousVersion = persister.getVersion( instance );
}
final Object ck;
if ( persister.canWriteToCache() ) {
final EntityDataAccess cache = persister.getCacheAccessStrategy();
ck = cache.generateCacheKey(
id,
persister,
factory,
session.getTenantIdentifier()
);
lock = cache.lockItem( session, ck, previousVersion );
}
else {
ck = null;
}
persister.update(
id,
state,
dirtyFields,
hasDirtyCollection,
previousState,
previousVersion,
instance,
rowId,
session
);
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( instance );
if ( entry == null ) {
throw new AssertionFailure( "possible non thread safe access to session" );
}
if ( entry.getStatus() == Status.MANAGED || persister.isVersionPropertyGenerated() ) { if ( entry.getStatus() == Status.MANAGED || persister.isVersionPropertyGenerated() ) {
final SharedSessionContractImplementor session = getSession();
final Object id = getId();
// 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
@ -194,7 +208,7 @@ public class EntityUpdateAction extends EntityAction {
// values... // values...
persister.processUpdateGeneratedProperties( id, instance, state, session ); persister.processUpdateGeneratedProperties( id, instance, state, session );
if ( persister.isVersionPropertyGenerated() ) { if ( persister.isVersionPropertyGenerated() ) {
nextVersion = Versioning.getVersion( state, persister ); nextVersion = Versioning.getVersion( state, persister);
} }
} }
// have the entity entry doAfterTransactionCompletion post-update processing, passing it the // have the entity entry doAfterTransactionCompletion post-update processing, passing it the
@ -207,52 +221,47 @@ public class EntityUpdateAction extends EntityAction {
final boolean isImpliedOptimisticLocking = !entityMetamodel.isVersioned() final boolean isImpliedOptimisticLocking = !entityMetamodel.isVersioned()
&& entityMetamodel.getOptimisticLockStyle().isAllOrDirty(); && entityMetamodel.getOptimisticLockStyle().isAllOrDirty();
if ( isImpliedOptimisticLocking && entry.getLoadedState() != null ) { if ( isImpliedOptimisticLocking && entry.getLoadedState() != null ) {
// The entity will be deleted and because we are going to create a delete statement that uses // The entity will be deleted and because we are going to create a delete statement
// all the state values in the where clause, the entry state needs to be updated otherwise the statement execution will // that uses all the state values in the where clause, the entry state needs to be
// not delete any row (see HHH-15218). // updated otherwise the statement execution will not delete any row (see HHH-15218).
entry.postUpdate( instance, state, nextVersion ); entry.postUpdate( instance, state, nextVersion );
} }
} }
final StatisticsImplementor statistics = factory.getStatistics();
if ( persister.canWriteToCache() ) {
if ( persister.isCacheInvalidationRequired() || entry.getStatus() != Status.MANAGED ) {
persister.getCacheAccessStrategy().remove( session, ck );
}
else if ( session.getCacheMode().isPutEnabled() ) {
//TODO: inefficient if that cache is just going to ignore the updated state!
final CacheEntry ce = persister.buildCacheEntry( instance, state, nextVersion, getSession() );
cacheEntry = persister.getCacheEntryStructure().structure( ce );
final boolean put = cacheUpdate( persister, previousVersion, ck );
if ( put && statistics.isStatisticsEnabled() ) {
statistics.entityCachePut(
StatsHelper.INSTANCE.getRootEntityRole( persister ),
getPersister().getCacheAccessStrategy().getRegion().getName()
);
}
}
}
if ( naturalIdMapping != null ) {
session.getPersistenceContextInternal().getNaturalIdResolutions().manageSharedResolution(
id,
naturalIdMapping.extractNaturalIdFromEntityState( state, session ),
previousNaturalIdValues,
persister,
CachedNaturalIdValueSource.UPDATE
);
}
postUpdate();
if ( statistics.isStatisticsEnabled() ) {
statistics.updateEntity( getPersister().getEntityName() );
}
} }
protected boolean cacheUpdate(EntityPersister persister, Object previousVersion, Object ck) { private Object getPreviousVersion() {
final EntityPersister persister = getPersister();
if ( persister.isVersionPropertyGenerated() ) {
// we need to grab the version value from the entity, otherwise
// we have issues with generated-version entities that may have
// multiple actions queued during the same flush
return persister.getVersion( getInstance() );
}
else {
return previousVersion;
}
}
private Object lockCacheItem(Object previousVersion) {
final EntityPersister persister = getPersister();
if ( persister.canWriteToCache() ) {
final SharedSessionContractImplementor session = getSession();
final EntityDataAccess cache = persister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey(
getId(),
persister,
session.getFactory(),
session.getTenantIdentifier()
);
lock = cache.lockItem( session, ck, previousVersion );
return ck;
}
else {
return null;
}
}
protected boolean updateCache(EntityPersister persister, Object previousVersion, Object ck) {
final SharedSessionContractImplementor session = getSession(); final SharedSessionContractImplementor session = getSession();
try { try {
session.getEventListenerManager().cachePutStart(); session.getEventListenerManager().cachePutStart();
@ -264,28 +273,30 @@ public class EntityUpdateAction extends EntityAction {
} }
protected boolean preUpdate() { protected boolean preUpdate() {
boolean veto = false; final EventListenerGroup<PreUpdateEventListener> listenerGroup
final EventListenerGroup<PreUpdateEventListener> listenerGroup = getFastSessionServices().eventListenerGroup_PRE_UPDATE; = getFastSessionServices().eventListenerGroup_PRE_UPDATE;
if ( listenerGroup.isEmpty() ) { if ( listenerGroup.isEmpty() ) {
return false;
}
else {
final PreUpdateEvent event = new PreUpdateEvent(
getInstance(),
getId(),
state,
previousState,
getPersister(),
eventSource()
);
boolean veto = false;
for ( PreUpdateEventListener listener : listenerGroup.listeners() ) {
veto |= listener.onPreUpdate( event );
}
return veto; return veto;
} }
final PreUpdateEvent event = new PreUpdateEvent(
getInstance(),
getId(),
state,
previousState,
getPersister(),
eventSource()
);
for ( PreUpdateEventListener listener : listenerGroup.listeners() ) {
veto |= listener.onPreUpdate( event );
}
return veto;
} }
protected void postUpdate() { protected void postUpdate() {
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_UPDATE
.eventListenerGroup_POST_UPDATE
.fireLazyEventOnEachListener( this::newPostUpdateEvent, PostUpdateEventListener::onPostUpdate ); .fireLazyEventOnEachListener( this::newPostUpdateEvent, PostUpdateEventListener::onPostUpdate );
} }
@ -302,9 +313,9 @@ public class EntityUpdateAction extends EntityAction {
} }
protected void postCommitUpdate(boolean success) { protected void postCommitUpdate(boolean success) {
getFastSessionServices() getFastSessionServices().eventListenerGroup_POST_COMMIT_UPDATE
.eventListenerGroup_POST_COMMIT_UPDATE .fireLazyEventOnEachListener( this::newPostUpdateEvent,
.fireLazyEventOnEachListener( this::newPostUpdateEvent, success ? PostUpdateEventListener::onPostUpdate : this::onPostCommitFailure ); success ? PostUpdateEventListener::onPostUpdate : this::onPostCommitFailure );
} }
private void onPostCommitFailure(PostUpdateEventListener listener, PostUpdateEvent event) { private void onPostCommitFailure(PostUpdateEventListener listener, PostUpdateEvent event) {
@ -319,18 +330,23 @@ public class EntityUpdateAction extends EntityAction {
@Override @Override
protected boolean hasPostCommitEventListeners() { protected boolean hasPostCommitEventListeners() {
final EventListenerGroup<PostUpdateEventListener> group = getFastSessionServices().eventListenerGroup_POST_COMMIT_UPDATE; final EventListenerGroup<PostUpdateEventListener> group
= getFastSessionServices().eventListenerGroup_POST_COMMIT_UPDATE;
for ( PostUpdateEventListener listener : group.listeners() ) { for ( PostUpdateEventListener listener : group.listeners() ) {
if ( listener.requiresPostCommitHandling( getPersister() ) ) { if ( listener.requiresPostCommitHandling( getPersister() ) ) {
return true; return true;
} }
} }
return false; return false;
} }
@Override @Override
public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws CacheException { public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws CacheException {
updateCacheIfNecessary( success, session );
postCommitUpdate( success );
}
private void updateCacheIfNecessary(boolean success, SharedSessionContractImplementor session) {
final EntityPersister persister = getPersister(); final EntityPersister persister = getPersister();
if ( persister.canWriteToCache() ) { if ( persister.canWriteToCache() ) {
final EntityDataAccess cache = persister.getCacheAccessStrategy(); final EntityDataAccess cache = persister.getCacheAccessStrategy();
@ -342,38 +358,40 @@ public class EntityUpdateAction extends EntityAction {
session.getTenantIdentifier() session.getTenantIdentifier()
); );
if ( cacheUpdateRequired( success, persister, session ) ) {
if ( success && cacheAfterUpdate( cache, ck, session);
cacheEntry != null &&
!persister.isCacheInvalidationRequired() &&
session.getCacheMode().isPutEnabled() ) {
final boolean put = cacheAfterUpdate( cache, ck );
final StatisticsImplementor statistics = factory.getStatistics();
if ( put && statistics.isStatisticsEnabled() ) {
statistics.entityCachePut(
StatsHelper.INSTANCE.getRootEntityRole( persister ),
cache.getRegion().getName()
);
}
} }
else { else {
cache.unlockItem( session, ck, lock ); cache.unlockItem( session, ck, lock );
} }
} }
postCommitUpdate( success );
} }
protected boolean cacheAfterUpdate(EntityDataAccess cache, Object ck) { private boolean cacheUpdateRequired(boolean success, EntityPersister persister, SharedSessionContractImplementor session) {
final SharedSessionContractImplementor session = getSession(); return success
SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); && cacheEntry != null
&& !persister.isCacheInvalidationRequired()
&& session.getCacheMode().isPutEnabled();
}
protected void cacheAfterUpdate(EntityDataAccess cache, Object ck, SharedSessionContractImplementor session) {
final SessionEventListenerManager eventListenerManager = session.getEventListenerManager();
try { try {
eventListenerManager.cachePutStart(); eventListenerManager.cachePutStart();
return cache.afterUpdate( session, ck, cacheEntry, nextVersion, previousVersion, lock ); boolean put = cache.afterUpdate( session, ck, cacheEntry, nextVersion, previousVersion, lock );
final StatisticsImplementor statistics = session.getFactory().getStatistics();
if ( put && statistics.isStatisticsEnabled() ) {
statistics.entityCachePut(
StatsHelper.INSTANCE.getRootEntityRole( getPersister() ),
cache.getRegion().getName()
);
}
} }
finally { finally {
eventListenerManager.cachePutEnd(); eventListenerManager.cachePutEnd();
} }
} }
} }

View File

@ -20,6 +20,7 @@ import org.hibernate.pretty.MessageHelper;
* @author Scott Marlow * @author Scott Marlow
*/ */
public class EntityVerifyVersionProcess implements BeforeTransactionCompletionProcess { public class EntityVerifyVersionProcess implements BeforeTransactionCompletionProcess {
private final Object object; private final Object object;
/** /**
@ -35,19 +36,18 @@ public class EntityVerifyVersionProcess implements BeforeTransactionCompletionPr
public void doBeforeTransactionCompletion(SessionImplementor session) { public void doBeforeTransactionCompletion(SessionImplementor session) {
final EntityEntry entry = session.getPersistenceContext().getEntry( object ); final EntityEntry entry = session.getPersistenceContext().getEntry( object );
// Don't check version for an entity that is not in the PersistenceContext; // Don't check version for an entity that is not in the PersistenceContext;
if ( entry == null ) { if ( entry != null ) {
return; final Object latestVersion = entry.getPersister().getCurrentVersion( entry.getId(), session );
} if ( !entry.getVersion().equals( latestVersion ) ) {
throw new OptimisticEntityLockException(
final EntityPersister persister = entry.getPersister(); object,
final Object latestVersion = persister.getCurrentVersion( entry.getId(), session ); "Newer version ["
if ( !entry.getVersion().equals( latestVersion ) ) { + latestVersion
throw new OptimisticEntityLockException( + "] of entity ["
object, + MessageHelper.infoString( entry.getEntityName(), entry.getId() )
"Newer version [" + latestVersion + + "] found in database"
"] of entity [" + MessageHelper.infoString( entry.getEntityName(), entry.getId() ) + );
"] found in database" }
);
} }
} }
} }

View File

@ -160,7 +160,6 @@ public class UnresolvedEntityInsertActions {
return dependenciesByAction.isEmpty(); return dependenciesByAction.isEmpty();
} }
@SuppressWarnings("unchecked")
private void addDependenciesByTransientEntity(AbstractEntityInsertAction insert, NonNullableTransientDependencies dependencies) { private void addDependenciesByTransientEntity(AbstractEntityInsertAction insert, NonNullableTransientDependencies dependencies) {
for ( Object transientEntity : dependencies.getNonNullableTransientEntities() ) { for ( Object transientEntity : dependencies.getNonNullableTransientEntities() ) {
Set<AbstractEntityInsertAction> dependentActions = dependentActionsByTransientEntity.get( transientEntity ); Set<AbstractEntityInsertAction> dependentActions = dependentActionsByTransientEntity.get( transientEntity );
@ -182,7 +181,6 @@ public class UnresolvedEntityInsertActions {
* *
* @throws IllegalArgumentException if {@code managedEntity} did not have managed or read-only status. * @throws IllegalArgumentException if {@code managedEntity} did not have managed or read-only status.
*/ */
@SuppressWarnings("unchecked")
public Set<AbstractEntityInsertAction> resolveDependentActions(Object managedEntity, SessionImplementor session) { public Set<AbstractEntityInsertAction> resolveDependentActions(Object managedEntity, SessionImplementor session) {
final EntityEntry entityEntry = session.getPersistenceContextInternal().getEntry( managedEntity ); final EntityEntry entityEntry = session.getPersistenceContextInternal().getEntry( managedEntity );
if ( entityEntry.getStatus() != Status.MANAGED && entityEntry.getStatus() != Status.READ_ONLY ) { if ( entityEntry.getStatus() != Status.MANAGED && entityEntry.getStatus() != Status.READ_ONLY ) {