Skip to content

Commit 3189f0e

Browse files
committed
HHH-19482 fix handling of lock modes for reads outside tx
and clean up some error messages
1 parent 50440e0 commit 3189f0e

18 files changed

+106
-100
lines changed

hibernate-core/src/main/java/org/hibernate/LockMode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public enum LockMode implements FindOption, RefreshOption {
5252
* rather than pull it from a cache.
5353
* <p>
5454
* This is the "default" lock mode, the mode requested by calling
55-
* {@link Session#get(Class, Object)} without passing an explicit
55+
* {@link Session#find(Class, Object)} without passing an explicit
5656
* mode. It permits the state of an object to be retrieved from
5757
* the cache without the cost of database access.
5858
*

hibernate-core/src/main/java/org/hibernate/Session.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -825,19 +825,32 @@ public interface Session extends SharedSessionContract, EntityManager {
825825
void remove(Object object);
826826

827827
/**
828-
* Determine the current {@link LockMode} of the given managed instance associated
829-
* with this session.
828+
* Determine the current {@linkplain LockMode lock mode} held on the given
829+
* managed instance associated with this session.
830+
* <p>
831+
* Unlike the JPA-standard {@link #getLockMode}, this operation may be
832+
* called when no transaction is active, in which case it should return
833+
* {@link LockMode#NONE}, indicating that no pessimistic lock is held on
834+
* the given entity.
830835
*
831836
* @param object a persistent instance associated with this session
832837
*
833-
* @return the current lock mode
838+
* @return the lock mode currently held on the given entity
839+
*
840+
* @throws IllegalStateException if the given instance is not associated
841+
* with this persistence context
842+
* @throws ObjectDeletedException if the given instance was already
843+
* {@linkplain #remove removed}
834844
*/
835845
LockMode getCurrentLockMode(Object object);
836846

837847
/**
838-
* Completely clear the session. Evict all loaded instances and cancel all pending
839-
* saves, updates and deletions. Do not close open iterators or instances of
840-
* {@link ScrollableResults}.
848+
* Completely clear the persistence context. Evict all loaded instances,
849+
* causing every managed entity currently associated with this session to
850+
* transition to the detached state, and cancel all pending insertions,
851+
* updates, and deletions.
852+
* <p>
853+
* Does not close open iterators or instances of {@link ScrollableResults}.
841854
*/
842855
@Override
843856
void clear();

hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntry.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,10 @@ public static EntityEntry deserialize(
113113
(String) ois.readObject(),
114114
ois.readObject(),
115115
Status.valueOf( (String) ois.readObject() ),
116-
( previousStatusString = (String) ois.readObject() ).length() == 0
117-
? null
118-
: Status.valueOf( previousStatusString ),
116+
( previousStatusString = (String) ois.readObject() )
117+
.isEmpty()
118+
? null
119+
: Status.valueOf( previousStatusString ),
119120
(Object[]) ois.readObject(),
120121
(Object[]) ois.readObject(),
121122
ois.readObject(),

hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntryFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
*
1919
* @author Emmanuel Bernard
2020
*/
21-
public class ImmutableEntityEntryFactory implements EntityEntryFactory {
21+
@Deprecated(since = "7", forRemoval = true)
22+
public final class ImmutableEntityEntryFactory implements EntityEntryFactory {
2223
/**
2324
* Singleton access
2425
*/

hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntry.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ public static EntityEntry deserialize(
8383
(String) ois.readObject(),
8484
ois.readObject(),
8585
Status.valueOf( (String) ois.readObject() ),
86-
( previousStatusString = (String) ois.readObject() ).length() == 0
87-
? null
88-
: Status.valueOf( previousStatusString ),
86+
( previousStatusString = (String) ois.readObject() )
87+
.isEmpty()
88+
? null
89+
: Status.valueOf( previousStatusString ),
8990
(Object[]) ois.readObject(),
9091
(Object[]) ois.readObject(),
9192
ois.readObject(),

hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntryFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
*
1919
* @author Emmanuel Bernard
2020
*/
21-
public class MutableEntityEntryFactory implements EntityEntryFactory {
21+
@Deprecated(since = "7", forRemoval = true)
22+
public final class MutableEntityEntryFactory implements EntityEntryFactory {
2223
/**
2324
* Singleton access
2425
*/

hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java

Lines changed: 27 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -655,58 +655,34 @@ public EntityEntry addEntry(
655655
final EntityPersister persister,
656656
final boolean disableVersionIncrement) {
657657
assert lockMode != null;
658-
659-
final EntityEntry e;
660-
661-
/*
662-
IMPORTANT!!!
663-
664-
The following instanceof checks and castings are intentional.
665-
666-
DO NOT REFACTOR to make calls through the EntityEntryFactory interface, which would result
667-
in polymorphic call sites which will severely impact performance.
668-
669-
When a virtual method is called via an interface the JVM needs to resolve which concrete
670-
implementation to call. This takes CPU cycles and is a performance penalty. It also prevents method
671-
inlining which further degrades performance. Casting to an implementation and making a direct method call
672-
removes the virtual call, and allows the methods to be inlined. In this critical code path, it has a very
673-
large impact on performance to make virtual method calls.
674-
*/
675-
if ( persister.getEntityEntryFactory() instanceof MutableEntityEntryFactory ) {
676-
//noinspection RedundantCast
677-
e = ( (MutableEntityEntryFactory) persister.getEntityEntryFactory() ).createEntityEntry(
678-
status,
679-
loadedState,
680-
rowId,
681-
id,
682-
version,
683-
lockMode,
684-
existsInDatabase,
685-
persister,
686-
disableVersionIncrement,
687-
this
688-
);
689-
}
690-
else {
691-
//noinspection RedundantCast
692-
e = ( (ImmutableEntityEntryFactory) persister.getEntityEntryFactory() ).createEntityEntry(
693-
status,
694-
loadedState,
695-
rowId,
696-
id,
697-
version,
698-
lockMode,
699-
existsInDatabase,
700-
persister,
701-
disableVersionIncrement,
702-
this
703-
);
704-
}
705-
706-
entityEntryContext.addEntityEntry( entity, e );
707-
658+
final EntityEntry entityEntry =
659+
persister.isMutable()
660+
? new MutableEntityEntry(
661+
status,
662+
loadedState,
663+
rowId,
664+
id,
665+
version,
666+
lockMode,
667+
existsInDatabase,
668+
persister,
669+
disableVersionIncrement,
670+
this
671+
)
672+
: new ImmutableEntityEntry(
673+
status,
674+
loadedState,
675+
rowId,
676+
id,
677+
version,
678+
lockMode,
679+
existsInDatabase,
680+
persister,
681+
disableVersionIncrement
682+
);
683+
entityEntryContext.addEntityEntry( entity, entityEntry );
708684
setHasNonReadOnlyEnties( status );
709-
return e;
685+
return entityEntry;
710686
}
711687

712688
@Override
@@ -716,7 +692,6 @@ public EntityEntry addReferenceEntry(
716692
final EntityEntry entityEntry = asManagedEntity( entity ).$$_hibernate_getEntityEntry();
717693
entityEntry.setStatus( status );
718694
entityEntryContext.addEntityEntry( entity, entityEntry );
719-
720695
setHasNonReadOnlyEnties( status );
721696
return entityEntry;
722697
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
* Contract to build {@link EntityEntry}
1414
*
1515
* @author Emmanuel Bernard
16+
*
17+
* @deprecated No longer used
1618
*/
19+
@Deprecated(since = "7", forRemoval = true)
1720
public interface EntityEntryFactory extends Serializable {
1821

1922
/**

hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,7 @@ private void checksBeforeQueryCreation() {
525525
public void prepareForQueryExecution(boolean requiresTxn) {
526526
checksBeforeQueryCreation();
527527
if ( requiresTxn && !isTransactionInProgress() ) {
528-
throw new TransactionRequiredException(
529-
"Query requires transaction be in progress, but no transaction is known to be in progress"
530-
);
528+
throw new TransactionRequiredException( "No active transaction" );
531529
}
532530
}
533531

hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -554,13 +554,13 @@ public LockMode getCurrentLockMode(Object object) {
554554
if ( e == null ) {
555555
throw new IllegalArgumentException( "Given entity is not associated with the persistence context" );
556556
}
557-
558-
if ( e.getStatus().isDeletedOrGone() ) {
559-
throw new ObjectDeletedException( "The given object was deleted", e.getId(),
557+
else if ( e.getStatus().isDeletedOrGone() ) {
558+
throw new ObjectDeletedException( "Given entity was removed", e.getId(),
560559
e.getPersister().getEntityName() );
561560
}
562-
563-
return e.getLockMode();
561+
else {
562+
return e.getLockMode();
563+
}
564564
}
565565

566566
@Override
@@ -2611,7 +2611,7 @@ private static CacheStoreMode determineCacheStoreMode(Map<String, Object> settin
26112611
}
26122612

26132613
private void checkTransactionNeededForUpdateOperation() {
2614-
checkTransactionNeededForUpdateOperation( "no transaction is in progress" );
2614+
checkTransactionNeededForUpdateOperation( "No active transaction" );
26152615
}
26162616

26172617
@Override
@@ -2772,11 +2772,11 @@ public LockModeType getLockMode(Object entity) {
27722772
checkOpen();
27732773

27742774
if ( !isTransactionInProgress() ) {
2775-
throw new TransactionRequiredException( "Call to EntityManager#getLockMode should occur within transaction according to spec" );
2775+
throw new TransactionRequiredException( "No active transaction" );
27762776
}
27772777

27782778
if ( !contains( entity ) ) {
2779-
throw getExceptionConverter().convert( new IllegalArgumentException( "entity not in the persistence context" ) );
2779+
throw getExceptionConverter().convert( new IllegalArgumentException( "Entity not associated with the persistence context" ) );
27802780
}
27812781

27822782
return LockModeTypeHelper.getLockModeType( getCurrentLockMode( entity ) );

hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4585,7 +4585,7 @@ private SQLQueryParser createSqlQueryParser(Table table) {
45854585

45864586
@Override
45874587
public EntityEntryFactory getEntityEntryFactory() {
4588-
return this.entityEntryFactory;
4588+
return entityEntryFactory;
45894589
}
45904590

45914591
/**

hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,11 @@ default String getSqlAliasStem() {
163163
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
164164

165165
/**
166-
* Get the EntityEntryFactory indicated for the entity mapped by this persister.
166+
* Get the {@link EntityEntryFactory} indicated for the entity mapped by this persister.
167167
*
168-
* @return The proper EntityEntryFactory.
168+
* @deprecated No longer used
169169
*/
170+
@Deprecated(since = "7", forRemoval = true)
170171
EntityEntryFactory getEntityEntryFactory();
171172

172173
/**

hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@
8787
import jakarta.persistence.ParameterMode;
8888
import jakarta.persistence.PersistenceException;
8989
import jakarta.persistence.TemporalType;
90-
import jakarta.persistence.TransactionRequiredException;
9190
import jakarta.persistence.metamodel.Type;
9291

9392
import static java.lang.Boolean.parseBoolean;
@@ -823,10 +822,6 @@ protected ProcedureOutputs outputs() {
823822

824823
@Override
825824
protected int doExecuteUpdate() {
826-
if ( !getSession().isTransactionInProgress() ) {
827-
throw new TransactionRequiredException( "jakarta.persistence.Query.executeUpdate requires active transaction" );
828-
}
829-
830825
// the expectation is that there is just one Output, of type UpdateCountOutput
831826
try {
832827
execute();

hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.util.Calendar;
99
import java.util.Collection;
1010
import java.util.Date;
11-
import java.util.HashSet;
1211
import java.util.Map;
1312
import java.util.Set;
1413

@@ -634,8 +633,9 @@ protected void prepareForExecution() {
634633

635634
@Override
636635
public int executeUpdate() throws HibernateException {
637-
getSession().checkTransactionNeededForUpdateOperation( "Executing an update/delete query" );
638-
final HashSet<String> fetchProfiles = beforeQueryHandlingFetchProfiles();
636+
//TODO: refactor copy/paste of QuerySqmImpl.executeUpdate()
637+
getSession().checkTransactionNeededForUpdateOperation( "No active transaction for update or delete query" );
638+
final var fetchProfiles = beforeQueryHandlingFetchProfiles();
639639
boolean success = false;
640640
try {
641641
final int result = doExecuteUpdate();

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@
8686
import java.util.Calendar;
8787
import java.util.Collection;
8888
import java.util.Date;
89-
import java.util.HashSet;
9089
import java.util.List;
9190
import java.util.Map;
9291
import java.util.function.BooleanSupplier;
@@ -507,9 +506,10 @@ private QueryInterpretationCache interpretationCache() {
507506

508507
@Override
509508
public int executeUpdate() {
509+
//TODO: refactor copy/paste of AbstractQuery.executeUpdate()
510510
verifyUpdate();
511-
getSession().checkTransactionNeededForUpdateOperation( "Executing an update/delete query" );
512-
final HashSet<String> fetchProfiles = beforeQueryHandlingFetchProfiles();
511+
getSession().checkTransactionNeededForUpdateOperation( "No active transaction for update or delete query" );
512+
final var fetchProfiles = beforeQueryHandlingFetchProfiles();
513513
boolean success = false;
514514
try {
515515
final int result = doExecuteUpdate();

hibernate-core/src/main/java/org/hibernate/sql/exec/internal/BaseExecutionContext.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
public class BaseExecutionContext implements ExecutionContext {
1616

1717
private final SharedSessionContractImplementor session;
18+
private final boolean transactionActive;
1819

1920
public BaseExecutionContext(SharedSessionContractImplementor session) {
2021
this.session = session;
22+
transactionActive = session.isTransactionInProgress();
2123
}
2224

2325
// Optimization: mark this as final so to avoid a megamorphic call on this
@@ -27,6 +29,11 @@ public final SharedSessionContractImplementor getSession() {
2729
return session;
2830
}
2931

32+
@Override
33+
public final boolean isTransactionActive() {
34+
return transactionActive;
35+
}
36+
3037
// Also marked as final for the same reason
3138
@Override
3239
public final LoadQueryInfluencers getLoadQueryInfluencers() {

hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ default boolean isScrollResult(){
2525

2626
SharedSessionContractImplementor getSession();
2727

28+
default boolean isTransactionActive() {
29+
return getSession().isTransactionInProgress();
30+
}
31+
2832
QueryOptions getQueryOptions();
2933

3034
LoadQueryInfluencers getLoadQueryInfluencers();

0 commit comments

Comments
 (0)