improvements to typesafety of NaturalIdLoadAccess
and clean up its jdoc and the jdoc of its friends
This commit is contained in:
parent
5efa49f7d1
commit
76fa597d1b
|
@ -13,24 +13,36 @@ import org.hibernate.graph.RootGraph;
|
|||
|
||||
/**
|
||||
* Loads an entity by its primary identifier.
|
||||
* <p>
|
||||
* The interface is especially useful when customizing association
|
||||
* fetching using an {@link jakarta.persistence.EntityGraph}.
|
||||
* <pre>
|
||||
* var graph = session.createEntityGraph(Book.class);
|
||||
* graph.addSubgraph(Book_.publisher);
|
||||
* graph.addPluralSubgraph(Book_.authors).addSubgraph(Author_.person);
|
||||
* session.byId(Book.class).withFetchGraph(graph).load(bookId);
|
||||
* </pre>
|
||||
*
|
||||
* @author Eric Dalquist
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see Session#byId(Class)
|
||||
*/
|
||||
public interface IdentifierLoadAccess<T> {
|
||||
/**
|
||||
* Specify the {@link LockOptions} to use when retrieving the entity.
|
||||
* Specify the {@linkplain LockOptions lock options} to use when
|
||||
* querying the database.
|
||||
*
|
||||
* @param lockOptions The lock options to use.
|
||||
* @param lockOptions The lock options to use
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
IdentifierLoadAccess<T> with(LockOptions lockOptions);
|
||||
|
||||
/**
|
||||
* Specify the {@link CacheMode} to use when retrieving the entity.
|
||||
* Specify the {@link CacheMode} to use when obtaining an entity.
|
||||
*
|
||||
* @param cacheMode The CacheMode to use.
|
||||
* @param cacheMode The {@code CacheMode} to use
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
|
@ -43,10 +55,20 @@ public interface IdentifierLoadAccess<T> {
|
|||
*/
|
||||
IdentifierLoadAccess<T> withReadOnly(boolean readOnly);
|
||||
|
||||
/**
|
||||
* Override the associations fetched by default by specifying
|
||||
* the complete list of associations to be fetched as an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph}.
|
||||
*/
|
||||
default IdentifierLoadAccess<T> withFetchGraph(RootGraph<T> graph) {
|
||||
return with( graph, GraphSemantic.FETCH );
|
||||
}
|
||||
|
||||
/**
|
||||
* Augment the associations fetched by default by specifying a
|
||||
* list of additional associations to be fetched as an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph}.
|
||||
*/
|
||||
default IdentifierLoadAccess<T> withLoadGraph(RootGraph<T> graph) {
|
||||
return with( graph, GraphSemantic.LOAD );
|
||||
}
|
||||
|
@ -59,15 +81,22 @@ public interface IdentifierLoadAccess<T> {
|
|||
return withLoadGraph( graph );
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the associations fetched by specifying an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph},
|
||||
* and how it should be {@linkplain GraphSemantic interpreted}.
|
||||
*/
|
||||
IdentifierLoadAccess<T> with(RootGraph<T> graph, GraphSemantic semantic);
|
||||
|
||||
/**
|
||||
* Return the persistent instance with the given identifier, assuming that the instance exists. This method
|
||||
* might return a proxied instance that is initialized on-demand, when a non-identifier method is accessed.
|
||||
* Return the persistent instance with the given identifier, assuming
|
||||
* that the instance exists. This method might return a proxied instance
|
||||
* that is initialized on-demand, when a non-identifier method is accessed.
|
||||
* <p>
|
||||
* You should not use this method to determine if an instance exists; to check for existence, use {@link #load}
|
||||
* instead. Use this only to retrieve an instance that you assume exists, where non-existence would be an
|
||||
* actual error.
|
||||
* You should not use this method to determine if an instance exists;
|
||||
* to check for existence, use {@link #load} instead. Use this only to
|
||||
* retrieve an instance that you assume exists, where non-existence would
|
||||
* be an actual error.
|
||||
*
|
||||
* @param id The identifier for which to obtain a reference
|
||||
*
|
||||
|
@ -76,9 +105,10 @@ public interface IdentifierLoadAccess<T> {
|
|||
T getReference(Object id);
|
||||
|
||||
/**
|
||||
* Return the persistent instance 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, initializing it if needed. This
|
||||
* method never returns an uninitialized instance.
|
||||
* Return the persistent instance 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, initializing it
|
||||
* if needed. This method never returns an uninitialized instance.
|
||||
*
|
||||
* @param id The identifier
|
||||
*
|
||||
|
@ -87,12 +117,12 @@ public interface IdentifierLoadAccess<T> {
|
|||
T load(Object id);
|
||||
|
||||
/**
|
||||
* Same semantic as {@link #load} except that here {@link Optional} is returned to
|
||||
* handle nullability.
|
||||
* Just like {@link #load}, except that here an {@link Optional} is
|
||||
* returned.
|
||||
*
|
||||
* @param id The identifier
|
||||
*
|
||||
* @return The persistent instance, if one, wrapped in Optional
|
||||
* @return The persistent instance, if any, as an {@link Optional}
|
||||
*/
|
||||
Optional<T> loadOptional(Object id);
|
||||
}
|
||||
|
|
|
@ -12,34 +12,53 @@ import org.hibernate.graph.GraphSemantic;
|
|||
import org.hibernate.graph.RootGraph;
|
||||
|
||||
/**
|
||||
* Loads multiple entities at once by identifiers, ultimately via one of the
|
||||
* {@link #multiLoad} methods, using the various options specified (if any)
|
||||
* Loads multiple instances of a given entity type at once, by
|
||||
* specifying a list of identifier values. This allows the entities
|
||||
* to be fetched from the database in batches.
|
||||
* <pre>
|
||||
* var graph = session.createEntityGraph(Book.class);
|
||||
* graph.addSubgraph(Book_.publisher);
|
||||
* session.byId(Book.class).withFetchGraph(graph).multiLoad(bookIds);
|
||||
* </pre>
|
||||
*
|
||||
* @see Session#byMultipleIds(Class)
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface MultiIdentifierLoadAccess<T> {
|
||||
/**
|
||||
* Specify the {@link LockOptions} to use when retrieving the entity.
|
||||
* Specify the {@linkplain LockOptions lock options} to use when
|
||||
* querying the database.
|
||||
*
|
||||
* @param lockOptions The lock options to use.
|
||||
* @param lockOptions The lock options to use
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> with(LockOptions lockOptions);
|
||||
|
||||
/**
|
||||
* Specify the {@link CacheMode} to use when retrieving the entity.
|
||||
* Specify the {@link CacheMode} to use when obtaining an entity.
|
||||
*
|
||||
* @param cacheMode The CacheMode to use.
|
||||
* @param cacheMode The {@code CacheMode} to use
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> with(CacheMode cacheMode);
|
||||
|
||||
/**
|
||||
* Override the associations fetched by default by specifying
|
||||
* the complete list of associations to be fetched as an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph}.
|
||||
*/
|
||||
default MultiIdentifierLoadAccess<T> withFetchGraph(RootGraph<T> graph) {
|
||||
return with( graph, GraphSemantic.FETCH );
|
||||
}
|
||||
|
||||
/**
|
||||
* Augment the associations fetched by default by specifying a
|
||||
* list of additional associations to be fetched as an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph}.
|
||||
*/
|
||||
default MultiIdentifierLoadAccess<T> withLoadGraph(RootGraph<T> graph) {
|
||||
return with( graph, GraphSemantic.LOAD );
|
||||
}
|
||||
|
@ -52,16 +71,28 @@ public interface MultiIdentifierLoadAccess<T> {
|
|||
return with( graph, GraphSemantic.LOAD );
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the associations fetched by specifying an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph},
|
||||
* and how it should be {@linkplain GraphSemantic interpreted}.
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> with(RootGraph<T> graph, GraphSemantic semantic);
|
||||
|
||||
/**
|
||||
* Specify a batch size for loading the entities (how many at a time). The default is
|
||||
* to use a batch sizing strategy defined by the Dialect in use. Any greater-than-one
|
||||
* value here will override that default behavior. If giving an explicit value here,
|
||||
* care should be taken to not exceed the capabilities of the underlying database.
|
||||
* Specify a batch size, that is, how many entities should be
|
||||
* fetched in each request to the database.
|
||||
* <ul>
|
||||
* <li>By default, the batch sizing strategy is determined by the
|
||||
* {@linkplain org.hibernate.dialect.Dialect#getBatchLoadSizingStrategy
|
||||
* SQL dialect}, but
|
||||
* <li>if some {@code batchSize>1} is specified as an
|
||||
* argument to this method, then that batch size will be used.
|
||||
* </ul>
|
||||
* <p>
|
||||
* Note that overall a batch-size is considered a hint. How the underlying loading
|
||||
* mechanism interprets that is completely up to that underlying loading mechanism.
|
||||
* If an explicit batch size is set manually, care should be taken
|
||||
* to not exceed the capabilities of the underlying database.
|
||||
* <p>
|
||||
* A batch size is considered a hint.
|
||||
*
|
||||
* @param batchSize The batch size
|
||||
*
|
||||
|
@ -70,53 +101,58 @@ public interface MultiIdentifierLoadAccess<T> {
|
|||
MultiIdentifierLoadAccess<T> withBatchSize(int batchSize);
|
||||
|
||||
/**
|
||||
* Specify whether we should check the {@link Session} to see whether the first-level cache already contains any of the
|
||||
* entities to be loaded in a managed state <b>for the purpose of not including those
|
||||
* ids to the batch-load SQL</b>.
|
||||
* Specifies whether the ids of managed entity instances already
|
||||
* cached in the current persistence context should be excluded
|
||||
* from the list of ids sent to the database.
|
||||
* <p>
|
||||
* By default, all ids are included and sent to the database.
|
||||
*
|
||||
* @param enabled {@code true} enables this checking; {@code false} (the default) disables it.
|
||||
* @param enabled {@code true} if they should be excluded;
|
||||
* {@code false} if they should be included.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> enableSessionCheck(boolean enabled);
|
||||
|
||||
/**
|
||||
* Should the multi-load operation be allowed to return entities that are locally
|
||||
* deleted? A locally deleted entity is one which has been passed to this
|
||||
* Session's {@link Session#delete} / {@link Session#remove} method, but not
|
||||
* yet flushed. The default behavior is to handle them as null in the return
|
||||
* (see {@link #enableOrderedReturn}).
|
||||
* Should {@link #multiLoad} return entity instances that have been
|
||||
* {@link Session#remove(Object) marked for removal} in the current
|
||||
* session, but not yet {@code delete}d in the database?
|
||||
* <p>
|
||||
* By default, instances marked for removal are replaced by null in
|
||||
* the returned list of entities when {@link #enableOrderedReturn}
|
||||
* is used.
|
||||
*
|
||||
* @param enabled {@code true} enables returning the deleted entities;
|
||||
* {@code false} (the default) disables it.
|
||||
* @param enabled {@code true} if removed entities should be returned;
|
||||
* {@code false} if they should be replaced by null values.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> enableReturnOfDeletedEntities(boolean enabled);
|
||||
|
||||
/**
|
||||
* Should the return List be ordered and positional in relation to the
|
||||
* incoming ids? If enabled (the default), the return List is ordered and
|
||||
* positional relative to the incoming ids. In other words, a request to
|
||||
* {@code multiLoad([2,1,3])} will return {@code [Entity#2, Entity#1, Entity#3]}.
|
||||
* Should the returned list of entity instances be ordered, with the
|
||||
* position of an entity instance determined by the position of its
|
||||
* identifier in the list if ids passed to {@link #multiLoad}?
|
||||
* <p>
|
||||
* An important distinction is made here in regards to the handling of
|
||||
* unknown entities depending on this "ordered return" setting. If enabled
|
||||
* a null is inserted into the List at the proper position(s). If disabled,
|
||||
* the nulls are not put into the return List. In other words, consumers of
|
||||
* the returned ordered List would need to be able to handle null elements.
|
||||
* By default, the returned list is ordered and the positions of the
|
||||
* entities correspond to the positions of their ids. In this case,
|
||||
* the {@linkplain #enableReturnOfDeletedEntities handling of entities
|
||||
* marked for removal} becomes important.
|
||||
*
|
||||
* @param enabled {@code true} (the default) enables ordering;
|
||||
* {@code false} disables it.
|
||||
* @param enabled {@code true} if entity instances should be ordered;
|
||||
* {@code false} if they may be returned in any order.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> enableOrderedReturn(boolean enabled);
|
||||
|
||||
/**
|
||||
* Perform a load of multiple entities by identifiers. See {@link #enableOrderedReturn}
|
||||
* and {@link #enableReturnOfDeletedEntities} for options which effect
|
||||
* the size and "shape" of the return list.
|
||||
* Retrieve the entities with the given identifiers.
|
||||
* <p>
|
||||
* Note that the options {@link #enableReturnOfDeletedEntities} and
|
||||
* {@link #enableOrderedReturn} affect the size and shape of the
|
||||
* returned list of entity instances.
|
||||
*
|
||||
* @param <K> The identifier type
|
||||
*
|
||||
|
@ -126,9 +162,11 @@ public interface MultiIdentifierLoadAccess<T> {
|
|||
<K> List<T> multiLoad(K... ids);
|
||||
|
||||
/**
|
||||
* Perform a load of multiple entities by identifiers. See {@link #enableOrderedReturn}
|
||||
* and {@link #enableReturnOfDeletedEntities} for options which effect
|
||||
* the size and "shape" of the return list.
|
||||
* Retrieve the entities with the given identifiers.
|
||||
* <p>
|
||||
* Note that the options {@link #enableReturnOfDeletedEntities} and
|
||||
* {@link #enableOrderedReturn} affect the size and shape of the
|
||||
* returned list of entity instances.
|
||||
*
|
||||
* @param ids The ids to load
|
||||
* @param <K> The identifier type
|
||||
|
|
|
@ -6,24 +6,37 @@
|
|||
*/
|
||||
package org.hibernate;
|
||||
|
||||
import jakarta.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Loads an entity by its natural identifier.
|
||||
* Loads an entity by its natural identifier, which may be a
|
||||
* composite value comprising more than one attribute of the
|
||||
* entity. If the entity has exactly one attribute annotated
|
||||
* {@link org.hibernate.annotations.NaturalId @NaturalId},
|
||||
* then {@link SimpleNaturalIdLoadAccess} may be used instead.
|
||||
* <p>
|
||||
* This is a generic form of load-by-natural-id covering both a single attribute
|
||||
* and multiple attributes as the natural-id. For natural-ids defined by a single
|
||||
* attribute, {@link SimpleNaturalIdLoadAccess} offers simplified access.
|
||||
* <pre>
|
||||
* Book book =
|
||||
* session.byNaturalId(Book.class)
|
||||
* .using(Book_.isbn, isbn)
|
||||
* .using(Book_.printing, printing)
|
||||
* .load();
|
||||
* </pre>
|
||||
*
|
||||
* @author Eric Dalquist
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see Session#byNaturalId(Class)
|
||||
* @see org.hibernate.annotations.NaturalId
|
||||
* @see Session#byNaturalId
|
||||
* @see SimpleNaturalIdLoadAccess
|
||||
*/
|
||||
public interface NaturalIdLoadAccess<T> {
|
||||
/**
|
||||
* Specify the {@link LockOptions} to use when retrieving the entity.
|
||||
* Specify the {@linkplain LockOptions lock options} to use when
|
||||
* querying the database.
|
||||
*
|
||||
* @param lockOptions The lock options to use.
|
||||
*
|
||||
|
@ -32,9 +45,23 @@ public interface NaturalIdLoadAccess<T> {
|
|||
NaturalIdLoadAccess<T> with(LockOptions lockOptions);
|
||||
|
||||
/**
|
||||
* Add a NaturalId attribute value.
|
||||
* Add a {@link org.hibernate.annotations.NaturalId @NaturalId}
|
||||
* attribute value in a typesafe way.
|
||||
*
|
||||
* @param attributeName The entity attribute name that is marked as a NaturalId
|
||||
* @param attribute A typesafe reference to an attribute of the
|
||||
* entity that is annotated {@code @NaturalId}
|
||||
* @param value The value of the attribute
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
<X> NaturalIdLoadAccess<T> using(SingularAttribute<? super T, X> attribute, X value);
|
||||
|
||||
/**
|
||||
* Add a {@link org.hibernate.annotations.NaturalId @NaturalId}
|
||||
* attribute value.
|
||||
*
|
||||
* @param attributeName The name of an attribute of the entity
|
||||
* that is annotated {@code @NaturalId}
|
||||
* @param value The value of the attribute
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
|
@ -42,24 +69,54 @@ public interface NaturalIdLoadAccess<T> {
|
|||
NaturalIdLoadAccess<T> using(String attributeName, Object value);
|
||||
|
||||
/**
|
||||
* Set multiple natural-id attribute values at once. The passed array is
|
||||
* expected to have an even number of elements, with the attribute name followed
|
||||
* by its value, for example, {@code using( "system", "matrix", "username", "neo" )}.
|
||||
* Set multiple {@link org.hibernate.annotations.NaturalId @NaturalId}
|
||||
* attribute values at once. An even number of arguments is expected,
|
||||
* with each attribute name followed by its value, for example:
|
||||
* <pre>
|
||||
* Book book =
|
||||
* session.byNaturalId(Book.class)
|
||||
* .using(Map.of(Book_.ISBN, isbn, Book_.PRINTING, printing))
|
||||
* .load();
|
||||
* </pre>
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
NaturalIdLoadAccess<T> using(Map<String,?> mappings);
|
||||
|
||||
/**
|
||||
* Set multiple {@link org.hibernate.annotations.NaturalId @NaturalId}
|
||||
* attribute values at once. An even number of arguments is expected,
|
||||
* with each attribute name followed by its value, for example:
|
||||
* <pre>
|
||||
* Book book =
|
||||
* session.byNaturalId(Book.class)
|
||||
* .using(Book_.ISBN, isbn, Book_.PRINTING, printing)
|
||||
* .load();
|
||||
* </pre>
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*
|
||||
* @deprecated use {@link #using(Map)} with {@link Map#of}, which is
|
||||
* slightly more typesafe
|
||||
*/
|
||||
@Deprecated(since = "6.3")
|
||||
NaturalIdLoadAccess<T> using(Object... mappings);
|
||||
|
||||
/**
|
||||
* For entities with mutable natural ids, should natural ids be synchronized prior to performing a lookup?
|
||||
* The default, for correctness, is to synchronize.
|
||||
* For entities with mutable natural ids, should natural ids be
|
||||
* synchronized prior to executing a query? The default, for
|
||||
* correctness, is to synchronize.
|
||||
* <p>
|
||||
* Here "synchronization" means updating the natural id to primary key cross-reference maintained by the
|
||||
* session. When enabled, prior to performing the lookup, Hibernate will check all entities of the given
|
||||
* type associated with the session to see if any natural id values have changed and, if so, update the
|
||||
* cross-reference. There is a performance penalty associated with this, so if it is completely certain
|
||||
* the no natural id in play has changed, this setting can be disabled to circumvent that impact.
|
||||
* Disabling this setting when natural id values <em>have</em> changed can result in incorrect results!
|
||||
* Here "synchronization" means updating the natural id to
|
||||
* primary key cross-reference maintained by the session. When
|
||||
* enabled, prior to performing the lookup, Hibernate will check
|
||||
* all entities of the given type associated with the session to
|
||||
* see if any natural id values have changed and, if so, update
|
||||
* the cross-reference. There is a performance penalty associated
|
||||
* with this, so if it is completely certain the no natural id in
|
||||
* play has changed, this setting can be disabled to circumvent
|
||||
* that impact. Disabling this setting when natural id values
|
||||
* <em>have</em> changed can lead to incorrect results!
|
||||
*
|
||||
* @param enabled Should synchronization be performed?
|
||||
* {@code true} indicates synchronization will be performed;
|
||||
|
@ -70,31 +127,36 @@ public interface NaturalIdLoadAccess<T> {
|
|||
NaturalIdLoadAccess<T> setSynchronizationEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Return the persistent instance with the natural id value(s) defined by the call(s) to {@link #using}. This
|
||||
* method might return a proxied instance that is initialized on-demand, when a non-identifier method is accessed.
|
||||
* Return the persistent instance with the full natural id specified
|
||||
* by previous calls to {@link #using}. This method might return a
|
||||
* proxied instance that is initialized on-demand, when a non-identifier
|
||||
* method is accessed.
|
||||
* <p>
|
||||
* You should not use this method to determine if an instance exists; to check for existence, use {@link #load}
|
||||
* instead. Use this only to retrieve an instance that you assume exists, where non-existence would be an
|
||||
* actual error.
|
||||
* You should not use this method to determine if an instance exists;
|
||||
* to check for existence, use {@link #load} instead. Use this method
|
||||
* only to retrieve an instance that you assume exists, where
|
||||
* non-existence would be an actual error.
|
||||
*
|
||||
* @return the persistent instance or proxy
|
||||
*/
|
||||
T getReference();
|
||||
|
||||
/**
|
||||
* Return the persistent instance with the natural id value(s) defined by the call(s) to {@link #using}, or
|
||||
* {@code null} if there is no such persistent instance. If the instance is already associated with the session,
|
||||
* return that instance, initializing it if needed. This method never returns an uninitialized instance.
|
||||
* Return the persistent instance with the full natural id specified
|
||||
* by previous calls to {@link #using}, or {@code null} if there is no
|
||||
* such persistent instance. If the instance is already associated with
|
||||
* the session, return that instance, initializing it if needed. This
|
||||
* method never returns an uninitialized instance.
|
||||
*
|
||||
* @return The persistent instance or {@code null}
|
||||
*/
|
||||
T load();
|
||||
|
||||
/**
|
||||
* Same semantic as {@link #load} except that here {@link Optional} is returned to
|
||||
* handle nullability.
|
||||
* Just like {@link #load}, except that here an {@link Optional} is
|
||||
* returned.
|
||||
*
|
||||
* @return The persistent instance, if one, wrapped in Optional
|
||||
* @return The persistent instance, if one, as an {@link Optional}
|
||||
*/
|
||||
Optional<T> loadOptional();
|
||||
|
||||
|
|
|
@ -11,34 +11,60 @@ import java.util.Map;
|
|||
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.graph.RootGraph;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.asMap;
|
||||
|
||||
/**
|
||||
* Defines the ability to load multiple entities by simple natural-id simultaneously.
|
||||
* Loads multiple instances of a given entity type at once, by
|
||||
* specifying a list of natural id values. This allows the entities
|
||||
* to be fetched from the database in batches.
|
||||
* <p>
|
||||
* Composite natural ids may be accommodated by passing a list of
|
||||
* maps of type {@code Map<String,Object>} to {@link #multiLoad}.
|
||||
* Each map must contain the natural id attribute values keyed by
|
||||
* {@link org.hibernate.annotations.NaturalId @NaturalId} attribute
|
||||
* name.
|
||||
* <pre>
|
||||
* var compositeNaturalId =
|
||||
* Map.of(Book_.ISBN, isbn, Book_.PRINTING, printing);
|
||||
* </pre>
|
||||
*
|
||||
* @see Session#byMultipleNaturalId(Class)
|
||||
*/
|
||||
public interface NaturalIdMultiLoadAccess<T> {
|
||||
/**
|
||||
* Specify the {@link LockOptions} to use when retrieving the entity.
|
||||
* Specify the {@linkplain LockOptions lock options} to use when
|
||||
* querying the database.
|
||||
*
|
||||
* @param lockOptions The lock options to use.
|
||||
* @param lockOptions The lock options to use
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
NaturalIdMultiLoadAccess<T> with(LockOptions lockOptions);
|
||||
|
||||
/**
|
||||
* Specify the {@link CacheMode} to use when retrieving the entity.
|
||||
* Specify the {@link CacheMode} to use when obtaining an entity.
|
||||
*
|
||||
* @param cacheMode The CacheMode to use.
|
||||
* @param cacheMode The {@code CacheMode} to use
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
NaturalIdMultiLoadAccess<T> with(CacheMode cacheMode);
|
||||
|
||||
/**
|
||||
* Override the associations fetched by default by specifying
|
||||
* the complete list of associations to be fetched as an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph}.
|
||||
*/
|
||||
default NaturalIdMultiLoadAccess<T> withFetchGraph(RootGraph<T> graph) {
|
||||
return with( graph, GraphSemantic.FETCH );
|
||||
}
|
||||
|
||||
/**
|
||||
* Augment the associations fetched by default by specifying a
|
||||
* list of additional associations to be fetched as an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph}.
|
||||
*/
|
||||
default NaturalIdMultiLoadAccess<T> withLoadGraph(RootGraph<T> graph) {
|
||||
return with( graph, GraphSemantic.LOAD );
|
||||
}
|
||||
|
@ -52,17 +78,27 @@ public interface NaturalIdMultiLoadAccess<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Define a load or fetch graph to be used when retrieving the entity
|
||||
* Customize the associations fetched by specifying an
|
||||
* {@linkplain jakarta.persistence.EntityGraph entity graph},
|
||||
* and how it should be {@linkplain GraphSemantic interpreted}.
|
||||
*/
|
||||
NaturalIdMultiLoadAccess<T> with(RootGraph<T> graph, GraphSemantic semantic);
|
||||
|
||||
/**
|
||||
* Specify a batch size for loading the entities (how many at a time). The default is
|
||||
* to use a batch sizing strategy defined by the Dialect in use. Any greater-than-one
|
||||
* value here will override that default behavior. If giving an explicit value here,
|
||||
* care should be taken to not exceed the capabilities of the underlying database.
|
||||
* Specify a batch size, that is, how many entities should be
|
||||
* fetched in each request to the database.
|
||||
* <ul>
|
||||
* <li>By default, the batch sizing strategy is determined by the
|
||||
* {@linkplain org.hibernate.dialect.Dialect#getBatchLoadSizingStrategy
|
||||
* SQL dialect}, but
|
||||
* <li>if some {@code batchSize>1} is specified as an
|
||||
* argument to this method, then that batch size will be used.
|
||||
* </ul>
|
||||
* <p>
|
||||
* Note that overall a batch-size is considered a hint.
|
||||
* If an explicit batch size is set manually, care should be taken
|
||||
* to not exceed the capabilities of the underlying database.
|
||||
* <p>
|
||||
* A batch size is considered a hint.
|
||||
*
|
||||
* @param batchSize The batch size
|
||||
*
|
||||
|
@ -71,68 +107,75 @@ public interface NaturalIdMultiLoadAccess<T> {
|
|||
NaturalIdMultiLoadAccess<T> withBatchSize(int batchSize);
|
||||
|
||||
/**
|
||||
* Should the multi-load operation be allowed to return entities that are locally
|
||||
* deleted? A locally deleted entity is one which has been passed to this
|
||||
* Session's {@link Session#delete} / {@link Session#remove} method, but not
|
||||
* yet flushed. The default behavior is to handle them as null in the return
|
||||
* (see {@link #enableOrderedReturn}).
|
||||
* Should {@link #multiLoad} return entity instances that have been
|
||||
* {@link Session#remove(Object) marked for removal} in the current
|
||||
* session, but not yet {@code delete}d in the database?
|
||||
* <p>
|
||||
* By default, instances marked for removal are replaced by null in
|
||||
* the returned list of entities when {@link #enableOrderedReturn}
|
||||
* is used.
|
||||
*
|
||||
* @param enabled {@code true} enables returning the deleted entities;
|
||||
* {@code false} (the default) disables it.
|
||||
* @param enabled {@code true} if removed entities should be returned;
|
||||
* {@code false} if they should be replaced by null values.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
NaturalIdMultiLoadAccess<T> enableReturnOfDeletedEntities(boolean enabled);
|
||||
|
||||
/**
|
||||
* Should the return List be ordered and positional in relation to the
|
||||
* incoming ids? If enabled (the default), the return List is ordered and
|
||||
* positional relative to the incoming ids. In other words, a request to
|
||||
* {@code multiLoad([2,1,3])} will return {@code [Entity#2, Entity#1, Entity#3]}.
|
||||
* Should the returned list of entity instances be ordered, with the
|
||||
* position of an entity instance determined by the position of its
|
||||
* identifier in the list if ids passed to {@link #multiLoad}?
|
||||
* <p>
|
||||
* An important distinction is made here in regards to the handling of
|
||||
* unknown entities depending on this "ordered return" setting. If enabled
|
||||
* a null is inserted into the List at the proper position(s). If disabled,
|
||||
* the nulls are not put into the return List. In other words, consumers of
|
||||
* the returned ordered List would need to be able to handle null elements.
|
||||
* By default, the returned list is ordered and the positions of the
|
||||
* entities correspond to the positions of their ids. In this case,
|
||||
* the {@linkplain #enableReturnOfDeletedEntities handling of entities
|
||||
* marked for removal} becomes important.
|
||||
*
|
||||
* @param enabled {@code true} (the default) enables ordering;
|
||||
* {@code false} disables it.
|
||||
* @param enabled {@code true} if entity instances should be ordered;
|
||||
* {@code false} if they may be returned in any order.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
NaturalIdMultiLoadAccess<T> enableOrderedReturn(boolean enabled);
|
||||
|
||||
/**
|
||||
* Perform a load of multiple entities by natural-id.
|
||||
* Retrieve the entities with the given natural id values.
|
||||
* <p>
|
||||
* See {@link #enableOrderedReturn} and {@link #enableReturnOfDeletedEntities}
|
||||
* for options which effect the size and "shape" of the return list.
|
||||
* Note that the options {@link #enableReturnOfDeletedEntities} and
|
||||
* {@link #enableOrderedReturn} affect the size and shape of the
|
||||
* returned list of entity instances.
|
||||
*
|
||||
* @param ids The natural-id values to load
|
||||
* @param ids The natural id values to load
|
||||
*
|
||||
* @return The managed entities.
|
||||
*/
|
||||
List<T> multiLoad(Object... ids);
|
||||
|
||||
/**
|
||||
* Perform a load of multiple entities by natural-id.
|
||||
* Retrieve the entities with the given natural id values.
|
||||
* <p>
|
||||
* See {@link #enableOrderedReturn} and {@link #enableReturnOfDeletedEntities}
|
||||
* for options which effect the size and "shape" of the return list.
|
||||
* Note that the options {@link #enableReturnOfDeletedEntities} and
|
||||
* {@link #enableOrderedReturn} affect the size and shape of the
|
||||
* returned list of entity instances.
|
||||
*
|
||||
* @param ids The natural-id values to load
|
||||
* @param ids The natural id values to load
|
||||
*
|
||||
* @return The managed entities.
|
||||
*/
|
||||
List<T> multiLoad(List<?> ids);
|
||||
|
||||
/**
|
||||
* Helper for creating a Map that represents the value of a compound natural-id
|
||||
* for use in loading. The passed array is expected to have an even number of elements
|
||||
* representing key, value pairs. E.g. `using( "system", "matrix", "username", "neo" )`
|
||||
* Helper for creating a {@link Map} that represents the value of a
|
||||
* composite natural id. An even number of arguments is expected,
|
||||
* with each attribute name followed by its value.
|
||||
*
|
||||
* @see NaturalIdLoadAccess#using(Object...)
|
||||
*
|
||||
* @deprecated use {@link Map#of} instead
|
||||
*/
|
||||
@Deprecated(since = "6.3")
|
||||
static Map<String,?> compoundValue(Object... elements) {
|
||||
return CollectionHelper.asMap( elements );
|
||||
return asMap( elements );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,31 +9,44 @@ package org.hibernate;
|
|||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Loads an entity by its natural identifier.
|
||||
* Loads an entity by its natural identifier. This simplified API is
|
||||
* used when the entity has exactly one field or property annotated
|
||||
* {@link org.hibernate.annotations.NaturalId @NaturalId}. If an
|
||||
* entity has multiple attributes annotated {@code @NaturalId}, then
|
||||
* {@link NaturalIdLoadAccess} should be used instead.
|
||||
* <p>
|
||||
* <pre>
|
||||
* Book book = session.bySimpleNaturalId(Book.class).load(isbn);
|
||||
* </pre>
|
||||
*
|
||||
* @author Eric Dalquist
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see Session#bySimpleNaturalId(Class)
|
||||
* @see org.hibernate.annotations.NaturalId
|
||||
* @see NaturalIdLoadAccess
|
||||
*/
|
||||
public interface SimpleNaturalIdLoadAccess<T> {
|
||||
/**
|
||||
* Specify the {@link LockOptions} to use when retrieving the entity.
|
||||
* Specify the {@linkplain LockOptions lock options} to use when
|
||||
* querying the database.
|
||||
*
|
||||
* @param lockOptions The lock options to use.
|
||||
* @param lockOptions The lock options to use
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
SimpleNaturalIdLoadAccess<T> with(LockOptions lockOptions);
|
||||
|
||||
/**
|
||||
* For entities with mutable natural ids, should Hibernate perform "synchronization" prior to performing
|
||||
* lookups? The default is to perform "synchronization" (for correctness).
|
||||
* For entities with mutable natural ids, should Hibernate perform
|
||||
* "synchronization" prior to performing lookups? The default is
|
||||
* to perform "synchronization" (for correctness).
|
||||
* <p>
|
||||
* See {@link NaturalIdLoadAccess#setSynchronizationEnabled} for detailed discussion.
|
||||
* See {@link NaturalIdLoadAccess#setSynchronizationEnabled} for
|
||||
* detailed discussion.
|
||||
*
|
||||
* @param enabled Should synchronization be performed? {@code true} indicates synchronization will be performed;
|
||||
* @param enabled Should synchronization be performed?
|
||||
* {@code true} indicates synchronization will be performed;
|
||||
* {@code false} indicates it will be circumvented.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
|
@ -41,37 +54,43 @@ public interface SimpleNaturalIdLoadAccess<T> {
|
|||
SimpleNaturalIdLoadAccess<T> setSynchronizationEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Return the persistent instance with the given natural id value, assuming that the instance exists. This method
|
||||
* might return a proxied instance that is initialized on-demand, when a non-identifier method is accessed.
|
||||
* Return the persistent instance with the given natural id value,
|
||||
* assuming that the instance exists. This method might return a
|
||||
* proxied instance that is initialized on-demand, when a
|
||||
* non-identifier method is accessed.
|
||||
* <p>
|
||||
* You should not use this method to determine if an instance exists;
|
||||
* to check for existence, use {@link #load} instead. Use this only to
|
||||
* retrieve an instance that you assume exists, where non-existence
|
||||
* would be an actual error.
|
||||
*
|
||||
* You should not use this method to determine if an instance exists; to check for existence, use {@link #load}
|
||||
* instead. Use this only to retrieve an instance that you assume exists, where non-existence would be an
|
||||
* actual error.
|
||||
* @param naturalIdValue The value of the natural id
|
||||
*
|
||||
* @param naturalIdValue The value of the natural-id for the entity to retrieve
|
||||
*
|
||||
* @return The persistent instance or proxy, if an instance exists. Otherwise, {@code null}.
|
||||
* @return The persistent instance or proxy, if an instance exists.
|
||||
* Otherwise, {@code null}.
|
||||
*/
|
||||
T getReference(Object naturalIdValue);
|
||||
|
||||
/**
|
||||
* Return the persistent instance with the given natural id value, or {@code null} if there is no such persistent
|
||||
* instance. If the instance is already associated with the session, return that instance, initializing it if
|
||||
* needed. This method never returns an uninitialized instance.
|
||||
* Return the persistent instance with the given natural id value,
|
||||
* or {@code null} if there is no such persistent instance. If the
|
||||
* instance is already associated with the session, return that
|
||||
* instance, initializing it if needed. This method never returns
|
||||
* an uninitialized instance.
|
||||
*
|
||||
* @param naturalIdValue The value of the natural-id for the entity to retrieve
|
||||
* @param naturalIdValue The value of the natural-id
|
||||
*
|
||||
* @return The persistent instance or {@code null}
|
||||
*/
|
||||
T load(Object naturalIdValue);
|
||||
|
||||
/**
|
||||
* Same semantic as {@link #load} except that here {@link Optional} is returned to
|
||||
* handle nullability.
|
||||
* Just like {@link #load}, except that here an {@link Optional}
|
||||
* is returned.
|
||||
*
|
||||
* @param naturalIdValue The identifier
|
||||
*
|
||||
* @return The persistent instance, if one, wrapped in Optional
|
||||
* @return The persistent instance, if any, as an {@link Optional}
|
||||
*/
|
||||
Optional<T> loadOptional(Object naturalIdValue);
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ import java.util.LinkedHashMap;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.metamodel.SingularAttribute;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.NaturalIdLoadAccess;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
||||
|
@ -31,6 +31,12 @@ public class NaturalIdLoadAccessImpl<T> extends BaseNaturalIdLoadAccessImpl<T> i
|
|||
return (NaturalIdLoadAccessImpl<T>) super.with( lockOptions );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> NaturalIdLoadAccess<T> using(SingularAttribute<? super T, X> attribute, X value) {
|
||||
naturalIdParameters.put( attribute.getName(), value );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccess<T> using(String attributeName, Object value) {
|
||||
naturalIdParameters.put( attributeName, value );
|
||||
|
@ -38,6 +44,12 @@ public class NaturalIdLoadAccessImpl<T> extends BaseNaturalIdLoadAccessImpl<T> i
|
|||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccess<T> using(Map<String, ?> mappings) {
|
||||
naturalIdParameters.putAll( mappings );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override @Deprecated
|
||||
public NaturalIdLoadAccess<T> using(Object... mappings) {
|
||||
CollectionHelper.collectMapEntries( naturalIdParameters::put, mappings );
|
||||
return this;
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
package org.hibernate.orm.test.mapping.identifier;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
|
@ -67,6 +66,16 @@ public class MultipleNaturalIdTest extends BaseEntityManagerFunctionalTestCase {
|
|||
.load();
|
||||
//end::naturalid-load-access-example[]
|
||||
|
||||
assertEquals("High-Performance Java Persistence", book.getTitle());
|
||||
});
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
Publisher publisher = entityManager.getReference(Publisher.class, 1L);
|
||||
Book book = entityManager
|
||||
.unwrap(Session.class)
|
||||
.byNaturalId(Book.class)
|
||||
.using(Map.of("productNumber", "973022823X", "publisher", publisher))
|
||||
.load();
|
||||
|
||||
assertEquals("High-Performance Java Persistence", book.getTitle());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -190,8 +190,8 @@ public class CompoundNaturalIdTests {
|
|||
final NaturalIdMultiLoadAccess<Account> loadAccess = session.byMultipleNaturalId( Account.class );
|
||||
loadAccess.enableOrderedReturn( false );
|
||||
final List<Account> accounts = loadAccess.multiLoad(
|
||||
NaturalIdMultiLoadAccess.compoundValue( "system", "matrix", "username", "neo" ),
|
||||
NaturalIdMultiLoadAccess.compoundValue( "system", "matrix", "username", "trinity" )
|
||||
Map.of( "system", "matrix", "username", "neo" ),
|
||||
Map.of( "system", "matrix", "username", "trinity" )
|
||||
);
|
||||
assertThat( accounts.size(), is( 2 ) );
|
||||
|
||||
|
|
Loading…
Reference in New Issue