Skip to content

Commit 92b7b0f

Browse files
geoandDavideD
authored andcommitted
Fix LockOnUpdateTest
This is done by following same pattern that we have in main: essentially make a reactive copy of the CacheEntityLoaderHelper (which was also have in main but was deleted to make the project compile against ORM 6) With this change org.hibernate.reactive.LockOnUpdateTest passes and there are no new failures
1 parent d00deee commit 92b7b0f

File tree

2 files changed

+150
-1
lines changed

2 files changed

+150
-1
lines changed

hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLoadEventListener.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.hibernate.proxy.HibernateProxy;
4040
import org.hibernate.proxy.LazyInitializer;
4141
import org.hibernate.reactive.event.ReactiveLoadEventListener;
42+
import org.hibernate.reactive.loader.entity.ReactiveCacheEntityLoaderHelper;
4243
import org.hibernate.reactive.logging.impl.Log;
4344
import org.hibernate.reactive.logging.impl.LoggerFactory;
4445
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister;
@@ -617,7 +618,7 @@ private CompletionStage<Object> doLoad(
617618
);
618619
}
619620

620-
CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = CacheEntityLoaderHelper.INSTANCE
621+
CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = ReactiveCacheEntityLoaderHelper.INSTANCE
621622
.loadFromSessionCache( event, keyToLoad, options );
622623
Object entity = persistenceContextEntry.getEntity();
623624
if ( entity != null ) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.loader.entity;
7+
8+
import static org.hibernate.loader.ast.internal.LoaderHelper.upgradeLock;
9+
import org.hibernate.HibernateException;
10+
import org.hibernate.LockMode;
11+
import org.hibernate.LockOptions;
12+
import org.hibernate.ObjectDeletedException;
13+
import org.hibernate.cache.spi.access.EntityDataAccess;
14+
import org.hibernate.cache.spi.access.SoftLock;
15+
import org.hibernate.engine.spi.EntityEntry;
16+
import org.hibernate.engine.spi.EntityKey;
17+
import org.hibernate.engine.spi.SessionImplementor;
18+
import org.hibernate.engine.spi.Status;
19+
import org.hibernate.event.spi.EventSource;
20+
import org.hibernate.event.spi.LoadEvent;
21+
import org.hibernate.event.spi.LoadEventListener;
22+
import org.hibernate.internal.CoreLogging;
23+
import org.hibernate.internal.CoreMessageLogger;
24+
import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.EntityStatus;
25+
import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.PersistenceContextEntry;
26+
import org.hibernate.persister.entity.EntityPersister;
27+
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister;
28+
import org.hibernate.sql.results.LoadingLogger;
29+
30+
/**
31+
* @author Gavin King
32+
*
33+
* @see org.hibernate.loader.ast.internal.CacheEntityLoaderHelper
34+
*/
35+
public class ReactiveCacheEntityLoaderHelper {
36+
37+
public static final ReactiveCacheEntityLoaderHelper INSTANCE = new ReactiveCacheEntityLoaderHelper();
38+
39+
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( ReactiveCacheEntityLoaderHelper.class );
40+
41+
private ReactiveCacheEntityLoaderHelper() {}
42+
43+
/**
44+
* Attempts to locate the entity in the session-level cache.
45+
* <p>
46+
* If allowed to return nulls, then if the entity happens to be found in
47+
* the session cache, we check the entity type for proper handling
48+
* of entity hierarchies.
49+
* <p>
50+
* If checkDeleted was set to true, then if the entity is found in the
51+
* session-level cache, its current status within the session cache
52+
* is checked to see if it has previously been scheduled for deletion.
53+
*
54+
* @param event The load event
55+
* @param keyToLoad The EntityKey representing the entity to be loaded.
56+
* @param options The load options.
57+
*
58+
* @return The entity from the session-level cache, or null.
59+
*
60+
* @throws HibernateException Generally indicates problems applying a lock-mode.
61+
*/
62+
public PersistenceContextEntry loadFromSessionCache(
63+
final LoadEvent event,
64+
final EntityKey keyToLoad,
65+
final LoadEventListener.LoadType options) throws HibernateException {
66+
67+
SessionImplementor session = event.getSession();
68+
Object old = session.getEntityUsingInterceptor( keyToLoad );
69+
70+
if ( old != null ) {
71+
// this object was already loaded
72+
EntityEntry oldEntry = session.getPersistenceContext().getEntry( old );
73+
if ( options.isCheckDeleted() ) {
74+
if ( oldEntry.getStatus().isDeletedOrGone() ) {
75+
LoadingLogger.LOGGER.debug(
76+
"Load request found matching entity in context, but it is scheduled for removal; returning null" );
77+
return new PersistenceContextEntry( old, EntityStatus.REMOVED_ENTITY_MARKER );
78+
}
79+
}
80+
if ( options.isAllowNulls() ) {
81+
final EntityPersister persister = event.getSession()
82+
.getFactory()
83+
.getRuntimeMetamodels()
84+
.getMappingMetamodel()
85+
.getEntityDescriptor( keyToLoad.getEntityName() );
86+
if ( !persister.isInstance( old ) ) {
87+
LOG.debug(
88+
"Load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null"
89+
);
90+
return new PersistenceContextEntry( old, EntityStatus.INCONSISTENT_RTN_CLASS_MARKER );
91+
}
92+
}
93+
upgradeLock( old, oldEntry, event.getLockOptions(), event.getSession() );
94+
}
95+
96+
return new PersistenceContextEntry( old, EntityStatus.MANAGED );
97+
}
98+
99+
/**
100+
* see org.hibernate.event.internal.AbstractLockUpgradeEventListener#upgradeLock(Object, EntityEntry, LockOptions, EventSource)
101+
*/
102+
private static void upgradeLock(Object object, EntityEntry entry, LockOptions lockOptions, EventSource source) {
103+
104+
LockMode requestedLockMode = lockOptions.getLockMode();
105+
if ( requestedLockMode.greaterThan( entry.getLockMode() ) ) {
106+
// The user requested a "greater" (i.e. more restrictive) form of pessimistic lock
107+
108+
if ( entry.getStatus() != Status.MANAGED ) {
109+
throw new ObjectDeletedException(
110+
"attempted to lock a deleted instance",
111+
entry.getId(),
112+
entry.getPersister().getEntityName()
113+
);
114+
}
115+
116+
final EntityPersister persister = entry.getPersister();
117+
final boolean cachingEnabled = persister.canWriteToCache();
118+
SoftLock lock = null;
119+
Object ck = null;
120+
try {
121+
if ( cachingEnabled ) {
122+
EntityDataAccess cache = persister.getCacheAccessStrategy();
123+
ck = cache.generateCacheKey( entry.getId(), persister, source.getFactory(), source.getTenantIdentifier() );
124+
lock = cache.lockItem( source, ck, entry.getVersion() );
125+
}
126+
127+
if ( persister.isVersioned() && requestedLockMode == LockMode.PESSIMISTIC_FORCE_INCREMENT ) {
128+
// todo : should we check the current isolation mode explicitly?
129+
Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), source );
130+
entry.forceLocked( object, nextVersion );
131+
}
132+
else {
133+
( (ReactiveEntityPersister) persister )
134+
.reactiveLock( entry.getId(), entry.getVersion(), object, lockOptions, source );
135+
}
136+
entry.setLockMode( requestedLockMode );
137+
}
138+
finally {
139+
// the database now holds a lock + the object is flushed from the cache,
140+
// so release the soft lock
141+
if ( cachingEnabled ) {
142+
persister.getCacheAccessStrategy().unlockItem( source, ck, lock );
143+
}
144+
}
145+
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)