Skip to content

Commit 4596a5c

Browse files
committed
HHH-9127 Invoked cache update and afterUpdate for forceIncrement
1 parent 52ba0ce commit 4596a5c

File tree

6 files changed

+199
-15
lines changed

6 files changed

+199
-15
lines changed

hibernate-core/src/main/java/org/hibernate/action/internal/EntityIncrementVersionProcess.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
88
import org.hibernate.engine.spi.EntityEntry;
99
import org.hibernate.engine.spi.SessionImplementor;
10-
import org.hibernate.persister.entity.EntityPersister;
10+
import org.hibernate.internal.OptimisticLockHelper;
1111

1212
/**
1313
* A {@link BeforeTransactionCompletionProcess} implementation to verify and
@@ -41,8 +41,6 @@ public void doBeforeTransactionCompletion(SessionImplementor session) {
4141
return;
4242
}
4343

44-
final EntityPersister persister = entry.getPersister();
45-
final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );
46-
entry.forceLocked( object, nextVersion );
44+
OptimisticLockHelper.forceVersionIncrement( object, entry, session.asEventSource() );
4745
}
4846
}

hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticForceIncrementLockingStrategy.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.hibernate.LockMode;
99
import org.hibernate.engine.spi.EntityEntry;
1010
import org.hibernate.event.spi.EventSource;
11+
import org.hibernate.internal.OptimisticLockHelper;
1112
import org.hibernate.persister.entity.EntityPersister;
1213

1314
/**
@@ -44,9 +45,7 @@ public void lock(Object id, Object version, Object object, int timeout, EventSou
4445
throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
4546
}
4647
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( object );
47-
final EntityPersister persister = entry.getPersister();
48-
final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), false, session );
49-
entry.forceLocked( object, nextVersion );
48+
OptimisticLockHelper.forceVersionIncrement( object, entry, session );
5049
}
5150

5251
/**

hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,10 @@ public void beforeTransactionCompletion() {
552552
// Execute completion actions only in transaction owner (aka parent session).
553553
if ( beforeTransactionProcesses != null ) {
554554
beforeTransactionProcesses.beforeTransactionCompletion();
555-
// `beforeTransactionCompletion()` can have added batch operations (e.g. to increment entity version)
556-
session.getJdbcCoordinator().executeBatch();
557555
}
556+
// Make sure to always execute pending batches before the transaction completes.
557+
// One such pending batch could be the pessimistic version increment for an entity
558+
session.getJdbcCoordinator().executeBatch();
558559
}
559560
}
560561

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.hibernate.event.spi.EventSource;
1515
import org.hibernate.event.spi.PostLoadEvent;
1616
import org.hibernate.event.spi.PostLoadEventListener;
17+
import org.hibernate.internal.OptimisticLockHelper;
1718
import org.hibernate.jpa.event.spi.CallbackRegistry;
1819
import org.hibernate.jpa.event.spi.CallbackRegistryConsumer;
1920
import org.hibernate.persister.entity.EntityPersister;
@@ -50,9 +51,7 @@ public void onPostLoad(PostLoadEvent event) {
5051
if ( persister.isVersioned() ) {
5152
switch ( lockMode ) {
5253
case PESSIMISTIC_FORCE_INCREMENT:
53-
final Object nextVersion =
54-
persister.forceVersionIncrement( entry.getId(), entry.getVersion(), false, session );
55-
entry.forceLocked( entity, nextVersion );
54+
OptimisticLockHelper.forceVersionIncrement( entity, entry, session );
5655
break;
5756
case OPTIMISTIC_FORCE_INCREMENT:
5857
session.getActionQueue().registerProcess( new EntityIncrementVersionProcess( entity ) );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.internal;
6+
7+
import org.hibernate.CacheMode;
8+
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
9+
import org.hibernate.cache.spi.access.EntityDataAccess;
10+
import org.hibernate.cache.spi.access.SoftLock;
11+
import org.hibernate.cache.spi.entry.CacheEntry;
12+
import org.hibernate.engine.spi.EntityEntry;
13+
import org.hibernate.engine.spi.SessionEventListenerManager;
14+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
15+
import org.hibernate.engine.spi.Status;
16+
import org.hibernate.event.monitor.spi.DiagnosticEvent;
17+
import org.hibernate.event.monitor.spi.EventMonitor;
18+
import org.hibernate.event.spi.EventSource;
19+
import org.hibernate.persister.entity.EntityPersister;
20+
import org.hibernate.stat.internal.StatsHelper;
21+
import org.hibernate.stat.spi.StatisticsImplementor;
22+
23+
public final class OptimisticLockHelper {
24+
25+
private OptimisticLockHelper() {
26+
//utility class, not to be constructed
27+
}
28+
29+
public static void forceVersionIncrement(Object object, EntityEntry entry, EventSource session) {
30+
final EntityPersister persister = entry.getPersister();
31+
final Object previousVersion = entry.getVersion();
32+
SoftLock lock = null;
33+
final Object cacheKey;
34+
if ( persister.canWriteToCache() ) {
35+
final EntityDataAccess cache = persister.getCacheAccessStrategy();
36+
cacheKey = cache.generateCacheKey(
37+
entry.getId(),
38+
persister,
39+
session.getFactory(),
40+
session.getTenantIdentifier()
41+
);
42+
lock = cache.lockItem( session, cacheKey, previousVersion );
43+
}
44+
else {
45+
cacheKey = null;
46+
}
47+
final Object nextVersion = persister.forceVersionIncrement( entry.getId(), previousVersion, session );
48+
entry.forceLocked( object, nextVersion );
49+
if ( persister.canWriteToCache() ) {
50+
final Object cacheEntry = updateCacheItem(
51+
object,
52+
previousVersion,
53+
nextVersion,
54+
cacheKey,
55+
entry,
56+
persister,
57+
session
58+
);
59+
session.getActionQueue().registerProcess( new CacheCleanupProcess(
60+
cacheKey,
61+
persister,
62+
previousVersion,
63+
nextVersion,
64+
lock,
65+
cacheEntry
66+
) );
67+
}
68+
}
69+
70+
private static Object updateCacheItem(Object entity, Object previousVersion, Object nextVersion, Object ck, EntityEntry entry, EntityPersister persister, EventSource session) {
71+
if ( isCacheInvalidationRequired( persister, session ) || entry.getStatus() != Status.MANAGED ) {
72+
persister.getCacheAccessStrategy().remove( session, ck );
73+
}
74+
else if ( session.getCacheMode().isPutEnabled() ) {
75+
//TODO: inefficient if that cache is just going to ignore the updated state!
76+
final CacheEntry ce = persister.buildCacheEntry( entity, entry.getLoadedState(), nextVersion, session );
77+
final Object cacheEntry = persister.getCacheEntryStructure().structure( ce );
78+
final boolean put = updateCache( persister, cacheEntry, previousVersion, nextVersion, ck, session );
79+
80+
final StatisticsImplementor statistics = session.getFactory().getStatistics();
81+
if ( put && statistics.isStatisticsEnabled() ) {
82+
statistics.entityCachePut(
83+
StatsHelper.getRootEntityRole( persister ),
84+
persister.getCacheAccessStrategy().getRegion().getName()
85+
);
86+
}
87+
return cacheEntry;
88+
}
89+
return null;
90+
}
91+
92+
private static boolean updateCache(EntityPersister persister, Object cacheEntry, Object previousVersion, Object nextVersion, Object ck, EventSource session) {
93+
final EventMonitor eventMonitor = session.getEventMonitor();
94+
final DiagnosticEvent cachePutEvent = eventMonitor.beginCachePutEvent();
95+
final EntityDataAccess cacheAccessStrategy = persister.getCacheAccessStrategy();
96+
boolean update = false;
97+
try {
98+
session.getEventListenerManager().cachePutStart();
99+
update = cacheAccessStrategy.update( session, ck, cacheEntry, nextVersion, previousVersion );
100+
return update;
101+
}
102+
finally {
103+
eventMonitor.completeCachePutEvent(
104+
cachePutEvent,
105+
session,
106+
cacheAccessStrategy,
107+
persister,
108+
update,
109+
EventMonitor.CacheActionDescription.ENTITY_UPDATE
110+
);
111+
session.getEventListenerManager().cachePutEnd();
112+
}
113+
}
114+
115+
private static boolean isCacheInvalidationRequired(
116+
EntityPersister persister,
117+
SharedSessionContractImplementor session) {
118+
// the cache has to be invalidated when CacheMode is equal to GET or IGNORE
119+
return persister.isCacheInvalidationRequired()
120+
|| session.getCacheMode() == CacheMode.GET
121+
|| session.getCacheMode() == CacheMode.IGNORE;
122+
}
123+
124+
private static class CacheCleanupProcess implements AfterTransactionCompletionProcess {
125+
private final Object cacheKey;
126+
private final EntityPersister persister;
127+
private final Object previousVersion;
128+
private final Object nextVersion;
129+
private final SoftLock lock;
130+
private final Object cacheEntry;
131+
132+
private CacheCleanupProcess(Object cacheKey, EntityPersister persister, Object previousVersion, Object nextVersion, SoftLock lock, Object cacheEntry) {
133+
this.cacheKey = cacheKey;
134+
this.persister = persister;
135+
this.previousVersion = previousVersion;
136+
this.nextVersion = nextVersion;
137+
this.lock = lock;
138+
this.cacheEntry = cacheEntry;
139+
}
140+
141+
@Override
142+
public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) {
143+
final EntityDataAccess cache = persister.getCacheAccessStrategy();
144+
if ( cacheUpdateRequired( success, persister, session ) ) {
145+
cacheAfterUpdate( cache, cacheKey, session );
146+
}
147+
else {
148+
cache.unlockItem( session, cacheKey, lock );
149+
}
150+
}
151+
152+
private static boolean cacheUpdateRequired(boolean success, EntityPersister persister, SharedSessionContractImplementor session) {
153+
return success
154+
&& !persister.isCacheInvalidationRequired()
155+
&& session.getCacheMode().isPutEnabled();
156+
}
157+
158+
protected void cacheAfterUpdate(EntityDataAccess cache, Object ck, SharedSessionContractImplementor session) {
159+
final SessionEventListenerManager eventListenerManager = session.getEventListenerManager();
160+
final EventMonitor eventMonitor = session.getEventMonitor();
161+
final DiagnosticEvent cachePutEvent = eventMonitor.beginCachePutEvent();
162+
boolean put = false;
163+
try {
164+
eventListenerManager.cachePutStart();
165+
put = cache.afterUpdate( session, ck, cacheEntry, nextVersion, previousVersion, lock );
166+
}
167+
finally {
168+
eventMonitor.completeCachePutEvent(
169+
cachePutEvent,
170+
session,
171+
cache,
172+
persister,
173+
put,
174+
EventMonitor.CacheActionDescription.ENTITY_AFTER_UPDATE
175+
);
176+
final StatisticsImplementor statistics = session.getFactory().getStatistics();
177+
if ( put && statistics.isStatisticsEnabled() ) {
178+
statistics.entityCachePut(
179+
StatsHelper.getRootEntityRole( persister ),
180+
cache.getRegion().getName()
181+
);
182+
}
183+
eventListenerManager.cachePutEnd();
184+
}
185+
}
186+
}
187+
188+
}

hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.hibernate.event.monitor.spi.EventMonitor;
2222
import org.hibernate.event.spi.EventSource;
2323
import org.hibernate.event.monitor.spi.DiagnosticEvent;
24+
import org.hibernate.internal.OptimisticLockHelper;
2425
import org.hibernate.internal.build.AllowReflection;
2526
import org.hibernate.loader.LoaderLogging;
2627
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
@@ -104,9 +105,7 @@ public static void upgradeLock(Object object, EntityEntry entry, LockOptions loc
104105

105106
if ( persister.isVersioned() && requestedLockMode == LockMode.PESSIMISTIC_FORCE_INCREMENT ) {
106107
// todo : should we check the current isolation mode explicitly?
107-
final Object nextVersion =
108-
persister.forceVersionIncrement( entry.getId(), entry.getVersion(), false, session );
109-
entry.forceLocked( object, nextVersion );
108+
OptimisticLockHelper.forceVersionIncrement( object, entry, session );
110109
}
111110
else {
112111
if ( entry.isExistsInDatabase() ) {

0 commit comments

Comments
 (0)