Skip to content

Commit 6c66284

Browse files
authored
HHH-19941 simplify Session.contains() / deprecate Session.contains(String, Object)
and introduce SharedSessionContractImplementor.isManaged() - Session.contains(String, Object) was not making use of its first parameter - Session.contains(Object) was doing a duplicate lookup for the EntityEntry - The use of Session.contains(String, Object) as an SPI was not very efficient
1 parent 47a497c commit 6c66284

File tree

8 files changed

+87
-80
lines changed

8 files changed

+87
-80
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,10 @@ public interface Session extends SharedSessionContract, EntityManager {
470470
* @param object an instance of a persistent class
471471
*
472472
* @return {@code true} if the given instance is associated with this {@code Session}
473+
*
474+
* @deprecated Use {@link #contains(Object)} instead.
473475
*/
476+
@Deprecated(since = "7.2", forRemoval = true)
474477
boolean contains(String entityName, Object object);
475478

476479
/**

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,11 @@ public boolean contains(Object object) {
800800
return delegate.contains( object );
801801
}
802802

803+
@Override
804+
public boolean isManaged(Object entity) {
805+
return delegate.isManaged( entity );
806+
}
807+
803808
@Override
804809
public LockModeType getLockMode(Object entity) {
805810
return delegate.getLockMode( entity );

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,15 @@ default Integer getConfiguredJdbcBatchSize() {
503503
*/
504504
PersistenceContext getPersistenceContextInternal();
505505

506+
/**
507+
* Is the given entity managed by this session?
508+
*
509+
* @return true if this is a stateful session and
510+
* the entity belongs to its persistence
511+
* context and was not removed
512+
*/
513+
boolean isManaged(Object entity);
514+
506515
/**
507516
* detect in-memory changes, determine if the changes are to tables
508517
* named in the query and, if so, complete execution the flush

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,4 +742,9 @@ public SharedSessionBuilder sessionWithOptions() {
742742
public TransactionCompletionCallbacksImplementor getTransactionCompletionCallbacksImplementor() {
743743
return delegate.getTransactionCompletionCallbacksImplementor();
744744
}
745+
746+
@Override
747+
public boolean isManaged(Object entity) {
748+
return delegate.isManaged( entity );
749+
}
745750
}

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ private static void handleUninitializedProxy(
7474
EventSource source,
7575
Object object,
7676
PersistenceContext persistenceContext) {
77-
final boolean isTransient = isTransient( event, source, object );
77+
final boolean isTransient = !source.isManaged( object );
7878
// If refreshAlready is nonempty then the refresh is the result of a cascade refresh and the
7979
// refresh of the parent will take care of initializing the lazy entity and setting the
8080
// correct lock. This is needed only when the refresh is called directly on a lazy entity.
@@ -117,11 +117,6 @@ else if ( isTransient ) {
117117
}
118118
}
119119

120-
private static boolean isTransient(RefreshEvent event, EventSource source, Object object) {
121-
final String entityName = event.getEntityName();
122-
return entityName == null ? !source.contains( object ) : !source.contains( entityName, object );
123-
}
124-
125120
private static void refresh(RefreshEvent event, RefreshContext refreshedAlready, Object object) {
126121
final var source = event.getSession();
127122
final var persistenceContext = source.getPersistenceContextInternal();

hibernate-core/src/main/java/org/hibernate/id/IdentifierGeneratorHelper.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.hibernate.TransientObjectException;
2121
import org.hibernate.boot.registry.selector.spi.StrategySelector;
2222
import org.hibernate.engine.config.spi.ConfigurationService;
23-
import org.hibernate.engine.spi.SessionImplementor;
2423
import org.hibernate.engine.spi.SharedSessionContractImplementor;
2524
import org.hibernate.id.enhanced.ImplicitDatabaseObjectNamingStrategy;
2625
import org.hibernate.id.enhanced.StandardNamingStrategy;
@@ -82,16 +81,15 @@ else if ( integralType == BigDecimal.class ) {
8281

8382
public static Object getForeignId(
8483
String entityName, String propertyName, SharedSessionContractImplementor sessionImplementor, Object object) {
85-
final var persister =
86-
sessionImplementor.getFactory().getMappingMetamodel()
87-
.getEntityDescriptor( entityName );
88-
if ( sessionImplementor instanceof SessionImplementor statefulSession
89-
&& statefulSession.contains( entityName, object ) ) {
84+
if ( sessionImplementor.isManaged( object ) ) {
9085
//abort the save (the object is already saved by a circular cascade)
9186
return SHORT_CIRCUIT_INDICATOR;
9287
//throw new IdentifierGenerationException("save associated object first, or disable cascade for inverse association");
9388
}
9489
else {
90+
final var persister =
91+
sessionImplementor.getFactory().getMappingMetamodel()
92+
.getEntityDescriptor( entityName );
9593
return identifier( sessionImplementor, entityType( propertyName, persister ),
9694
associatedEntity( entityName, propertyName, object, persister ) );
9795
}

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

Lines changed: 55 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,13 +1338,45 @@ private void fireRefresh(final RefreshContext refreshedAlready, final RefreshEve
13381338
}
13391339

13401340
private void checkEntityManaged(String entityName, Object entity) {
1341-
if ( !managed( entityName, entity ) ) {
1341+
if ( !isManaged( entity ) ) {
13421342
throw new IllegalArgumentException( "Given entity is not associated with the persistence context" );
13431343
}
13441344
}
13451345

1346-
private boolean managed(String entityName, Object entity) {
1347-
return entityName == null ? contains( entity ) : contains( entityName, entity );
1346+
@Override
1347+
public boolean isManaged(Object entity) {
1348+
try {
1349+
final var lazyInitializer = extractLazyInitializer( entity );
1350+
if ( lazyInitializer != null ) {
1351+
//do not use proxiesByKey, since not all
1352+
//proxies that point to this session's
1353+
//instances are in that collection!
1354+
if ( lazyInitializer.isUninitialized() ) {
1355+
//if it is an uninitialized proxy, pointing
1356+
//with this session, then when it is accessed,
1357+
//the underlying instance will be "contained"
1358+
return lazyInitializer.getSession() == this;
1359+
}
1360+
else {
1361+
//if it is initialized, see if the underlying
1362+
//instance is contained, since we need to
1363+
//account for the fact that it might have been
1364+
//evicted
1365+
entity = lazyInitializer.getImplementation();
1366+
}
1367+
}
1368+
// A session is considered to contain an entity only if the entity has
1369+
// an entry in the session's persistence context and the entry reports
1370+
// that the entity has not been removed
1371+
final var entry = persistenceContext.getEntry( entity );
1372+
return entry != null && !entry.getStatus().isDeletedOrGone();
1373+
}
1374+
catch ( MappingException e ) {
1375+
throw new IllegalArgumentException( e.getMessage(), e );
1376+
}
1377+
catch ( RuntimeException e ) {
1378+
throw getExceptionConverter().convert( e );
1379+
}
13481380
}
13491381

13501382
// replicate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1632,8 +1664,10 @@ else if ( isPersistentAttributeInterceptable( object ) ) {
16321664
public boolean contains(Object object) {
16331665
checkOpen();
16341666
pulseTransactionCoordinator();
1667+
delayedAfterCompletion();
16351668

16361669
if ( object == null ) {
1670+
//TODO: this should throw IllegalArgumentException
16371671
return false;
16381672
}
16391673

@@ -1662,25 +1696,10 @@ public boolean contains(Object object) {
16621696
// an entry in the session's persistence context and the entry reports
16631697
// that the entity has not been removed
16641698
final var entry = persistenceContext.getEntry( object );
1665-
delayedAfterCompletion();
1666-
16671699
if ( entry == null ) {
1668-
if ( lazyInitializer == null && persistenceContext.getEntry( object ) == null ) {
1669-
// check if it is even an entity -> if not throw an exception (per JPA)
1670-
try {
1671-
final String entityName = getEntityNameResolver().resolveEntityName( object );
1672-
if ( entityName == null ) {
1673-
throw new IllegalArgumentException( "Could not resolve entity name for class '"
1674-
+ object.getClass() + "'" );
1675-
}
1676-
else {
1677-
requireEntityPersister( entityName );
1678-
}
1679-
}
1680-
catch ( HibernateException e ) {
1681-
throw new IllegalArgumentException( "Class '" + object.getClass()
1682-
+ "' is not an entity class", e );
1683-
}
1700+
if ( lazyInitializer == null ) {
1701+
// if not an entity, throw an exception, as required by spec
1702+
assertInstanceOfEntityType( object );
16841703
}
16851704
return false;
16861705
}
@@ -1696,60 +1715,28 @@ public boolean contains(Object object) {
16961715
}
16971716
}
16981717

1699-
@Override
1700-
public boolean contains(String entityName, Object object) {
1701-
checkOpenOrWaitingForAutoClose();
1702-
pulseTransactionCoordinator();
1703-
1704-
if ( object == null ) {
1705-
return false;
1706-
}
1707-
1718+
private void assertInstanceOfEntityType(Object object) {
17081719
try {
1709-
final var lazyInitializer = extractLazyInitializer( object );
1710-
if ( lazyInitializer == null && persistenceContext.getEntry( object ) == null ) {
1711-
// check if it is an entity -> if not throw an exception (per JPA)
1712-
try {
1713-
requireEntityPersister( entityName );
1714-
}
1715-
catch (HibernateException e) {
1716-
throw new IllegalArgumentException( "Not an entity [" + entityName + "] : " + object );
1717-
}
1720+
final String entityName = getEntityNameResolver().resolveEntityName( object );
1721+
if ( entityName == null ) {
1722+
throw new IllegalArgumentException( "Could not resolve entity name for class '"
1723+
+ object.getClass() + "'" );
17181724
}
1719-
1720-
if ( lazyInitializer != null ) {
1721-
//do not use proxiesByKey, since not all
1722-
//proxies that point to this session's
1723-
//instances are in that collection!
1724-
if ( lazyInitializer.isUninitialized() ) {
1725-
//if it is an uninitialized proxy, pointing
1726-
//with this session, then when it is accessed,
1727-
//the underlying instance will be "contained"
1728-
return lazyInitializer.getSession() == this;
1729-
}
1730-
else {
1731-
//if it is initialized, see if the underlying
1732-
//instance is contained, since we need to
1733-
//account for the fact that it might have been
1734-
//evicted
1735-
object = lazyInitializer.getImplementation();
1736-
}
1725+
else {
1726+
requireEntityPersister( entityName );
17371727
}
1738-
// A session is considered to contain an entity only if the entity has
1739-
// an entry in the session's persistence context and the entry reports
1740-
// that the entity has not been removed
1741-
final var entry = persistenceContext.getEntry( object );
1742-
delayedAfterCompletion();
1743-
return entry != null && !entry.getStatus().isDeletedOrGone();
17441728
}
1745-
catch ( MappingException e ) {
1746-
throw new IllegalArgumentException( e.getMessage(), e );
1747-
}
1748-
catch ( RuntimeException e ) {
1749-
throw getExceptionConverter().convert( e );
1729+
catch ( HibernateException e ) {
1730+
throw new IllegalArgumentException( "Class '" + object.getClass()
1731+
+ "' is not an entity class", e );
17501732
}
17511733
}
17521734

1735+
@Override @Deprecated(forRemoval = true)
1736+
public boolean contains(String entityName, Object object) {
1737+
return contains( object );
1738+
}
1739+
17531740
@Override
17541741
public ProcedureCall createStoredProcedureCall(String procedureName) {
17551742
checkOpen();

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,11 @@ public boolean isIdentifierRollbackEnabled() {
13021302
return false;
13031303
}
13041304

1305+
@Override
1306+
public boolean isManaged(Object entity) {
1307+
return false;
1308+
}
1309+
13051310
/////////////////////////////////////////////////////////////////////////////////////////////////////
13061311

13071312
//TODO: COPY/PASTE FROM SessionImpl, pull up!

0 commit comments

Comments
 (0)