HHH-18748 Add missing overloads of find() to Session

It was always weird and inconsistent that you sometimes had to use get(),
and this is also better documented this way (i.e. Hibernate-specific
semantics of find() are not documented by EntityManager)

Also add some missing @Overload annotations

Also move some impl down off the interface

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-10-21 00:24:15 +02:00
parent 5fca5255ca
commit e868c8c3cd
7 changed files with 216 additions and 125 deletions

View File

@ -15,8 +15,6 @@ import org.hibernate.stat.SessionStatistics;
import jakarta.persistence.CacheRetrieveMode;
import jakarta.persistence.CacheStoreMode;
import jakarta.persistence.ConnectionConsumer;
import jakarta.persistence.ConnectionFunction;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManager;
import jakarta.persistence.FlushModeType;
@ -194,6 +192,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @throws HibernateException if changes could not be synchronized with the database
*/
@Override
void flush();
/**
@ -205,7 +204,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @param flushMode the new {@link FlushModeType}
*
* @see #setHibernateFlushMode(FlushMode) for additional options
* @see #setHibernateFlushMode(FlushMode)
*/
@Override
void setFlushMode(FlushModeType flushMode);
@ -232,6 +231,8 @@ public interface Session extends SharedSessionContract, EntityManager {
* Get the current {@linkplain FlushModeType JPA flush mode} for this session.
*
* @return the {@link FlushModeType} currently in effect
*
* @see #getHibernateFlushMode()
*/
@Override
FlushModeType getFlushMode();
@ -267,6 +268,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @since 6.2
*/
@Override
CacheStoreMode getCacheStoreMode();
/**
@ -276,6 +278,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @since 6.2
*/
@Override
CacheRetrieveMode getCacheRetrieveMode();
/**
@ -287,6 +290,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @since 6.2
*/
@Override
void setCacheStoreMode(CacheStoreMode cacheStoreMode);
/**
@ -298,6 +302,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @since 6.2
*/
@Override
void setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode);
/**
@ -474,9 +479,109 @@ public interface Session extends SharedSessionContract, EntityManager {
*/
void evict(Object object);
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized
* instance.
* <p>
* The object returned by {@code get()} or {@code find()} is either an unproxied instance
* of the given entity class, or a fully-fetched proxy object.
* <p>
* This operation requests {@link LockMode#NONE}, that is, no lock, allowing the object
* to be retrieved from the cache without the cost of database access. However, if it is
* necessary to read the state from the database, the object will be returned with the
* lock mode {@link LockMode#READ}.
* <p>
* To bypass the {@linkplain Cache second-level cache}, and ensure that the state of the
* requested instance is read directly from the database, either:
* <ul>
* <li>call {@link #find(Class, Object, FindOption...)}, passing
* {@link CacheRetrieveMode#BYPASS} as an option,
* <li>call {@link #find(Class, Object, LockMode)} with the explicit lock mode
* {@link LockMode#READ}, or
* <li>{@linkplain #setCacheRetrieveMode set the cache mode} to
* {@link CacheRetrieveMode#BYPASS} before calling this method.
* </ul>
*
* @apiNote This operation is very similar to {@link #get(Class, Object)}.
*
* @param entityType the entity type
* @param id an identifier
*
* @return a fully-fetched persistent instance or null
*/
@Override
<T> T find(Class<T> entityType, Object id);
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized
* instance. Obtain the specified lock mode if the instance exists.
* <p>
* Convenient form of {@link #find(Class, Object, LockOptions)}.
*
* @param entityType the entity type
* @param id an identifier
* @param lockMode the lock mode
*
* @return a fully-fetched persistent instance or null
*
* @since 7.0
*
* @see #find(Class, Object, LockOptions)
*/
<T> T find(Class<T> entityType, Object id, LockMode lockMode);
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized
* instance. Obtain the specified lock mode if the instance exists.
*
* @param entityType the entity type
* @param id an identifier
* @param lockOptions the lock mode
*
* @return a fully-fetched persistent instance or null
*
* @since 7.0
*/
<T> T find(Class<T> entityType, Object id, LockOptions lockOptions);
/**
* Return the persistent instances of the given entity class with the given identifiers
* as a list. The position of an instance in the returned list matches the position of its
* identifier in the given list of identifiers, and the returned list contains a null value
* if there is no persistent instance matching a given identifier. If an instance is already
* associated with the session, that instance is returned. This method never returns an
* uninitialized instance.
* <p>
* Every object returned by {@code findMultiple()} is either an unproxied instance of the
* given entity class, or a fully-fetched proxy object.
* <p>
* For more advanced cases, use {@link #byMultipleIds(Class)}, which returns an instance of
* {@link MultiIdentifierLoadAccess}.
*
* @param entityType the entity type
* @param ids the list of identifiers
* @param options options, if any
*
* @return an ordered list of persistent instances, with null elements representing missing
* entities, whose positions in the list match the positions of their ids in the
* given list of identifiers
* @see #byMultipleIds(Class)
* @since 7.0
*/
<E> List<E> findMultiple(Class<E> entityType, List<Object> ids, FindOption... options);
/**
* Read the persistent state associated with the given identifier into the given
* transient instance.
*
* @param object a transient instance of an entity class
* @param id an identifier
*/
void load(Object object, Object id);
@ -524,6 +629,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @return an updated persistent instance
*/
@Override
<T> T merge(T object);
/**
@ -554,6 +660,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @param object a transient instance to be made persistent
*/
@Override
void persist(Object object);
/**
@ -635,6 +742,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @param object a persistent instance associated with this session
*/
@Override
void refresh(Object object);
/**
@ -688,39 +796,15 @@ public interface Session extends SharedSessionContract, EntityManager {
* saves, updates and deletions. Do not close open iterators or instances of
* {@link ScrollableResults}.
*/
@Override
void clear();
/**
* Return the persistent instances of the given entity class with the given identifiers
* as a list. The position of an instance in the list matches the position of its identifier
* in the given array, and the list contains a null value if there is no persistent instance
* matching a given identifier. If an instance is already associated with the session, that
* instance is returned. This method never returns an uninitialized instance.
* <p>
* Every object returned by {@code findMultiple()} is either an unproxied instance of the
* given entity class, or a fully-fetched proxy object.
* <p>
* For more advanced cases, use {@link #byMultipleIds(Class)}, which returns an instance of
* {@link MultiIdentifierLoadAccess}.
*
* @param entityType the entity type
* @param ids the identifiers
* @param options options, if any
* @return an ordered list of persistent instances, with null elements representing missing
* entities
* @see #byMultipleIds(Class)
* @since 7.0
*/
<E> List<E> findMultiple(Class<E> entityType, List<Object> ids, FindOption... options);
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized
* instance.
* <p>
* This operation is very similar to {@link #find(Class, Object)}.
* <p>
* The object returned by {@code get()} or {@code find()} is either an unproxied instance
* of the given entity class, or a fully-fetched proxy object.
* <p>
@ -734,10 +818,12 @@ public interface Session extends SharedSessionContract, EntityManager {
* <ul>
* <li>call {@link #get(Class, Object, LockMode)} with the explicit lock mode
* {@link LockMode#READ}, or
* <li>{@linkplain #setCacheMode(CacheMode) set the cache mode} to {@link CacheMode#IGNORE}
* <li>{@linkplain #setCacheMode set the cache mode} to {@link CacheMode#IGNORE}
* before calling this method.
* </ul>
*
* @apiNote This operation is very similar to {@link #find(Class, Object)}.
*
* @param entityType the entity type
* @param id an identifier
*
@ -752,8 +838,8 @@ public interface Session extends SharedSessionContract, EntityManager {
* instance. Obtain the specified lock mode if the instance exists.
* <p>
* Convenient form of {@link #get(Class, Object, LockOptions)}.
* <p>
* This operation is very similar to {@link #find(Class, Object, jakarta.persistence.LockModeType)}.
*
* @apiNote This operation is very similar to {@link #find(Class, Object, LockModeType)}.
*
* @param entityType the entity type
* @param id an identifier
@ -886,6 +972,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @since 6.0
*/
@Override
<T> T getReference(T object);
/**
@ -1026,15 +1113,6 @@ public interface Session extends SharedSessionContract, EntityManager {
*/
<T> NaturalIdMultiLoadAccess<T> byMultipleNaturalId(String entityName);
@Override
Filter enableFilter(String filterName);
@Override
Filter getEnabledFilter(String filterName);
@Override
void disableFilter(String filterName);
/**
* Get the {@linkplain SessionStatistics statistics} for this session.
*
@ -1196,32 +1274,4 @@ public interface Session extends SharedSessionContract, EntityManager {
*/
@Override @Deprecated(since = "6.0") @SuppressWarnings("rawtypes")
Query createQuery(CriteriaUpdate updateQuery);
@Override
default <C> void runWithConnection(ConnectionConsumer<C> action) {
doWork( connection -> {
try {
//noinspection unchecked
action.accept( (C) connection );
}
catch (Exception e) {
throw new RuntimeException( e );
}
} );
}
@Override
default <C, T> T callWithConnection(ConnectionFunction<C, T> function) {
return doReturningWork( (connection) -> {
try {
//noinspection unchecked
return function.apply( (C) connection );
}
catch (Exception e) {
throw new RuntimeException( e );
}
} );
}
}

View File

@ -302,6 +302,7 @@ public interface SessionFactory extends EntityManagerFactory, Referenceable, Ser
*
* @since 6.2
*/
@Override
SchemaManager getSchemaManager();
/**
@ -327,6 +328,7 @@ public interface SessionFactory extends EntityManagerFactory, Referenceable, Ser
*
* @throws HibernateException Indicates an issue closing the factory.
*/
@Override
void close() throws HibernateException;
/**

View File

@ -871,6 +871,16 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
return delegate.find( entityGraph, primaryKey, options );
}
@Override
public <T> T find(Class<T> entityType, Object id, LockMode lockMode) {
return delegate.find( entityType, id, lockMode );
}
@Override
public <T> T find(Class<T> entityType, Object id, LockOptions lockOptions) {
return delegate.find( entityType, id, lockOptions );
}
@Override
public <T> T getReference(Class<T> entityClass, Object id) {
return delegate.getReference( entityClass, id );

View File

@ -4,6 +4,8 @@
*/
package org.hibernate.engine.spi;
import jakarta.persistence.ConnectionConsumer;
import jakarta.persistence.ConnectionFunction;
import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.Session;
@ -108,4 +110,29 @@ public interface SessionImplementor extends Session, SharedSessionContractImplem
return true;
}
@Override
default <C> void runWithConnection(ConnectionConsumer<C> action) {
doWork( connection -> {
try {
//noinspection unchecked
action.accept( (C) connection );
}
catch (Exception e) {
throw new RuntimeException( e );
}
} );
}
@Override
default <C, T> T callWithConnection(ConnectionFunction<C, T> function) {
return doReturningWork( connection -> {
try {
//noinspection unchecked
return function.apply( (C) connection );
}
catch (Exception e) {
throw new RuntimeException( e );
}
} );
}
}

View File

@ -774,6 +774,16 @@ public class SessionLazyDelegator implements Session {
return this.lazySession.get().find( entityGraph, primaryKey, options );
}
@Override
public <T> T find(Class<T> entityType, Object id, LockMode lockMode) {
return this.lazySession.get().find( entityType, id, lockMode );
}
@Override
public <T> T find(Class<T> entityType, Object id, LockOptions lockOptions) {
return this.lazySession.get().find( entityType, id, lockOptions );
}
@Override
public void lock(Object entity, LockModeType lockMode) {
this.lazySession.get().lock( entity, lockMode );

View File

@ -2292,12 +2292,12 @@ public class SessionImpl
@Override
public <T> T find(Class<T> entityClass, Object primaryKey) {
return find( entityClass, primaryKey, null, null );
return find( entityClass, primaryKey, (LockOptions) null, null );
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
return find( entityClass, primaryKey, null, properties );
return find( entityClass, primaryKey, (LockOptions) null, properties );
}
@Override
@ -2305,20 +2305,35 @@ public class SessionImpl
return find( entityClass, primaryKey, lockModeType, null );
}
@Override
public <T> T find(Class<T> entityType, Object id, LockMode lockMode) {
checkTransactionNeededForLock( lockMode );
final LockOptions lockOptions = copySessionLockOptions();
lockOptions.setLockMode( lockMode );
return find( entityType, id, lockOptions, null );
}
@Override
public <T> T find(Class<T> entityType, Object id, LockOptions lockOptions) {
checkTransactionNeededForLock( lockOptions.getLockMode() );
return find( entityType, id, lockOptions, null );
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockModeType, Map<String, Object> properties) {
checkOpen();
if ( lockModeType == null ) {
throw new IllegalArgumentException("Given LockModeType was null");
}
final LockMode lockMode = LockModeTypeHelper.getLockMode( lockModeType );
checkTransactionNeededForLock( lockMode );
return find( entityClass, primaryKey, buildLockOptions( lockMode, properties ), properties );
}
final LockOptions lockOptions = lockModeType == null ? null : buildLockOptions( lockModeType, properties );
private <T> T find(Class<T> entityClass, Object primaryKey, LockOptions lockOptions, Map<String, Object> properties) {
try {
if ( lockModeType != null ) {
checkTransactionNeededForLock( LockModeTypeHelper.getLockMode( lockModeType ) );
}
final EffectiveEntityGraph effectiveEntityGraph = loadQueryInfluencers.getEffectiveEntityGraph();
effectiveEntityGraph.applyConfiguredGraph( properties );
loadQueryInfluencers.getEffectiveEntityGraph().applyConfiguredGraph( properties );
loadQueryInfluencers.setReadOnly( readOnlyHint( properties ) );
return byId( entityClass )
.with( determineAppropriateLocalCacheMode( properties ) )
.with( lockOptions )
@ -2531,17 +2546,17 @@ public class SessionImpl
@Override
public void lock(Object entity, LockModeType lockModeType, Map<String, Object> properties) {
lock( entity, buildLockOptions( lockModeType, properties ) );
lock( entity, buildLockOptions( LockModeTypeHelper.getLockMode( lockModeType ), properties ) );
}
@Override
public void lock(Object entity, LockModeType lockMode, LockOption... options) {
lock( entity, buildLockOptions( lockMode, options ) );
public void lock(Object entity, LockModeType lockModeType, LockOption... options) {
lock( entity, buildLockOptions( LockModeTypeHelper.getLockMode( lockModeType ), options ) );
}
private LockOptions buildLockOptions(LockModeType lockModeType, LockOption[] options) {
private LockOptions buildLockOptions(LockMode lockMode, LockOption[] options) {
final LockOptions lockOptions = copySessionLockOptions();
lockOptions.setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) );
lockOptions.setLockMode( lockMode );
for ( LockOption option : options ) {
if ( option instanceof PessimisticLockScope lockScope ) {
lockOptions.setLockScope( lockScope );
@ -2553,9 +2568,9 @@ public class SessionImpl
return lockOptions;
}
private LockOptions buildLockOptions(LockModeType lockModeType, Map<String, Object> properties) {
private LockOptions buildLockOptions(LockMode lockMode, Map<String, Object> properties) {
final LockOptions lockOptions = copySessionLockOptions();
lockOptions.setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) );
lockOptions.setLockMode( lockMode );
if ( properties != null ) {
applyPropertiesToLockOptions( properties, () -> lockOptions );
}
@ -2591,7 +2606,7 @@ public class SessionImpl
refresh( entity );
}
else {
refresh( entity, buildLockOptions( lockModeType, properties ) );
refresh( entity, buildLockOptions( LockModeTypeHelper.getLockMode( lockModeType ), properties ) );
}
}
finally {

View File

@ -6,7 +6,6 @@ package org.hibernate.internal.util;
import jakarta.persistence.LockModeType;
import org.hibernate.AssertionFailure;
import org.hibernate.LockMode;
/**
@ -26,26 +25,14 @@ public final class LockModeConverter {
* @return The JPA {@link LockModeType}
*/
public static LockModeType convertToLockModeType(LockMode lockMode) {
switch (lockMode) {
case NONE:
case READ: // no exact equivalent in JPA
return LockModeType.NONE;
case OPTIMISTIC:
return LockModeType.OPTIMISTIC;
case OPTIMISTIC_FORCE_INCREMENT:
return LockModeType.OPTIMISTIC_FORCE_INCREMENT;
case PESSIMISTIC_READ:
return LockModeType.PESSIMISTIC_READ;
case PESSIMISTIC_WRITE:
case UPGRADE_NOWAIT:
case UPGRADE_SKIPLOCKED:
return LockModeType.PESSIMISTIC_WRITE;
case WRITE: // no exact equivalent in JPA
case PESSIMISTIC_FORCE_INCREMENT:
return LockModeType.PESSIMISTIC_FORCE_INCREMENT;
default:
throw new AssertionFailure( "unhandled lock mode " + lockMode );
}
return switch ( lockMode ) {
case NONE, READ -> LockModeType.NONE; // no exact equivalent in JPA
case OPTIMISTIC -> LockModeType.OPTIMISTIC;
case OPTIMISTIC_FORCE_INCREMENT -> LockModeType.OPTIMISTIC_FORCE_INCREMENT;
case PESSIMISTIC_READ -> LockModeType.PESSIMISTIC_READ;
case PESSIMISTIC_WRITE, UPGRADE_NOWAIT, UPGRADE_SKIPLOCKED -> LockModeType.PESSIMISTIC_WRITE; // no exact equivalent in JPA
case WRITE, PESSIMISTIC_FORCE_INCREMENT -> LockModeType.PESSIMISTIC_FORCE_INCREMENT;
};
}
@ -56,23 +43,13 @@ public final class LockModeConverter {
* @return The Hibernate {@link LockMode}.
*/
public static LockMode convertToLockMode(LockModeType lockModeType) {
switch ( lockModeType ) {
case NONE:
return LockMode.NONE;
case READ:
case OPTIMISTIC:
return LockMode.OPTIMISTIC;
case WRITE:
case OPTIMISTIC_FORCE_INCREMENT:
return LockMode.OPTIMISTIC_FORCE_INCREMENT;
case PESSIMISTIC_READ:
return LockMode.PESSIMISTIC_READ;
case PESSIMISTIC_WRITE:
return LockMode.PESSIMISTIC_WRITE;
case PESSIMISTIC_FORCE_INCREMENT:
return LockMode.PESSIMISTIC_FORCE_INCREMENT;
default:
throw new AssertionFailure( "Unknown LockModeType: " + lockModeType );
}
return switch ( lockModeType ) {
case NONE -> LockMode.NONE;
case READ, OPTIMISTIC -> LockMode.OPTIMISTIC;
case WRITE, OPTIMISTIC_FORCE_INCREMENT -> LockMode.OPTIMISTIC_FORCE_INCREMENT;
case PESSIMISTIC_READ -> LockMode.PESSIMISTIC_READ;
case PESSIMISTIC_WRITE -> LockMode.PESSIMISTIC_WRITE;
case PESSIMISTIC_FORCE_INCREMENT -> LockMode.PESSIMISTIC_FORCE_INCREMENT;
};
}
}