From e43220e6d6a0cc356a583dd735b957d72d39badc Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 30 Apr 2025 16:40:00 +0200 Subject: [PATCH] [#2104] Create QueryProducer interface for the sessions --- .../org/hibernate/reactive/mutiny/Mutiny.java | 1193 +++++++--------- .../mutiny/impl/MutinySessionImpl.java | 31 +- .../impl/MutinyStatelessSessionImpl.java | 76 +- .../session/ReactiveQueryProducer.java | 5 +- .../org/hibernate/reactive/stage/Stage.java | 1239 +++++++---------- .../reactive/stage/impl/StageSessionImpl.java | 22 +- .../stage/impl/StageStatelessSessionImpl.java | 27 + 7 files changed, 1106 insertions(+), 1487 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index c5b0df7a9..7ced2785a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -485,715 +485,739 @@ default Query setLockMode(String alias, LockModeType lockModeType) { Query enableFetchProfile(String profileName); } - - /** - * A non-blocking counterpart to the Hibernate {@link org.hibernate.Session} - * interface, allowing a reactive style of interaction with the database. - *

- * The semantics of operations on this interface are identical to the - * semantics of the similarly-named operations of {@code Session}, except - * that the operations are performed asynchronously, returning a {@link Uni} - * without blocking the calling thread. - *

- * Entities associated with an {@code Session} do not support transparent - * lazy association fetching. Instead, {@link #fetch(Object)} should be used - * to explicitly request asynchronous fetching of an association, or the - * association should be fetched eagerly when the entity is first retrieved, - * for example, by: - * - *

    - *
  • {@link #enableFetchProfile(String) enabling a fetch profile}, - *
  • using an {@link EntityGraph}, or - *
  • writing a {@code join fetch} clause in a HQL query. - *
- * - * @see org.hibernate.Session - */ - interface Session extends Closeable { - + interface QueryProducer { /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, or {@code null} if there is no such - * persistent instance. If the instance is already associated with - * the session, return the associated instance. This method never - * returns an uninitialized instance. - * - *
-		 * {@code session.find(Book.class, id).map(book -> print(book.getTitle()));}
-		 * 
+ * Create an instance of {@link SelectionQuery} for the given HQL/JPQL + * query string. * - * @param entityClass The entity type - * @param id an identifier + * @param queryString The HQL/JPQL query * - * @return a persistent instance or null via a {@code Uni} + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#find(Class, Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - Uni find(Class entityClass, Object id); + SelectionQuery createSelectionQuery(String queryString, Class resultType); /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, requesting the given {@link LockMode}. + * Create a typed {@link org.hibernate.query.Query} instance for the given typed query reference. * - * @param entityClass The entity type - * @param id an identifier - * @param lockMode the requested {@link LockMode} + * @param typedQueryReference the type query reference * - * @return a persistent instance or null via a {@code Uni} + * @return The {@link org.hibernate.query.Query} instance for execution * - * @see #find(Class, Object) - * @see #lock(Object, LockMode) this discussion of lock modes + * @throws IllegalArgumentException if a query has not been + * defined with the name of the typed query reference or if + * the query result is found to not be assignable to + * result class of the typed query reference + * + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) */ - Uni find(Class entityClass, Object id, LockMode lockMode); + Query createQuery(TypedQueryReference typedQueryReference); /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, requesting the given {@link LockModeType}. + * Create an instance of {@link MutationQuery} for the given HQL/JPQL + * update or delete statement. * - * @param entityClass The entity type - * @param id an identifier - * @param lockModeType the requested {@link LockModeType} + * @param queryString The HQL/JPQL query, update or delete statement * - * @return a persistent instance or null via a {@code Uni} + * @return The {@link MutationQuery} instance for manipulation and execution * - * @see #find(Class, Object) - * @see #lock(Object, LockMode) this discussion of lock modes + * @see jakarta.persistence.EntityManager#createQuery(String) */ - default Uni find(Class entityClass, Object id, LockModeType lockModeType) { - return find( entityClass, id, convertToLockMode( lockModeType ) ); - } + MutationQuery createMutationQuery(String queryString); /** - * Asynchronously return the persistent instance with the given - * identifier of an entity class, using the given {@link EntityGraph} - * as a fetch plan. + * Create an instance of {@link MutationQuery} for the given update tree. * - * @param entityGraph an {@link EntityGraph} specifying the entity - * and associations to be fetched - * @param id an identifier + * @param updateQuery the update criteria query * - * @see #find(Class, Object) + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) */ - Uni find(EntityGraph entityGraph, Object id); + MutationQuery createMutationQuery(CriteriaUpdate updateQuery); /** - * Asynchronously return the persistent instances of the given entity - * class with the given identifiers, or null if there is no such - * persistent instance. + * Create an instance of {@link MutationQuery} for the given delete tree. * - * @param entityClass The entity type - * @param ids the identifiers + * @param deleteQuery the delete criteria query * - * @return a list of persistent instances and nulls via a {@code Uni} + * @return The {@link MutationQuery} instance for manipulation and execution * - * @see org.hibernate.Session#findMultiple(Class, List, FindOption...) + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) */ - Uni> find(Class entityClass, Object... ids); + MutationQuery createMutationQuery(CriteriaDelete deleteQuery); /** - * Asynchronously return the persistent instance of the given entity - * class with the given natural identifier, or null if there is no - * such persistent instance. + * Create a {@link MutationQuery} from the given insert select criteria tree * - * @param entityClass The entity type - * @param naturalId the natural identifier + * @param insert the insert select criteria query * - * @return a persistent instance or null via a {@code Uni} + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) */ - @Incubating - Uni find(Class entityClass, Identifier naturalId); + MutationQuery createMutationQuery(JpaCriteriaInsert insert); /** - * Return the persistent instance of the given entity class with the - * given identifier, assuming that the instance exists. This method - * never results in access to the underlying data store, and thus - * might return a proxied instance that must be initialized explicitly - * using {@link #fetch(Object)}. - *

- * You should not use this method to determine if an instance exists - * (use {@link #find} instead). Use this only to retrieve an instance - * which you safely assume exists, where non-existence would be an - * actual error. + * Create an instance of {@link Query} for the given HQL/JPQL query + * string or HQL/JPQL update or delete statement. In the case of an + * update or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. * - * @param entityClass a persistent class - * @param id a valid identifier of an existing persistent instance of the class + * @param queryString The HQL/JPQL query, update or delete statement * - * @return the persistent instance or proxy + * @return The {@link Query} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#getReference(Class, Object) + * @deprecated See explanation in + * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} + * + * @see jakarta.persistence.EntityManager#createQuery(String) */ - T getReference(Class entityClass, Object id); + @Deprecated + Query createQuery(String queryString); /** - * Return the persistent instance with the same identity as the given - * instance, which might be detached, assuming that the instance is - * still persistent in the database. This method never results in - * access to the underlying data store, and thus might return a proxy - * that must be initialized explicitly using {@link #fetch(Object)}. + * Create an instance of {@link SelectionQuery} for the given HQL/JPQL + * query string and query result type. * - * @param entity a detached persistent instance + * @param queryString The HQL/JPQL query + * @param resultType the Java type returned in each row of query results * - * @return the persistent instance or proxy + * @return The {@link SelectionQuery} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - T getReference(T entity); + SelectionQuery createQuery(String queryString, Class resultType); /** - * Asynchronously persist the given transient instance, first assigning - * a generated identifier. (Or using the current value of the identifier - * property if the entity has assigned identifiers.) - *

- * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + * Create an instance of {@link SelectionQuery} for the given criteria + * query. * - *

-		 * {@code session.persist(newBook).map(v -> session.flush());}
-		 * 
+ * @param criteriaQuery The {@link CriteriaQuery} * - * @param object a transient instance of a persistent class + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#persist(Object) + * @see jakarta.persistence.EntityManager#createQuery(String) */ - Uni persist(Object object); + SelectionQuery createQuery(CriteriaQuery criteriaQuery); /** - * Make a transient instance persistent and mark it for later insertion in the - * database. This operation cascades to associated instances if the association - * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. - *

- * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, - * {@code persist()} ultimately results in generation of an identifier for the - * given instance. But this may happen asynchronously, when the session is - * {@linkplain #flush() flushed}, depending on the identifier generation strategy. + * Create an instance of {@link MutationQuery} for the given criteria update. * - * @param entityName the entity name - * @param object a transient instance to be made persistent - * @see #persist(Object) - */ - Uni persist(String entityName, Object object); - - /** - * Persist multiple transient entity instances at once. + * @param criteriaUpdate The {@link CriteriaUpdate} * - * @see #persist(Object) + * @return The {@link MutationQuery} instance for manipulation and execution */ - Uni persistAll(Object... entities); + MutationQuery createQuery(CriteriaUpdate criteriaUpdate); /** - * Asynchronously remove a persistent instance from the datastore. The - * argument may be an instance associated with the receiving session or - * a transient instance with an identifier associated with existing - * persistent state. - *

- * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#REMOVE}. - * - *

-		 * {@code session.delete(book).thenAccept(v -> session.flush());}
-		 * 
+ * Create an instance of {@link MutationQuery} for the given criteria delete. * - * @param entity the managed persistent instance to be removed + * @param criteriaDelete The {@link CriteriaDelete} * - * @throws IllegalArgumentException if the given instance is not managed - * @see jakarta.persistence.EntityManager#remove(Object) + * @return The {@link MutationQuery} instance for manipulation and execution */ - Uni remove(Object entity); + MutationQuery createQuery(CriteriaDelete criteriaDelete); /** - * Remove multiple entity instances at once. + * Create an instance of {@link Query} for the named query. * - * @see #remove(Object) + * @param queryName The name of the query + * + * @return The {@link Query} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createQuery(String) */ - Uni removeAll(Object... entities); + Query createNamedQuery(String queryName); /** - * Copy the state of the given object onto the persistent instance with - * the same identifier. If there is no such persistent instance currently - * associated with the session, it will be loaded. Return the persistent - * instance. Or, if the given instance is transient, save a copy of it - * and return the copy as a newly persistent instance. The given instance - * does not become associated with the session. - *

- * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#MERGE}. + * Create an instance of {@link SelectionQuery} for the named query. * - * @param entity a detached instance with state to be copied + * @param queryName The name of the query + * @param resultType the Java type returned in each row of query results * - * @return an updated persistent instance + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#merge(Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - Uni merge(T entity); + SelectionQuery createNamedQuery(String queryName, Class resultType); /** - * Merge multiple entity instances at once. - * - * @see #merge(Object) - */ - Uni mergeAll(Object... entities); - - /** - * Re-read the state of the given instance from the underlying database. - * It is inadvisable to use this to implement long-running sessions that - * span many business tasks. This method is, however, useful in certain - * special circumstances, for example: + * Create an instance of {@link Query} for the given SQL query string, + * or SQL update, insert, or delete statement. In the case of an update, + * insert, or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. + * In the case of a query: * *

    - *
  • where a database trigger alters the object state after insert or - * update, or - *
  • after executing direct native SQL in the same session. + *
  • If the result set has a single column, the results will be returned + * as scalars.
  • + *
  • Otherwise, if the result set has multiple columns, the results will + * be returned as elements of arrays of type {@code Object[]}.
  • *
* - * @param entity a managed persistent instance - * - * @throws IllegalArgumentException if the given instance is not managed - * @see jakarta.persistence.EntityManager#refresh(Object) + * @param queryString The SQL select, update, insert, or delete statement */ - Uni refresh(Object entity); + Query createNativeQuery(String queryString); /** - * Re-read the state of the given instance from the underlying database, - * requesting the given {@link LockMode}. + * Create an instance of {@link Query} for the given SQL query string, + * or SQL update, insert, or delete statement. In the case of an update, + * insert, or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. + * In the case of a query: * - * @param entity a managed persistent entity instance - * @param lockMode the requested lock mode + *
    + *
  • If the result set has a single column, the results will be returned + * as scalars.
  • + *
  • Otherwise, if the result set has multiple columns, the results will + * be returned as elements of arrays of type {@code Object[]}.
  • + *
+ *

+ * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the statement. * - * @see #refresh(Object) + * @param queryString The SQL select, update, insert, or delete statement + * @param affectedEntities The entities which are affected by the statement */ - Uni refresh(Object entity, LockMode lockMode); + Query createNativeQuery(String queryString, AffectedEntities affectedEntities); /** - * Re-read the state of the given instance from the underlying database, - * requesting the given {@link LockModeType}. + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@code resultType} to interpret the results. * - * @param entity a managed persistent entity instance - * @param lockModeType the requested lock mode + *

    + *
  • If the given result type is {@link Object}, or a built-in type + * such as {@link String} or {@link Integer}, the result set must + * have a single column, which will be returned as a scalar. + *
  • If the given result type is {@code Object[]}, then the result set + * must have multiple columns, which will be returned in arrays. + *
  • Otherwise, the given result type must be an entity class, in which + * case the result set column aliases must map to the fields of the + * entity, and the query will return instances of the entity. + *
* - * @see #refresh(Object) - */ - default Uni refresh(Object entity, LockModeType lockModeType) { - return refresh( entity, convertToLockMode( lockModeType ) ); - } - - /** - * Refresh multiple entity instances at once. + * @param queryString The SQL query + * @param resultType the Java type returned in each row of query results * - * @see #refresh(Object) + * @return The {@link SelectionQuery} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) */ - Uni refreshAll(Object... entities); + SelectionQuery createNativeQuery(String queryString, Class resultType); /** - * Obtain the specified lock level upon the given object. For example, - * this operation may be used to: + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@code resultType} to interpret the results. * *
    - *
  • perform a version check with {@link LockMode#PESSIMISTIC_READ}, - *
  • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}, - *
  • force a version increment with {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}, - *
  • schedule a version check just before the end of the transaction with - * {@link LockMode#OPTIMISTIC}, or - *
  • schedule a version increment just before the end of the transaction - * with {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}. + *
  • If the given result type is {@link Object}, or a built-in type + * such as {@link String} or {@link Integer}, the result set must + * have a single column, which will be returned as a scalar. + *
  • If the given result type is {@code Object[]}, then the result set + * must have multiple columns, which will be returned in arrays. + *
  • Otherwise, the given result type must be an entity class, in which + * case the result set column aliases must map to the fields of the + * entity, and the query will return instances of the entity. *
*

- * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. + * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the query. * - * @param entity a managed persistent instance - * @param lockMode the lock level + * @param queryString The SQL query + * @param resultType the Java type returned in each row of query results + * @param affectedEntities The entities which are affected by the query * - * @throws IllegalArgumentException if the given instance is not managed + * @return The {@link Query} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) */ - Uni lock(Object entity, LockMode lockMode); + SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities); /** - * Obtain the specified lock level upon the given object. For example, - * this operation may be used to: + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@link ResultSetMapping} to interpret the + * result set. * - *

    - *
  • perform a version check with {@link LockModeType#PESSIMISTIC_READ}, - *
  • upgrade to a pessimistic lock with {@link LockModeType#PESSIMISTIC_WRITE}, - *
  • force a version increment with {@link LockModeType#PESSIMISTIC_FORCE_INCREMENT}, - *
  • schedule a version check just before the end of the transaction with - * {@link LockModeType#OPTIMISTIC}, or - *
  • schedule a version increment just before the end of the transaction - * with {@link LockModeType#OPTIMISTIC_FORCE_INCREMENT}. - *
- *

- * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. + * @param queryString The SQL query + * @param resultSetMapping the result set mapping * - * @param entity a managed persistent instance - * @param lockModeType the lock level + * @return The {@link Query} instance for manipulation and execution * - * @throws IllegalArgumentException if the given instance is not managed + * @see #getResultSetMapping(Class, String) + * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) */ - default Uni lock(Object entity, LockModeType lockModeType) { - return lock( entity, convertToLockMode( lockModeType ) ); - } + SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); /** - * Force this session to flush asynchronously. Must be called at the - * end of a unit of work, before committing the transaction and closing - * the session. Flushing is the process of synchronizing the - * underlying persistent store with state held in memory. + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@link ResultSetMapping} to interpret the + * result set. + *

+ * Any {@link AffectedEntities affected entities} are synchronized with the + * database before execution of the query. * - *

-		 * {@code session.flush().thenAccept(v -> print("done saving changes"));}
-		 * 
+ * @param queryString The SQL query + * @param resultSetMapping the result set mapping + * @param affectedEntities The entities which are affected by the query * - * @see jakarta.persistence.EntityManager#flush() + * @return The {@link SelectionQuery} instance for manipulation and execution + * + * @see #getResultSetMapping(Class, String) + * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) */ - Uni flush(); + SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities); /** - * Asynchronously fetch an association configured for lazy loading. - *

- *

-		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()));}
-		 * 
- *

- *

- * This operation may be even be used to initialize a reference returned by - * {@link #getReference(Class, Object)}. - *

- *

-		 * {@code session.fetch(session.getReference(Author.class, authorId))}
-		 * 
- *

- * @param association a lazy-loaded association, or a proxy - * - * @return the fetched association, via a {@code Uni} - * - * @see Mutiny#fetch(Object) - * @see #getReference(Class, Object) - * @see org.hibernate.Hibernate#initialize(Object) + * Obtain a native SQL result set mapping defined via the annotation + * {@link jakarta.persistence.SqlResultSetMapping}. */ - Uni fetch(T association); + ResultSetMapping getResultSetMapping(Class resultType, String mappingName); /** - * Fetch a lazy property of the given entity, identified by a JPA - * {@link Attribute attribute metamodel}. Note that this feature is - * only supported in conjunction with the Hibernate bytecode enhancer. + * Obtain a named {@link EntityGraph} + */ + EntityGraph getEntityGraph(Class rootType, String graphName); + + /** + * Create a new mutable {@link EntityGraph} + */ + EntityGraph createEntityGraph(Class rootType); + + /** + * Create a new mutable copy of a named {@link EntityGraph} + */ + EntityGraph createEntityGraph(Class rootType, String graphName); + + /** + * Convenience method to obtain the {@link CriteriaBuilder}. * - *
-		 * {@code session.fetch(book, Book_.isbn).thenAccept(isbn -> print(isbn))}
-		 * 
+ * @since 3 */ - Uni fetch(E entity, Attribute field); + CriteriaBuilder getCriteriaBuilder(); + } + + /** + * A non-blocking counterpart to the Hibernate {@link org.hibernate.Session} + * interface, allowing a reactive style of interaction with the database. + *

+ * The semantics of operations on this interface are identical to the + * semantics of the similarly-named operations of {@code Session}, except + * that the operations are performed asynchronously, returning a {@link Uni} + * without blocking the calling thread. + *

+ * Entities associated with an {@code Session} do not support transparent + * lazy association fetching. Instead, {@link #fetch(Object)} should be used + * to explicitly request asynchronous fetching of an association, or the + * association should be fetched eagerly when the entity is first retrieved, + * for example, by: + * + *

    + *
  • {@link #enableFetchProfile(String) enabling a fetch profile}, + *
  • using an {@link EntityGraph}, or + *
  • writing a {@code join fetch} clause in a HQL query. + *
+ * + * @see org.hibernate.Session + */ + interface Session extends QueryProducer, Closeable { /** - * Asynchronously fetch an association that's configured for lazy loading, - * and unwrap the underlying entity implementation from any proxy. + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, or {@code null} if there is no such + * persistent instance. If the instance is already associated with + * the session, return the associated instance. This method never + * returns an uninitialized instance. * *
-		 * {@code session.unproxy(author.getBook()).thenAccept(book -> print(book.getTitle()));}
+		 * {@code session.find(Book.class, id).map(book -> print(book.getTitle()));}
 		 * 
* - * @param association a lazy-loaded association + * @param entityClass The entity type + * @param id an identifier * - * @return the fetched association, via a {@code Uni} + * @return a persistent instance or null via a {@code Uni} * - * @see org.hibernate.Hibernate#unproxy(Object) + * @see jakarta.persistence.EntityManager#find(Class, Object) */ - Uni unproxy(T association); + Uni find(Class entityClass, Object id); /** - * Determine the current lock mode of the given entity. + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, requesting the given {@link LockMode}. + * + * @param entityClass The entity type + * @param id an identifier + * @param lockMode the requested {@link LockMode} + * + * @return a persistent instance or null via a {@code Uni} + * + * @see #find(Class, Object) + * @see #lock(Object, LockMode) this discussion of lock modes */ - LockMode getLockMode(Object entity); + Uni find(Class entityClass, Object id, LockMode lockMode); /** - * Determine if the given instance belongs to this persistence context. + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, requesting the given {@link LockModeType}. + * + * @param entityClass The entity type + * @param id an identifier + * @param lockModeType the requested {@link LockModeType} + * + * @return a persistent instance or null via a {@code Uni} + * + * @see #find(Class, Object) + * @see #lock(Object, LockMode) this discussion of lock modes */ - boolean contains(Object entity); + default Uni find(Class entityClass, Object id, LockModeType lockModeType) { + return find( entityClass, id, convertToLockMode( lockModeType ) ); + } /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string. - * - * @param queryString The HQL/JPQL query + * Asynchronously return the persistent instance with the given + * identifier of an entity class, using the given {@link EntityGraph} + * as a fetch plan. * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entityGraph an {@link EntityGraph} specifying the entity + * and associations to be fetched + * @param id an identifier * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #find(Class, Object) */ - SelectionQuery createSelectionQuery(String queryString, Class resultType); + Uni find(EntityGraph entityGraph, Object id); /** - * Create an instance of {@link MutationQuery} for the given HQL/JPQL - * update or delete statement. + * Asynchronously return the persistent instances of the given entity + * class with the given identifiers, or null if there is no such + * persistent instance. * - * @param queryString The HQL/JPQL query, update or delete statement + * @param entityClass The entity type + * @param ids the identifiers * - * @return The {@link MutationQuery} instance for manipulation and execution + * @return a list of persistent instances and nulls via a {@code Uni} * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see org.hibernate.Session#findMultiple(Class, List, FindOption...) */ - MutationQuery createMutationQuery(String queryString); + Uni> find(Class entityClass, Object... ids); /** - * Create an instance of {@link MutationQuery} for the given update tree. - * - * @param updateQuery the update criteria query + * Asynchronously return the persistent instance of the given entity + * class with the given natural identifier, or null if there is no + * such persistent instance. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entityClass The entity type + * @param naturalId the natural identifier * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) + * @return a persistent instance or null via a {@code Uni} */ - MutationQuery createMutationQuery(CriteriaUpdate updateQuery); + @Incubating + Uni find(Class entityClass, Identifier naturalId); /** - * Create an instance of {@link MutationQuery} for the given delete tree. + * Return the persistent instance of the given entity class with the + * given identifier, assuming that the instance exists. This method + * never results in access to the underlying data store, and thus + * might return a proxied instance that must be initialized explicitly + * using {@link #fetch(Object)}. + *

+ * You should not use this method to determine if an instance exists + * (use {@link #find} instead). Use this only to retrieve an instance + * which you safely assume exists, where non-existence would be an + * actual error. * - * @param deleteQuery the delete criteria query + * @param entityClass a persistent class + * @param id a valid identifier of an existing persistent instance of the class * - * @return The {@link MutationQuery} instance for manipulation and execution + * @return the persistent instance or proxy * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) + * @see jakarta.persistence.EntityManager#getReference(Class, Object) */ - MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + T getReference(Class entityClass, Object id); /** - * Create a {@link MutationQuery} from the given insert select criteria tree - * - * @param insert the insert select criteria query + * Return the persistent instance with the same identity as the given + * instance, which might be detached, assuming that the instance is + * still persistent in the database. This method never results in + * access to the underlying data store, and thus might return a proxy + * that must be initialized explicitly using {@link #fetch(Object)}. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a detached persistent instance * - * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) + * @return the persistent instance or proxy */ - MutationQuery createMutationQuery(JpaCriteriaInsert insert); + T getReference(T entity); /** - * Create a typed {@link org.hibernate.query.Query} instance for the given typed query reference. + * Asynchronously persist the given transient instance, first assigning + * a generated identifier. (Or using the current value of the identifier + * property if the entity has assigned identifiers.) + *

+ * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#PERSIST}. * - * @param typedQueryReference the type query reference + *

+		 * {@code session.persist(newBook).map(v -> session.flush());}
+		 * 
* - * @return The {@link org.hibernate.query.Query} instance for execution + * @param object a transient instance of a persistent class * - * @throws IllegalArgumentException if a query has not been - * defined with the name of the typed query reference or if - * the query result is found to not be assignable to - * result class of the typed query reference + * @see jakarta.persistence.EntityManager#persist(Object) + */ + Uni persist(Object object); + + /** + * Make a transient instance persistent and mark it for later insertion in the + * database. This operation cascades to associated instances if the association + * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + *

+ * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, + * {@code persist()} ultimately results in generation of an identifier for the + * given instance. But this may happen asynchronously, when the session is + * {@linkplain #flush() flushed}, depending on the identifier generation strategy. * - * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + * @param entityName the entity name + * @param object a transient instance to be made persistent + * @see #persist(Object) */ - Query createQuery(TypedQueryReference typedQueryReference); + Uni persist(String entityName, Object object); /** - * Create an instance of {@link Query} for the given HQL/JPQL query - * string or HQL/JPQL update or delete statement. In the case of an - * update or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. + * Persist multiple transient entity instances at once. * - * @param queryString The HQL/JPQL query, update or delete statement + * @see #persist(Object) + */ + Uni persistAll(Object... entities); + + /** + * Asynchronously remove a persistent instance from the datastore. The + * argument may be an instance associated with the receiving session or + * a transient instance with an identifier associated with existing + * persistent state. + *

+ * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#REMOVE}. * - * @return The {@link Query} instance for manipulation and execution + *

+		 * {@code session.delete(book).thenAccept(v -> session.flush());}
+		 * 
* - * @deprecated See explanation in - * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} + * @param entity the managed persistent instance to be removed * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @throws IllegalArgumentException if the given instance is not managed + * @see jakarta.persistence.EntityManager#remove(Object) */ - @Deprecated - Query createQuery(String queryString); + Uni remove(Object entity); /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string and query result type. - * - * @param queryString The HQL/JPQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link SelectionQuery} instance for manipulation and execution + * Remove multiple entity instances at once. * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #remove(Object) */ - SelectionQuery createQuery(String queryString, Class resultType); + Uni removeAll(Object... entities); /** - * Create an instance of {@link SelectionQuery} for the given criteria - * query. + * Copy the state of the given object onto the persistent instance with + * the same identifier. If there is no such persistent instance currently + * associated with the session, it will be loaded. Return the persistent + * instance. Or, if the given instance is transient, save a copy of it + * and return the copy as a newly persistent instance. The given instance + * does not become associated with the session. + *

+ * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#MERGE}. * - * @param criteriaQuery The {@link CriteriaQuery} + * @param entity a detached instance with state to be copied * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @return an updated persistent instance * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see jakarta.persistence.EntityManager#merge(Object) */ - SelectionQuery createQuery(CriteriaQuery criteriaQuery); + Uni merge(T entity); /** - * Create an instance of {@link MutationQuery} for the given criteria update. - * - * @param criteriaUpdate The {@link CriteriaUpdate} + * Merge multiple entity instances at once. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @see #merge(Object) */ - MutationQuery createQuery(CriteriaUpdate criteriaUpdate); + Uni mergeAll(Object... entities); /** - * Create an instance of {@link MutationQuery} for the given criteria delete. + * Re-read the state of the given instance from the underlying database. + * It is inadvisable to use this to implement long-running sessions that + * span many business tasks. This method is, however, useful in certain + * special circumstances, for example: * - * @param criteriaDelete The {@link CriteriaDelete} + *

    + *
  • where a database trigger alters the object state after insert or + * update, or + *
  • after executing direct native SQL in the same session. + *
* - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a managed persistent instance + * + * @throws IllegalArgumentException if the given instance is not managed + * @see jakarta.persistence.EntityManager#refresh(Object) */ - MutationQuery createQuery(CriteriaDelete criteriaDelete); + Uni refresh(Object entity); /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query + * Re-read the state of the given instance from the underlying database, + * requesting the given {@link LockMode}. * - * @return The {@link Query} instance for manipulation and execution + * @param entity a managed persistent entity instance + * @param lockMode the requested lock mode * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see #refresh(Object) */ - Query createNamedQuery(String queryName); + Uni refresh(Object entity, LockMode lockMode); /** - * Create an instance of {@link SelectionQuery} for the named query. - * - * @param queryName The name of the query - * @param resultType the Java type returned in each row of query results + * Re-read the state of the given instance from the underlying database, + * requesting the given {@link LockModeType}. * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entity a managed persistent entity instance + * @param lockModeType the requested lock mode * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #refresh(Object) */ - SelectionQuery createNamedQuery(String queryName, Class resultType); + default Uni refresh(Object entity, LockModeType lockModeType) { + return refresh( entity, convertToLockMode( lockModeType ) ); + } /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * In the case of a query: - * - *
    - *
  • If the result set has a single column, the results will be returned - * as scalars.
  • - *
  • Otherwise, if the result set has multiple columns, the results will - * be returned as elements of arrays of type {@code Object[]}.
  • - *
+ * Refresh multiple entity instances at once. * - * @param queryString The SQL select, update, insert, or delete statement + * @see #refresh(Object) */ - Query createNativeQuery(String queryString); + Uni refreshAll(Object... entities); /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * In the case of a query: + * Obtain the specified lock level upon the given object. For example, + * this operation may be used to: * *
    - *
  • If the result set has a single column, the results will be returned - * as scalars.
  • - *
  • Otherwise, if the result set has multiple columns, the results will - * be returned as elements of arrays of type {@code Object[]}.
  • + *
  • perform a version check with {@link LockMode#PESSIMISTIC_READ}, + *
  • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}, + *
  • force a version increment with {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}, + *
  • schedule a version check just before the end of the transaction with + * {@link LockMode#OPTIMISTIC}, or + *
  • schedule a version increment just before the end of the transaction + * with {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}. *
*

- * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the statement. + * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. * - * @param queryString The SQL select, update, insert, or delete statement - * @param affectedEntities The entities which are affected by the statement + * @param entity a managed persistent instance + * @param lockMode the lock level + * + * @throws IllegalArgumentException if the given instance is not managed */ - Query createNativeQuery(String queryString, AffectedEntities affectedEntities); + Uni lock(Object entity, LockMode lockMode); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@code resultType} to interpret the results. + * Obtain the specified lock level upon the given object. For example, + * this operation may be used to: * *

    - *
  • If the given result type is {@link Object}, or a built-in type - * such as {@link String} or {@link Integer}, the result set must - * have a single column, which will be returned as a scalar. - *
  • If the given result type is {@code Object[]}, then the result set - * must have multiple columns, which will be returned in arrays. - *
  • Otherwise, the given result type must be an entity class, in which - * case the result set column aliases must map to the fields of the - * entity, and the query will return instances of the entity. + *
  • perform a version check with {@link LockModeType#PESSIMISTIC_READ}, + *
  • upgrade to a pessimistic lock with {@link LockModeType#PESSIMISTIC_WRITE}, + *
  • force a version increment with {@link LockModeType#PESSIMISTIC_FORCE_INCREMENT}, + *
  • schedule a version check just before the end of the transaction with + * {@link LockModeType#OPTIMISTIC}, or + *
  • schedule a version increment just before the end of the transaction + * with {@link LockModeType#OPTIMISTIC_FORCE_INCREMENT}. *
+ *

+ * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entity a managed persistent instance + * @param lockModeType the lock level * - * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) + * @throws IllegalArgumentException if the given instance is not managed */ - SelectionQuery createNativeQuery(String queryString, Class resultType); + default Uni lock(Object entity, LockModeType lockModeType) { + return lock( entity, convertToLockMode( lockModeType ) ); + } /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@code resultType} to interpret the results. + * Force this session to flush asynchronously. Must be called at the + * end of a unit of work, before committing the transaction and closing + * the session. Flushing is the process of synchronizing the + * underlying persistent store with state held in memory. * - *

    - *
  • If the given result type is {@link Object}, or a built-in type - * such as {@link String} or {@link Integer}, the result set must - * have a single column, which will be returned as a scalar. - *
  • If the given result type is {@code Object[]}, then the result set - * must have multiple columns, which will be returned in arrays. - *
  • Otherwise, the given result type must be an entity class, in which - * case the result set column aliases must map to the fields of the - * entity, and the query will return instances of the entity. - *
+ *
+		 * {@code session.flush().thenAccept(v -> print("done saving changes"));}
+		 * 
+ * + * @see jakarta.persistence.EntityManager#flush() + */ + Uni flush(); + + /** + * Asynchronously fetch an association configured for lazy loading. *

- * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the query. + *

+		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()));}
+		 * 
+ *

+ *

+ * This operation may be even be used to initialize a reference returned by + * {@link #getReference(Class, Object)}. + *

+ *

+		 * {@code session.fetch(session.getReference(Author.class, authorId))}
+		 * 
+ *

+ * @param association a lazy-loaded association, or a proxy * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * @param affectedEntities The entities which are affected by the query + * @return the fetched association, via a {@code Uni} * - * @return The {@link Query} instance for manipulation and execution + * @see Mutiny#fetch(Object) + * @see #getReference(Class, Object) + * @see org.hibernate.Hibernate#initialize(Object) + */ + Uni fetch(T association); + + /** + * Fetch a lazy property of the given entity, identified by a JPA + * {@link Attribute attribute metamodel}. Note that this feature is + * only supported in conjunction with the Hibernate bytecode enhancer. * - * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) + *
+		 * {@code session.fetch(book, Book_.isbn).thenAccept(isbn -> print(isbn))}
+		 * 
*/ - SelectionQuery createNativeQuery( - String queryString, Class resultType, - AffectedEntities affectedEntities); + Uni fetch(E entity, Attribute field); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. + * Asynchronously fetch an association that's configured for lazy loading, + * and unwrap the underlying entity implementation from any proxy. * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping + *
+		 * {@code session.unproxy(author.getBook()).thenAccept(book -> print(book.getTitle()));}
+		 * 
* - * @return The {@link Query} instance for manipulation and execution + * @param association a lazy-loaded association * - * @see #getResultSetMapping(Class, String) - * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) + * @return the fetched association, via a {@code Uni} + * + * @see org.hibernate.Hibernate#unproxy(Object) */ - SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); + Uni unproxy(T association); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. - *

- * Any {@link AffectedEntities affected entities} are synchronized with the - * database before execution of the query. - * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping - * @param affectedEntities The entities which are affected by the query - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see #getResultSetMapping(Class, String) - * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) + * Determine the current lock mode of the given entity. + */ + LockMode getLockMode(Object entity); + + /** + * Determine if the given instance belongs to this persistence context. */ - SelectionQuery createNativeQuery( - String queryString, - ResultSetMapping resultSetMapping, - AffectedEntities affectedEntities); + boolean contains(Object entity); /** * Set the {@link FlushMode flush mode} for this session. @@ -1265,27 +1289,6 @@ default Session setFlushMode(FlushModeType flushModeType) { */ Session enableFetchProfile(String name); - /** - * Obtain a native SQL result set mapping defined via the annotation - * {@link jakarta.persistence.SqlResultSetMapping}. - */ - ResultSetMapping getResultSetMapping(Class resultType, String mappingName); - - /** - * Obtain a named {@link EntityGraph} - */ - EntityGraph getEntityGraph(Class rootType, String graphName); - - /** - * Create a new mutable {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType); - - /** - * Create a new mutable copy of a named {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType, String graphName); - /** * Disable a particular fetch profile on this session, or do nothing if * the requested fetch profile is not enabled. @@ -1511,15 +1514,6 @@ default Session setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); - - /** - * Convenience method to obtain the {@link CriteriaBuilder}. - * - * @since 3 - */ - default CriteriaBuilder getCriteriaBuilder() { - return getFactory().getCriteriaBuilder(); - } } /** @@ -1552,7 +1546,7 @@ default CriteriaBuilder getCriteriaBuilder() { * * @see org.hibernate.StatelessSession */ - interface StatelessSession extends Closeable { + interface StatelessSession extends QueryProducer, Closeable { /** * Retrieve a row. @@ -1617,169 +1611,6 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp */ Uni get(EntityGraph entityGraph, Object id); - /** - * Create an instance of {@link Query} for the given HQL/JPQL query - * string or HQL/JPQL update or delete statement. In the case of an - * update or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The HQL/JPQL query, update or delete statement - * - * @return The {@link Query} instance for manipulation and execution - * - * @deprecated See explanation in - * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} - * - * @see Session#createQuery(String) - */ - @Deprecated - Query createQuery(String queryString); - - /** - * Create a typed {@link Query} instance for the given typed query reference. - * - * @param typedQueryReference the type query reference - * - * @return The {@link Query} instance for execution - * - * @throws IllegalArgumentException if a query has not been - * defined with the name of the typed query reference or if - * the query result is found to not be assignable to - * result class of the typed query reference - * - * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) - */ - Query createQuery(TypedQueryReference typedQueryReference); - - /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string and query result type. - * - * @param queryString The HQL/JPQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see Session#createQuery(String, Class) - */ - SelectionQuery createQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string. - * - * @param queryString The HQL/JPQL query - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) - */ - SelectionQuery createSelectionQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link MutationQuery} for the given HQL/JPQL - * update or delete statement. - * - * @param queryString The HQL/JPQL query, update or delete statement - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - MutationQuery createMutationQuery(String queryString); - - /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query - * - * @return The {@link Query} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - Query createNamedQuery(String queryName); - - /** - * Create an instance of {@link SelectionQuery} for the named query. - * - * @param queryName The name of the query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) - */ - SelectionQuery createNamedQuery(String queryName, Class resultType); - - /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The SQL select, update, insert, or delete statement - * - * @see Session#createNativeQuery(String) - */ - Query createNativeQuery(String queryString); - - /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@code resultType} to interpret the results. - * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see Session#createNativeQuery(String, Class) - */ - SelectionQuery createNativeQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. - * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping - * - * @return The {@link Query} instance for manipulation and execution - * - * @see Session#createNativeQuery(String, ResultSetMapping) - */ - SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); - - /** - * Create an instance of {@link SelectionQuery} for the given criteria - * query. - * - * @param criteriaQuery The {@link CriteriaQuery} - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - SelectionQuery createQuery(CriteriaQuery criteriaQuery); - - /** - * Create an instance of {@link MutationQuery} for the given criteria update. - * - * @param criteriaUpdate The {@link CriteriaUpdate} - * - * @return The {@link MutationQuery} instance for manipulation and execution - */ - MutationQuery createQuery(CriteriaUpdate criteriaUpdate); - - /** - * Create an instance of {@link MutationQuery} for the given criteria delete. - * - * @param criteriaDelete The {@link CriteriaDelete} - * - * @return The {@link MutationQuery} instance for manipulation and execution - */ - MutationQuery createQuery(CriteriaDelete criteriaDelete); - /** * Insert a row. * @@ -2046,27 +1877,6 @@ default Uni refresh(Object entity, LockModeType lockModeType) { */ Object getIdentifier(Object entity); - /** - * Obtain a native SQL result set mapping defined via the annotation - * {@link jakarta.persistence.SqlResultSetMapping}. - */ - ResultSetMapping getResultSetMapping(Class resultType, String mappingName); - - /** - * Obtain a named {@link EntityGraph} - */ - EntityGraph getEntityGraph(Class rootType, String graphName); - - /** - * Create a new mutable {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType); - - /** - * Create a new mutable copy of a named {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType, String graphName); - /** * Performs the given work within the scope of a database transaction, * automatically flushing the session. The transaction will be rolled @@ -2115,15 +1925,6 @@ default Uni refresh(Object entity, LockModeType lockModeType) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); - - /** - * Convenience method to obtain the {@link CriteriaBuilder}. - * - * @since 3 - */ - default CriteriaBuilder getCriteriaBuilder() { - return getFactory().getCriteriaBuilder(); - } } /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index ab57ee718..8a4149104 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -5,18 +5,6 @@ */ package org.hibernate.reactive.mutiny.impl; -import io.smallrye.mutiny.Uni; -import jakarta.persistence.CacheRetrieveMode; -import jakarta.persistence.CacheStoreMode; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.LockModeType; -import jakarta.persistence.PersistenceException; -import jakarta.persistence.TypedQueryReference; -import jakarta.persistence.criteria.CriteriaDelete; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.CriteriaUpdate; -import jakarta.persistence.metamodel.Attribute; import org.hibernate.CacheMode; import org.hibernate.Filter; import org.hibernate.FlushMode; @@ -40,6 +28,19 @@ import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.session.ReactiveSession; +import io.smallrye.mutiny.Uni; +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.PersistenceException; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.metamodel.Attribute; import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.CompletionStage; @@ -48,7 +49,6 @@ import static org.hibernate.reactive.util.impl.CompletionStages.applyToAll; - /** * Implements the {@link Mutiny.Session} API. This delegating class is * needed to avoid name clashes when implementing both @@ -578,6 +578,11 @@ public Mutiny.SessionFactory getFactory() { return factory; } + @Override + public CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } + @Override public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { return delegate.getResultSetMapping( resultType, mappingName ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index cb7db4a8e..f5a6352c1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -5,14 +5,10 @@ */ package org.hibernate.reactive.mutiny.impl; -import io.smallrye.mutiny.Uni; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.TypedQueryReference; -import jakarta.persistence.criteria.CriteriaDelete; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.CriteriaUpdate; import org.hibernate.LockMode; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.query.criteria.JpaCriteriaInsert; +import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny.Query; @@ -21,13 +17,19 @@ import org.hibernate.reactive.query.ReactiveQuery; import org.hibernate.reactive.session.ReactiveStatelessSession; +import io.smallrye.mutiny.Uni; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Function; import java.util.function.Supplier; - /** * Implements the {@link Mutiny.StatelessSession} API. This delegating * class is needed to avoid name clashes when implementing both @@ -47,7 +49,6 @@ public ReactiveConnection getReactiveConnection() { return delegate.getReactiveConnection(); } - Uni uni(Supplier> stageSupplier) { return factory.uni( stageSupplier ); } @@ -91,12 +92,42 @@ public SelectionQuery createQuery(String queryString, Class resultType @Override public SelectionQuery createSelectionQuery(String queryString, Class resultType) { - return new MutinySelectionQueryImpl<>( delegate.createReactiveSelectionQuery( queryString, resultType), factory ); + return new MutinySelectionQueryImpl<>( delegate.createReactiveSelectionQuery( queryString, resultType ), factory ); } @Override public Mutiny.MutationQuery createMutationQuery(String queryString) { - return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( queryString), factory ); + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( queryString ), factory ); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(CriteriaUpdate updateQuery) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( updateQuery ), factory ); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( deleteQuery ) , factory ); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(JpaCriteriaInsert insert) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( insert ) , factory ); + } + + @Override + public Query createNativeQuery(String queryString, AffectedEntities affectedEntities) { + return new MutinyQueryImpl<>( delegate.createReactiveNativeQuery( queryString, affectedEntities ), factory ); + } + + @Override + public SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities) { + return new MutinyQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultType, affectedEntities ), factory ); + } + + @Override + public SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities) { + return new MutinyQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultSetMapping, affectedEntities ), factory ); } @Override @@ -259,26 +290,6 @@ public Object getIdentifier(Object entity) { return delegate.getIdentifier(entity); } -// @Override -// public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { -// return delegate.getResultSetMapping( resultType, mappingName ); -// } -// -// @Override -// public EntityGraph getEntityGraph(Class entity, String name) { -// return delegate.getEntityGraph( entity, name ); -// } -// -// @Override -// public EntityGraph createEntityGraph(Class entity) { -// return delegate.createEntityGraph( entity ); -// } -// -// @Override -// public EntityGraph createEntityGraph(Class entity, String name) { -// return delegate.createEntityGraph( entity, name ); -// } - @Override public Uni withTransaction(Function> work) { return currentTransaction == null ? new Transaction().execute( work ) : work.apply( currentTransaction ); @@ -359,6 +370,11 @@ public MutinySessionFactoryImpl getFactory() { return factory; } + @Override + public CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } + @Override public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { return delegate.getResultSetMapping( resultType, mappingName ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java index c9a7cc11b..4169d479d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java @@ -5,9 +5,6 @@ */ package org.hibernate.reactive.session; -import jakarta.persistence.TypedQueryReference; -import java.util.concurrent.CompletionStage; - import org.hibernate.Incubating; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -22,9 +19,11 @@ import org.hibernate.reactive.query.ReactiveSelectionQuery; import jakarta.persistence.EntityGraph; +import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; +import java.util.concurrent.CompletionStage; /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 6de95c154..7085ef861 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -26,7 +26,6 @@ import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.Page; -import org.hibernate.query.Query; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; @@ -484,740 +483,744 @@ default Query setLockMode(String alias, LockModeType lockModeType) { Query setComment(String comment); } - /** - * A non-blocking counterpart to the Hibernate {@link org.hibernate.Session} - * interface, allowing a reactive style of interaction with the database. - *

- * The semantics of operations on this interface are identical to the - * semantics of the similarly-named operations of {@code Session}, except - * that the operations are performed asynchronously, returning a - * {@link CompletionStage} without blocking the calling thread. - *

- * Entities associated with an {@code Session} do not support transparent - * lazy association fetching. Instead, {@link #fetch(Object)} should be used - * to explicitly request asynchronous fetching of an association, or the - * association should be fetched eagerly when the entity is first retrieved, - * for example, by: - * - *

    - *
  • {@link #enableFetchProfile(String) enabling a fetch profile}, - *
  • using an {@link EntityGraph}, or - *
  • writing a {@code join fetch} clause in a HQL query. - *
- * - * @see org.hibernate.Session - */ - interface Session extends Closeable { - + interface QueryProducer { /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, or {@code null} if there is no such - * persistent instance. If the instance is already associated with - * the session, return the associated instance. This method never - * returns an uninitialized instance. - * - *
-		 * {@code session.find(Book.class, id).thenAccept(book -> print(book.getTitle()));}
-		 * 
+ * Create an instance of {@link SelectionQuery} for the given HQL/JPQL + * query string. * - * @param entityClass The entity type - * @param id an identifier + * @param queryString The HQL/JPQL query * - * @return a persistent instance or null via a {@code CompletionStage} + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#find(Class, Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - CompletionStage find(Class entityClass, Object id); + SelectionQuery createSelectionQuery(String queryString, Class resultType); /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, requesting the given {@link LockMode}. + * Create an instance of {@link MutationQuery} for the given HQL/JPQL + * update or delete statement. * - * @param entityClass The entity type - * @param id an identifier - * @param lockMode the requested {@link LockMode} + * @param queryString The HQL/JPQL query, update or delete statement * - * @return a persistent instance or null via a {@code CompletionStage} + * @return The {@link MutationQuery} instance for manipulation and execution * - * @see #find(Class,Object) - * @see #lock(Object, LockMode) this discussion of lock modes + * @see jakarta.persistence.EntityManager#createQuery(String) */ - CompletionStage find(Class entityClass, Object id, LockMode lockMode); + MutationQuery createMutationQuery(String queryString); /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, requesting the given {@link LockModeType}. - * - * @param entityClass The entity type - * @param id an identifier - * @param lockModeType the requested {@link LockModeType} - * - * @return a persistent instance or null via a {@code CompletionStage} + * Create an instance of {@link MutationQuery} for the given update tree. * - * @see #find(Class,Object) - * @see #lock(Object, LockMode) this discussion of lock modes - */ - default CompletionStage find(Class entityClass, Object id, LockModeType lockModeType) { - return find( entityClass, id, convertToLockMode(lockModeType) ); - } - -// /** -// * Asynchronously return the persistent instance of the given entity -// * class with the given identifier, requesting the given {@link LockOptions}. -// * -// * @param entityClass The entity type -// * @param id an identifier -// * @param lockOptions the requested {@link LockOptions} -// * -// * @return a persistent instance or null via a {@code CompletionStage} -// * -// * @see #find(Class,Object) -// * @see #lock(Object, LockMode) this discussion of lock modes -// */ -// CompletionStage find(Class entityClass, Object id, LockOptions lockOptions); - - /** - * Asynchronously return the persistent instance with the given - * identifier of an entity class, using the given {@link EntityGraph} - * as a fetch plan. + * @param updateQuery the update criteria query * - * @param entityGraph an {@link EntityGraph} specifying the entity - * and associations to be fetched - * @param id an identifier + * @return The {@link MutationQuery} instance for manipulation and execution * - * @see #find(Class,Object) + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) */ - CompletionStage find(EntityGraph entityGraph, Object id); + MutationQuery createMutationQuery(CriteriaUpdate updateQuery); /** - * Asynchronously return the persistent instances of the given entity - * class with the given identifiers, or null if there is no such - * persistent instance. + * Create an instance of {@link MutationQuery} for the given delete tree. * - * @param entityClass The entity type - * @param ids the identifiers + * @param deleteQuery the delete criteria query * - * @return a list of persistent instances and nulls via a {@code CompletionStage} + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) */ - CompletionStage> find(Class entityClass, Object... ids); + MutationQuery createMutationQuery(CriteriaDelete deleteQuery); /** - * Asynchronously return the persistent instance of the given entity - * class with the given natural identifiers, or null if there is no - * such persistent instance. + * Create a {@link MutationQuery} from the given insert select criteria tree * - * @param entityClass The entity type - * @param naturalId the natural identifier + * @param insert the insert select criteria query * - * @return a persistent instance or null via a {@code CompletionStage} + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) */ - @Incubating - CompletionStage find(Class entityClass, Identifier naturalId); + MutationQuery createMutationQuery(JpaCriteriaInsert insert); /** - * Return the persistent instance of the given entity class with the - * given identifier, assuming that the instance exists. This method - * never results in access to the underlying data store, and thus - * might return a proxy that must be initialized explicitly using - * {@link #fetch(Object)}. - *

- * You should not use this method to determine if an instance exists - * (use {@link #find} instead). Use this only to retrieve an instance - * which you safely assume exists, where non-existence would be an - * actual error. + * Create a typed {@link Query} instance for the given typed query reference. * - * @param entityClass a persistent class - * @param id a valid identifier of an existing persistent instance of the class + * @param typedQueryReference the type query reference * - * @return the persistent instance or proxy + * @return The {@link Query} instance for execution * - * @see jakarta.persistence.EntityManager#getReference(Class, Object) + * @throws IllegalArgumentException if a query has not been + * defined with the name of the typed query reference or if + * the query result is found to not be assignable to + * result class of the typed query reference + * + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) */ - T getReference(Class entityClass, Object id); + Query createQuery(TypedQueryReference typedQueryReference); /** - * Return the persistent instance with the same identity as the given - * instance, which might be detached, assuming that the instance is - * still persistent in the database. This method never results in - * access to the underlying data store, and thus might return a proxy - * that must be initialized explicitly using {@link #fetch(Object)}. + * Create an instance of {@link Query} for the given HQL/JPQL query + * string or HQL/JPQL update or delete statement. In the case of an + * update or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. * - * @param entity a detached persistent instance + * @param queryString The HQL/JPQL query, update or delete statement * - * @return the persistent instance or proxy + * @return The {@link Query} instance for manipulation and execution + * + * @deprecated See explanation in + * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} + * + * @see jakarta.persistence.EntityManager#createQuery(String) */ - T getReference(T entity); + @Deprecated + Query createQuery(String queryString); /** - * Asynchronously persist the given transient instance, first assigning - * a generated identifier. (Or using the current value of the identifier - * property if the entity has assigned identifiers.) - *

- * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + * Create an instance of {@link SelectionQuery} for the given HQL/JPQL + * query string and query result type. * - *

-		 * {@code session.persist(newBook).thenAccept(v -> session.flush());}
-		 * 
+ * @param queryString The HQL/JPQL query + * @param resultType the Java type returned in each row of query results * - * @param entity a transient instance of a persistent class + * @return The {@link Query} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#persist(Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - CompletionStage persist(Object entity); + SelectionQuery createQuery(String queryString, Class resultType); /** - * Make a transient instance persistent and mark it for later insertion in the - * database. This operation cascades to associated instances if the association - * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. - *

- * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, - * {@code persist()} ultimately results in generation of an identifier for the - * given instance. But this may happen asynchronously, when the session is - * {@linkplain #flush() flushed}, depending on the identifier generation strategy. + * Create an instance of {@link MutationQuery} for the given criteria + * update. * - * @param entityName the entity name - * @param object a transient instance to be made persistent - * @see #persist(Object) - */ - CompletionStage persist(String entityName, Object object); - - /** - * Persist multiple transient entity instances at once. + * @param criteriaUpdate The {@link CriteriaUpdate} * - * @see #persist(Object) + * @return The {@link MutationQuery} instance for manipulation and execution */ - CompletionStage persist(Object... entities); + MutationQuery createQuery(CriteriaUpdate criteriaUpdate); /** - * Asynchronously remove a persistent instance from the datastore. The - * argument may be an instance associated with the receiving session or - * a transient instance with an identifier associated with existing - * persistent state. - *

- * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#REMOVE}. - * - *

-		 * {@code session.delete(book).thenAccept(v -> session.flush());}
-		 * 
- * - * @param entity the managed persistent instance to be removed + * Create an instance of {@link MutationQuery} for the given criteria + * delete. * - * @throws IllegalArgumentException if the given instance is not managed + * @param criteriaDelete The {@link CriteriaDelete} * - * @see jakarta.persistence.EntityManager#remove(Object) + * @return The {@link MutationQuery} instance for manipulation and execution */ - CompletionStage remove(Object entity); + MutationQuery createQuery(CriteriaDelete criteriaDelete); /** - * Remove multiple entity instances at once. + * Create an instance of {@link Query} for the named query. * - * @see #remove(Object) + * @param queryName The name of the query + * + * @return The {@link Query} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createQuery(String) */ - CompletionStage remove(Object... entities); + Query createNamedQuery(String queryName); /** - * Copy the state of the given object onto the persistent instance with - * the same identifier. If there is no such persistent instance currently - * associated with the session, it will be loaded. Return the persistent - * instance. Or, if the given instance is transient, save a copy of it - * and return the copy as a newly persistent instance. The given instance - * does not become associated with the session. - *

- * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#MERGE}. + * Create an instance of {@link SelectionQuery} for the named query. * - * @param entity a detached instance with state to be copied + * @param queryName The name of the query + * @param resultType the Java type returned in each row of query results * - * @return an updated persistent instance + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#merge(Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - CompletionStage merge(T entity); + SelectionQuery createNamedQuery(String queryName, Class resultType); /** - * Merge multiple entity instances at once. - * - * @see #merge(Object) + * Create an instance of {@link Query} for the given SQL query string, + * using the given {@code resultType} to interpret the results. + * + *

    + *
  • If the given result type is {@link Object}, or a built-in type + * such as {@link String} or {@link Integer}, the result set must + * have a single column, which will be returned as a scalar.
  • + *
  • If the given result type is {@code Object[]}, then the result set + * must have multiple columns, which will be returned as elements of + * arrays of type {@code Object[]}.
  • + *
  • Otherwise, the given result type must be an entity class, in which + * case the result set column aliases must map to the fields of the + * entity, and the query will return instances of the entity.
  • + *
+ * + * @param queryString The SQL query + * @param resultType the Java type returned in each row of query results + * + * @return The {@link SelectionQuery} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) */ - CompletionStage merge(Object... entities); + SelectionQuery createNativeQuery(String queryString, Class resultType); /** - * Re-read the state of the given instance from the underlying database. - * It is inadvisable to use this to implement long-running sessions that - * span many business tasks. This method is, however, useful in certain - * special circumstances, for example: + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@code resultType} to interpret the results. * *
    - *
  • where a database trigger alters the object state after insert or - * update, or - *
  • after executing direct native SQL in the same session. + *
  • If the given result type is {@link Object}, or a built-in type + * such as {@link String} or {@link Integer}, the result set must + * have a single column, which will be returned as a scalar.
  • + *
  • If the given result type is {@code Object[]}, then the result set + * must have multiple columns, which will be returned as elements of + * arrays of type {@code Object[]}.
  • + *
  • Otherwise, the given result type must be an entity class, in which + * case the result set column aliases must map to the fields of the + * entity, and the query will return instances of the entity.
  • *
* - * @param entity a managed persistent instance + * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the query. * - * @throws IllegalArgumentException if the given instance is not managed + * @param queryString The SQL query + * @param resultType the Java type returned in each row of query results + * @param affectedEntities The entities which are affected by the query * - * @see jakarta.persistence.EntityManager#refresh(Object) + * @return The {@link Query} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) */ - CompletionStage refresh(Object entity); + SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities); /** - * Re-read the state of the given instance from the underlying database, - * requesting the given {@link LockMode}. + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@link ResultSetMapping} to interpret the + * result set. * - * @param entity a managed persistent entity instance - * @param lockMode the requested lock mode + * @param queryString The SQL query + * @param resultSetMapping the result set mapping * - * @see #refresh(Object) + * @return The {@link Query} instance for manipulation and execution + * + * @see #getResultSetMapping(Class, String) + * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) */ - CompletionStage refresh(Object entity, LockMode lockMode); + SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); /** - * Re-read the state of the given instance from the underlying database, - * requesting the given {@link LockModeType}. + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@link ResultSetMapping} to interpret the + * result set. + *

+ * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the query. * - * @param entity a managed persistent entity instance - * @param lockModeType the requested lock mode + * @param queryString The SQL query + * @param resultSetMapping the result set mapping + * @param affectedEntities The entities which are affected by the query * - * @see #refresh(Object) + * @return The {@link Query} instance for manipulation and execution + * + * @see #getResultSetMapping(Class, String) + * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) */ - default CompletionStage refresh(Object entity, LockModeType lockModeType) { - return refresh( entity, convertToLockMode(lockModeType) ); - } - -// /** -// * Re-read the state of the given instance from the underlying database, -// * requesting the given {@link LockOptions}. -// * -// * @param entity a managed persistent entity instance -// * @param lockOptions the requested {@link LockOptions} -// * -// * @see #refresh(Object) -// */ -// CompletionStage refresh(Object entity, LockOptions lockOptions); + SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities); /** - * Refresh multiple entity instances at once. + * Create an instance of {@link Query} for the given SQL query string, + * or SQL update, insert, or delete statement. In the case of an update, + * insert, or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. + * In the case of a query: * - * @see #refresh(Object) + *

    + *
  • If the result set has a single column, the results will be returned + * as scalars.
  • + *
  • Otherwise, if the result set has multiple columns, the results will + * be returned as elements of arrays of type {@code Object[]}.
  • + *
+ * + * @param queryString The SQL select, update, insert, or delete statement */ - CompletionStage refresh(Object... entities); + Query createNativeQuery(String queryString); /** - * Obtain the specified lock level upon the given object. For example, - * this operation may be used to: + * Create an instance of {@link Query} for the given SQL query string, + * or SQL update, insert, or delete statement. In the case of an update, + * insert, or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. + * In the case of a query: * *
    - *
  • perform a version check with {@link LockMode#PESSIMISTIC_READ}, - *
  • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}, - *
  • force a version increment with {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}, - *
  • schedule a version check just before the end of the transaction with - * {@link LockMode#OPTIMISTIC}, or - *
  • schedule a version increment just before the end of the transaction - * with {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}. + *
  • If the result set has a single column, the results will be returned + * as scalars.
  • + *
  • Otherwise, if the result set has multiple columns, the results will + * be returned as elements of arrays of type {@code Object[]}.
  • *
* - * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. - * - * @param entity a managed persistent instance - * @param lockMode the lock level + * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the statement. * - * @throws IllegalArgumentException if the given instance is not managed + * @param queryString The SQL select, update, insert, or delete statement + * @param affectedEntities The entities which are affected by the statement */ - CompletionStage lock(Object entity, LockMode lockMode); + Query createNativeQuery(String queryString, AffectedEntities affectedEntities); /** - * Obtain the specified lock level upon the given object. For example, - * this operation may be used to: - * - *
    - *
  • perform a version check with {@link LockModeType#PESSIMISTIC_READ}, - *
  • upgrade to a pessimistic lock with {@link LockModeType#PESSIMISTIC_WRITE}, - *
  • force a version increment with {@link LockModeType#PESSIMISTIC_FORCE_INCREMENT}, - *
  • schedule a version check just before the end of the transaction with - * {@link LockModeType#OPTIMISTIC}, or - *
  • schedule a version increment just before the end of the transaction - * with {@link LockModeType#OPTIMISTIC_FORCE_INCREMENT}. - *
+ * Create an instance of {@link SelectionQuery} for the given criteria + * query. * - * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. + * @param criteriaQuery The {@link CriteriaQuery} * - * @param entity a managed persistent instance - * @param lockModeType the lock level + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @throws IllegalArgumentException if the given instance is not managed + * @see jakarta.persistence.EntityManager#createQuery(String) */ - default CompletionStage lock(Object entity, LockModeType lockModeType) { - return lock( entity, convertToLockMode(lockModeType) ); - } + SelectionQuery createQuery(CriteriaQuery criteriaQuery); /** - * Force this session to flush asynchronously. Must be called at the - * end of a unit of work, before committing the transaction and closing - * the session. Flushing is the process of synchronizing the - * underlying persistent store with state held in memory. - * - *
-		 * {@code session.flush().thenAccept(v -> print("done saving changes"));}
-		 * 
+ * Obtain a native SQL result set mapping defined via the annotation + * {@link jakarta.persistence.SqlResultSetMapping}. + */ + ResultSetMapping getResultSetMapping(Class resultType, String mappingName); + + /** + * Obtain a named {@link EntityGraph} + */ + EntityGraph getEntityGraph(Class rootType, String graphName); + + /** + * Create a new mutable {@link EntityGraph} + */ + EntityGraph createEntityGraph(Class rootType); + + /** + * Create a new mutable copy of a named {@link EntityGraph} + */ + EntityGraph createEntityGraph(Class rootType, String graphName); + + /** + * Convenience method to obtain the {@link CriteriaBuilder}. * - * @see jakarta.persistence.EntityManager#flush() + * @since 3 */ - CompletionStage flush(); + CriteriaBuilder getCriteriaBuilder(); + } + + /** + * A non-blocking counterpart to the Hibernate {@link org.hibernate.Session} + * interface, allowing a reactive style of interaction with the database. + *

+ * The semantics of operations on this interface are identical to the + * semantics of the similarly-named operations of {@code Session}, except + * that the operations are performed asynchronously, returning a + * {@link CompletionStage} without blocking the calling thread. + *

+ * Entities associated with an {@code Session} do not support transparent + * lazy association fetching. Instead, {@link #fetch(Object)} should be used + * to explicitly request asynchronous fetching of an association, or the + * association should be fetched eagerly when the entity is first retrieved, + * for example, by: + * + *

    + *
  • {@link #enableFetchProfile(String) enabling a fetch profile}, + *
  • using an {@link EntityGraph}, or + *
  • writing a {@code join fetch} clause in a HQL query. + *
+ * + * @see org.hibernate.Session + */ + interface Session extends QueryProducer, Closeable { /** - * Asynchronously fetch an association configured for lazy loading. - *

- *

-		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()))}
-		 * 
- *

- *

- * This operation may be even be used to initialize a reference returned by - * {@link #getReference(Class, Object)}. - *

+ * Asynchronously return the persistent instance of the given entity + * class with the given identifier, or {@code null} if there is no such + * persistent instance. If the instance is already associated with + * the session, return the associated instance. This method never + * returns an uninitialized instance. + * *

-		 * {@code session.fetch(session.getReference(Author.class, authorId))}
+		 * {@code session.find(Book.class, id).thenAccept(book -> print(book.getTitle()));}
 		 * 
- *

* - * @param association a lazy-loaded association, or a proxy + * @param entityClass The entity type + * @param id an identifier * - * @return the fetched association, via a {@code CompletionStage} + * @return a persistent instance or null via a {@code CompletionStage} * - * @see Stage#fetch(Object) - * @see #getReference(Class, Object) - * @see org.hibernate.Hibernate#initialize(Object) + * @see jakarta.persistence.EntityManager#find(Class, Object) */ - CompletionStage fetch(T association); + CompletionStage find(Class entityClass, Object id); /** - * Fetch a lazy property of the given entity, identified by a JPA - * {@link Attribute attribute metamodel}. Note that this feature is - * only supported in conjunction with the Hibernate bytecode enhancer. + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, requesting the given {@link LockMode}. * - *
-		 * {@code session.fetch(book, Book_.isbn).thenAccept(isbn -> print(isbn))}
-		 * 
+ * @param entityClass The entity type + * @param id an identifier + * @param lockMode the requested {@link LockMode} + * + * @return a persistent instance or null via a {@code CompletionStage} + * + * @see #find(Class,Object) + * @see #lock(Object, LockMode) this discussion of lock modes */ - CompletionStage fetch(E entity, Attribute field); + CompletionStage find(Class entityClass, Object id, LockMode lockMode); /** - * Asynchronously fetch an association that's configured for lazy loading, - * and unwrap the underlying entity implementation from any proxy. - * - *
-		 * {@code session.unproxy(author.getBook()).thenAccept(book -> print(book.getTitle()));}
-		 * 
+ * Asynchronously return the persistent instance of the given entity + * class with the given identifier, requesting the given {@link LockModeType}. * - * @param association a lazy-loaded association + * @param entityClass The entity type + * @param id an identifier + * @param lockModeType the requested {@link LockModeType} * - * @return the fetched association, via a {@code CompletionStage} + * @return a persistent instance or null via a {@code CompletionStage} * - * @see org.hibernate.Hibernate#unproxy(Object) + * @see #find(Class,Object) + * @see #lock(Object, LockMode) this discussion of lock modes */ - CompletionStage unproxy(T association); + default CompletionStage find(Class entityClass, Object id, LockModeType lockModeType) { + return find( entityClass, id, convertToLockMode(lockModeType) ); + } - /** - * Determine the current lock mode of the given entity. + /** + * Asynchronously return the persistent instance with the given + * identifier of an entity class, using the given {@link EntityGraph} + * as a fetch plan. + * + * @param entityGraph an {@link EntityGraph} specifying the entity + * and associations to be fetched + * @param id an identifier + * + * @see #find(Class,Object) */ - LockMode getLockMode(Object entity); + CompletionStage find(EntityGraph entityGraph, Object id); /** - * Determine if the given instance belongs to this persistence context. + * Asynchronously return the persistent instances of the given entity + * class with the given identifiers, or null if there is no such + * persistent instance. + * + * @param entityClass The entity type + * @param ids the identifiers + * + * @return a list of persistent instances and nulls via a {@code CompletionStage} */ - boolean contains(Object entity); + CompletionStage> find(Class entityClass, Object... ids); /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string. - * - * @param queryString The HQL/JPQL query + * Asynchronously return the persistent instance of the given entity + * class with the given natural identifiers, or null if there is no + * such persistent instance. * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entityClass The entity type + * @param naturalId the natural identifier * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @return a persistent instance or null via a {@code CompletionStage} */ - SelectionQuery createSelectionQuery(String queryString, Class resultType); + @Incubating + CompletionStage find(Class entityClass, Identifier naturalId); /** - * Create an instance of {@link MutationQuery} for the given HQL/JPQL - * update or delete statement. + * Return the persistent instance of the given entity class with the + * given identifier, assuming that the instance exists. This method + * never results in access to the underlying data store, and thus + * might return a proxy that must be initialized explicitly using + * {@link #fetch(Object)}. + *

+ * You should not use this method to determine if an instance exists + * (use {@link #find} instead). Use this only to retrieve an instance + * which you safely assume exists, where non-existence would be an + * actual error. * - * @param queryString The HQL/JPQL query, update or delete statement + * @param entityClass a persistent class + * @param id a valid identifier of an existing persistent instance of the class * - * @return The {@link MutationQuery} instance for manipulation and execution + * @return the persistent instance or proxy * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see jakarta.persistence.EntityManager#getReference(Class, Object) */ - MutationQuery createMutationQuery(String queryString); + T getReference(Class entityClass, Object id); /** - * Create an instance of {@link MutationQuery} for the given update tree. - * - * @param updateQuery the update criteria query + * Return the persistent instance with the same identity as the given + * instance, which might be detached, assuming that the instance is + * still persistent in the database. This method never results in + * access to the underlying data store, and thus might return a proxy + * that must be initialized explicitly using {@link #fetch(Object)}. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a detached persistent instance * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) + * @return the persistent instance or proxy */ - MutationQuery createMutationQuery(CriteriaUpdate updateQuery); + T getReference(T entity); /** - * Create an instance of {@link MutationQuery} for the given delete tree. + * Asynchronously persist the given transient instance, first assigning + * a generated identifier. (Or using the current value of the identifier + * property if the entity has assigned identifiers.) + *

+ * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#PERSIST}. * - * @param deleteQuery the delete criteria query + *

+		 * {@code session.persist(newBook).thenAccept(v -> session.flush());}
+		 * 
* - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a transient instance of a persistent class * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) + * @see jakarta.persistence.EntityManager#persist(Object) */ - MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + CompletionStage persist(Object entity); /** - * Create a {@link MutationQuery} from the given insert select criteria tree - * - * @param insert the insert select criteria query + * Make a transient instance persistent and mark it for later insertion in the + * database. This operation cascades to associated instances if the association + * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + *

+ * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, + * {@code persist()} ultimately results in generation of an identifier for the + * given instance. But this may happen asynchronously, when the session is + * {@linkplain #flush() flushed}, depending on the identifier generation strategy. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entityName the entity name + * @param object a transient instance to be made persistent + * @see #persist(Object) + */ + CompletionStage persist(String entityName, Object object); + + /** + * Persist multiple transient entity instances at once. * - * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) + * @see #persist(Object) */ - MutationQuery createMutationQuery(JpaCriteriaInsert insert); + CompletionStage persist(Object... entities); /** - * Create a typed {@link Query} instance for the given typed query reference. + * Asynchronously remove a persistent instance from the datastore. The + * argument may be an instance associated with the receiving session or + * a transient instance with an identifier associated with existing + * persistent state. + *

+ * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#REMOVE}. * - * @param typedQueryReference the type query reference + *

+		 * {@code session.delete(book).thenAccept(v -> session.flush());}
+		 * 
* - * @return The {@link Query} instance for execution + * @param entity the managed persistent instance to be removed * - * @throws IllegalArgumentException if a query has not been - * defined with the name of the typed query reference or if - * the query result is found to not be assignable to - * result class of the typed query reference + * @throws IllegalArgumentException if the given instance is not managed + * + * @see jakarta.persistence.EntityManager#remove(Object) + */ + CompletionStage remove(Object entity); + + /** + * Remove multiple entity instances at once. * - * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + * @see #remove(Object) */ - Query createQuery(TypedQueryReference typedQueryReference); + CompletionStage remove(Object... entities); /** - * Create an instance of {@link Query} for the given HQL/JPQL query - * string or HQL/JPQL update or delete statement. In the case of an - * update or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The HQL/JPQL query, update or delete statement + * Copy the state of the given object onto the persistent instance with + * the same identifier. If there is no such persistent instance currently + * associated with the session, it will be loaded. Return the persistent + * instance. Or, if the given instance is transient, save a copy of it + * and return the copy as a newly persistent instance. The given instance + * does not become associated with the session. + *

+ * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#MERGE}. * - * @return The {@link Query} instance for manipulation and execution + * @param entity a detached instance with state to be copied * - * @deprecated See explanation in - * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} + * @return an updated persistent instance * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see jakarta.persistence.EntityManager#merge(Object) */ - @Deprecated - Query createQuery(String queryString); + CompletionStage merge(T entity); /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string and query result type. - * - * @param queryString The HQL/JPQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution + * Merge multiple entity instances at once. * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #merge(Object) */ - SelectionQuery createQuery(String queryString, Class resultType); + CompletionStage merge(Object... entities); /** - * Create an instance of {@link MutationQuery} for the given criteria - * update. + * Re-read the state of the given instance from the underlying database. + * It is inadvisable to use this to implement long-running sessions that + * span many business tasks. This method is, however, useful in certain + * special circumstances, for example: * - * @param criteriaUpdate The {@link CriteriaUpdate} + *

    + *
  • where a database trigger alters the object state after insert or + * update, or + *
  • after executing direct native SQL in the same session. + *
* - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a managed persistent instance + * + * @throws IllegalArgumentException if the given instance is not managed + * + * @see jakarta.persistence.EntityManager#refresh(Object) */ - MutationQuery createQuery(CriteriaUpdate criteriaUpdate); + CompletionStage refresh(Object entity); /** - * Create an instance of {@link MutationQuery} for the given criteria - * delete. + * Re-read the state of the given instance from the underlying database, + * requesting the given {@link LockMode}. * - * @param criteriaDelete The {@link CriteriaDelete} + * @param entity a managed persistent entity instance + * @param lockMode the requested lock mode * - * @return The {@link MutationQuery} instance for manipulation and execution + * @see #refresh(Object) */ - MutationQuery createQuery(CriteriaDelete criteriaDelete); + CompletionStage refresh(Object entity, LockMode lockMode); /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query + * Re-read the state of the given instance from the underlying database, + * requesting the given {@link LockModeType}. * - * @return The {@link Query} instance for manipulation and execution + * @param entity a managed persistent entity instance + * @param lockModeType the requested lock mode * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see #refresh(Object) */ - Query createNamedQuery(String queryName); + default CompletionStage refresh(Object entity, LockModeType lockModeType) { + return refresh( entity, convertToLockMode(lockModeType) ); + } /** - * Create an instance of {@link SelectionQuery} for the named query. - * - * @param queryName The name of the query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link SelectionQuery} instance for manipulation and execution + * Refresh multiple entity instances at once. * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #refresh(Object) */ - SelectionQuery createNamedQuery(String queryName, Class resultType); + CompletionStage refresh(Object... entities); /** - * Create an instance of {@link Query} for the given SQL query string, - * using the given {@code resultType} to interpret the results. + * Obtain the specified lock level upon the given object. For example, + * this operation may be used to: * *
    - *
  • If the given result type is {@link Object}, or a built-in type - * such as {@link String} or {@link Integer}, the result set must - * have a single column, which will be returned as a scalar.
  • - *
  • If the given result type is {@code Object[]}, then the result set - * must have multiple columns, which will be returned as elements of - * arrays of type {@code Object[]}.
  • - *
  • Otherwise, the given result type must be an entity class, in which - * case the result set column aliases must map to the fields of the - * entity, and the query will return instances of the entity.
  • + *
  • perform a version check with {@link LockMode#PESSIMISTIC_READ}, + *
  • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}, + *
  • force a version increment with {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}, + *
  • schedule a version check just before the end of the transaction with + * {@link LockMode#OPTIMISTIC}, or + *
  • schedule a version increment just before the end of the transaction + * with {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}. *
* - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results + * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entity a managed persistent instance + * @param lockMode the lock level * - * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) + * @throws IllegalArgumentException if the given instance is not managed */ - SelectionQuery createNativeQuery(String queryString, Class resultType); + CompletionStage lock(Object entity, LockMode lockMode); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@code resultType} to interpret the results. + * Obtain the specified lock level upon the given object. For example, + * this operation may be used to: * *
    - *
  • If the given result type is {@link Object}, or a built-in type - * such as {@link String} or {@link Integer}, the result set must - * have a single column, which will be returned as a scalar.
  • - *
  • If the given result type is {@code Object[]}, then the result set - * must have multiple columns, which will be returned as elements of - * arrays of type {@code Object[]}.
  • - *
  • Otherwise, the given result type must be an entity class, in which - * case the result set column aliases must map to the fields of the - * entity, and the query will return instances of the entity.
  • + *
  • perform a version check with {@link LockModeType#PESSIMISTIC_READ}, + *
  • upgrade to a pessimistic lock with {@link LockModeType#PESSIMISTIC_WRITE}, + *
  • force a version increment with {@link LockModeType#PESSIMISTIC_FORCE_INCREMENT}, + *
  • schedule a version check just before the end of the transaction with + * {@link LockModeType#OPTIMISTIC}, or + *
  • schedule a version increment just before the end of the transaction + * with {@link LockModeType#OPTIMISTIC_FORCE_INCREMENT}. *
* - * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the query. - * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * @param affectedEntities The entities which are affected by the query + * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. * - * @return The {@link Query} instance for manipulation and execution + * @param entity a managed persistent instance + * @param lockModeType the lock level * - * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) + * @throws IllegalArgumentException if the given instance is not managed */ - SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities); + default CompletionStage lock(Object entity, LockModeType lockModeType) { + return lock( entity, convertToLockMode(lockModeType) ); + } /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. - * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping + * Force this session to flush asynchronously. Must be called at the + * end of a unit of work, before committing the transaction and closing + * the session. Flushing is the process of synchronizing the + * underlying persistent store with state held in memory. * - * @return The {@link Query} instance for manipulation and execution + *
+		 * {@code session.flush().thenAccept(v -> print("done saving changes"));}
+		 * 
* - * @see #getResultSetMapping(Class, String) - * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) + * @see jakarta.persistence.EntityManager#flush() */ - SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); + CompletionStage flush(); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. + * Asynchronously fetch an association configured for lazy loading. *

- * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the query. + *

+		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()))}
+		 * 
+ *

+ *

+ * This operation may be even be used to initialize a reference returned by + * {@link #getReference(Class, Object)}. + *

+ *

+		 * {@code session.fetch(session.getReference(Author.class, authorId))}
+		 * 
+ *

* - * @param queryString The SQL query - * @param resultSetMapping the result set mapping - * @param affectedEntities The entities which are affected by the query + * @param association a lazy-loaded association, or a proxy * - * @return The {@link Query} instance for manipulation and execution + * @return the fetched association, via a {@code CompletionStage} * - * @see #getResultSetMapping(Class, String) - * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) + * @see Stage#fetch(Object) + * @see #getReference(Class, Object) + * @see org.hibernate.Hibernate#initialize(Object) */ - SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities); + CompletionStage fetch(T association); /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * In the case of a query: - * - *
    - *
  • If the result set has a single column, the results will be returned - * as scalars.
  • - *
  • Otherwise, if the result set has multiple columns, the results will - * be returned as elements of arrays of type {@code Object[]}.
  • - *
+ * Fetch a lazy property of the given entity, identified by a JPA + * {@link Attribute attribute metamodel}. Note that this feature is + * only supported in conjunction with the Hibernate bytecode enhancer. * - * @param queryString The SQL select, update, insert, or delete statement + *
+		 * {@code session.fetch(book, Book_.isbn).thenAccept(isbn -> print(isbn))}
+		 * 
*/ - Query createNativeQuery(String queryString); + CompletionStage fetch(E entity, Attribute field); /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * In the case of a query: + * Asynchronously fetch an association that's configured for lazy loading, + * and unwrap the underlying entity implementation from any proxy. * - *
    - *
  • If the result set has a single column, the results will be returned - * as scalars.
  • - *
  • Otherwise, if the result set has multiple columns, the results will - * be returned as elements of arrays of type {@code Object[]}.
  • - *
+ *
+		 * {@code session.unproxy(author.getBook()).thenAccept(book -> print(book.getTitle()));}
+		 * 
* - * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the statement. + * @param association a lazy-loaded association + * + * @return the fetched association, via a {@code CompletionStage} * - * @param queryString The SQL select, update, insert, or delete statement - * @param affectedEntities The entities which are affected by the statement + * @see org.hibernate.Hibernate#unproxy(Object) */ - Query createNativeQuery(String queryString, AffectedEntities affectedEntities); + CompletionStage unproxy(T association); /** - * Create an instance of {@link SelectionQuery} for the given criteria - * query. - * - * @param criteriaQuery The {@link CriteriaQuery} - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) + * Determine the current lock mode of the given entity. */ - SelectionQuery createQuery(CriteriaQuery criteriaQuery); + LockMode getLockMode(Object entity); + + /** + * Determine if the given instance belongs to this persistence context. + */ + boolean contains(Object entity); /** * Set the {@link FlushMode flush mode} for this session. @@ -1289,27 +1292,6 @@ default Session setFlushMode(FlushModeType flushModeType) { */ Session enableFetchProfile(String name); - /** - * Obtain a native SQL result set mapping defined via the annotation - * {@link jakarta.persistence.SqlResultSetMapping}. - */ - ResultSetMapping getResultSetMapping(Class resultType, String mappingName); - - /** - * Obtain a named {@link EntityGraph} - */ - EntityGraph getEntityGraph(Class rootType, String graphName); - - /** - * Create a new mutable {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType); - - /** - * Create a new mutable copy of a named {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType, String graphName); - /** * Disable a particular fetch profile on this session, or do nothing if * the requested fetch profile is not enabled. @@ -1534,14 +1516,6 @@ default Session setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); - /** - * Convenience method to obtain the {@link CriteriaBuilder}. - * - * @since 3 - */ - default CriteriaBuilder getCriteriaBuilder() { - return getFactory().getCriteriaBuilder(); - } } /** @@ -1574,7 +1548,7 @@ default CriteriaBuilder getCriteriaBuilder() { * * @see org.hibernate.StatelessSession */ - interface StatelessSession extends Closeable { + interface StatelessSession extends QueryProducer, Closeable { /** * Retrieve a row. @@ -1639,194 +1613,6 @@ default CompletionStage get(Class entityClass, Object id, LockModeType */ CompletionStage get(EntityGraph entityGraph, Object id); - /** - * Create an instance of {@link Query} for the given HQL/JPQL query - * string or HQL/JPQL update or delete statement. In the case of an - * update or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The HQL/JPQL query, update or delete statement - * - * @return The {@link Query} instance for manipulation and execution - * - * @deprecated See explanation in - * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} - * - * @see org.hibernate.Session#createQuery(String) - */ - @Deprecated - Query createQuery(String queryString); - - /** - * Create a typed {@link Query} instance for the given typed query reference. - * - * @param typedQueryReference the type query reference - * - * @return The {@link Query} instance for execution - * - * @throws IllegalArgumentException if a query has not been - * defined with the name of the typed query reference or if - * the query result is found to not be assignable to - * result class of the typed query reference - * - * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) - */ - Query createQuery(TypedQueryReference typedQueryReference); - - /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string and query result type. - * - * @param queryString The HQL/JPQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see org.hibernate.Session#createQuery(String, Class) - */ - SelectionQuery createQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string. - * - * @param queryString The HQL/JPQL query - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) - */ - SelectionQuery createSelectionQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link MutationQuery} for the given HQL/JPQL - * update or delete statement. - * - * @param queryString The HQL/JPQL query, update or delete statement - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - MutationQuery createMutationQuery(String queryString); - - /** - * Create an instance of {@link MutationQuery} for the given update tree. - * - * @param updateQuery the update criteria query - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) - */ - MutationQuery createMutationQuery(CriteriaUpdate updateQuery); - - /** - * Create an instance of {@link MutationQuery} for the given delete tree. - * - * @param deleteQuery the delete criteria query - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) - */ - MutationQuery createMutationQuery(CriteriaDelete deleteQuery); - - /** - * Create a {@link MutationQuery} from the given insert select criteria tree - * - * @param insert the insert select criteria query - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) - */ - MutationQuery createMutationQuery(JpaCriteriaInsert insert); - - /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The SQL select, update, insert, or delete statement - * - * @see org.hibernate.Session#createNativeQuery(String) - */ - Query createNativeQuery(String queryString); - - /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query - * - * @return The {@link Query} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - Query createNamedQuery(String queryName); - - /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) - */ - SelectionQuery createNamedQuery(String queryName, Class resultType); - - /** - * Create an instance of {@link Query} for the given SQL query string, - * using the given {@code resultType} to interpret the results. - * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see org.hibernate.Session#createNativeQuery(String, Class) - */ - SelectionQuery createNativeQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link SelectionQuery} for the given criteria - * query. - * - * @param criteriaQuery The {@link CriteriaQuery} - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - SelectionQuery createQuery(CriteriaQuery criteriaQuery); - - /** - * Create an instance of {@link MutationQuery} for the given criteria - * update. - * - * @param criteriaUpdate The {@link CriteriaUpdate} - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - MutationQuery createQuery(CriteriaUpdate criteriaUpdate); - - /** - * Create an instance of {@link MutationQuery} for the given criteria - * delete. - * - * @param criteriaDelete The {@link CriteriaDelete} - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - MutationQuery createQuery(CriteriaDelete criteriaDelete); - /** * Insert a row. * @@ -2087,27 +1873,6 @@ default CompletionStage refresh(Object entity, LockModeType lockModeType) */ Object getIdentifier(Object entity); - /** - * Obtain a native SQL result set mapping defined via the annotation - * {@link jakarta.persistence.SqlResultSetMapping}. - */ - ResultSetMapping getResultSetMapping(Class resultType, String mappingName); - - /** - * Obtain a named {@link EntityGraph} - */ - EntityGraph getEntityGraph(Class rootType, String graphName); - - /** - * Create a new mutable {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType); - - /** - * Create a new mutable copy of a named {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType, String graphName); - /** * Performs the given work within the scope of a database transaction, * automatically flushing the session. The transaction will be rolled diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index dd25105f1..32227de7b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -5,11 +5,6 @@ */ package org.hibernate.reactive.stage.impl; -import jakarta.persistence.TypedQueryReference; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; import org.hibernate.CacheMode; import org.hibernate.Filter; @@ -42,10 +37,16 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.PersistenceException; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Attribute; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; import static org.hibernate.reactive.util.impl.CompletionStages.applyToAll; import static org.hibernate.reactive.util.impl.CompletionStages.returnOrRethrow; @@ -374,17 +375,17 @@ public boolean isFetchProfileEnabled(String name) { @Override public Filter enableFilter(String filterName) { - return delegate.enableFilter(filterName); + return delegate.enableFilter( filterName ); } @Override public void disableFilter(String filterName) { - delegate.disableFilter(filterName); + delegate.disableFilter( filterName ); } @Override public Filter getEnabledFilter(String filterName) { - return delegate.getEnabledFilter(filterName); + return delegate.getEnabledFilter( filterName ); } @Override @@ -512,6 +513,11 @@ public Stage.SessionFactory getFactory() { return delegate.getFactory().unwrap( Stage.SessionFactory.class ); } + @Override + public CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } + @Override public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { return delegate.getResultSetMapping( resultType, mappingName ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 425a0d363..4c325fdf9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -8,6 +8,7 @@ import org.hibernate.LockMode; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.query.criteria.JpaCriteriaInsert; +import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.query.ReactiveQuery; @@ -19,6 +20,7 @@ import jakarta.persistence.EntityGraph; import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; @@ -206,6 +208,11 @@ public Stage.SessionFactory getFactory() { return delegate.getFactory().unwrap( Stage.SessionFactory.class ); } + @Override + public CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } + private Transaction currentTransaction; @Override @@ -332,6 +339,11 @@ public Query createNativeQuery(String queryString) { return new StageQueryImpl<>( delegate.createReactiveNativeQuery( queryString ) ); } + @Override + public Query createNativeQuery(String queryString, AffectedEntities affectedEntities) { + return new StageQueryImpl<>( delegate.createReactiveNativeQuery( queryString, affectedEntities ) ); + } + @Override public Query createNamedQuery(String queryName) { return new StageQueryImpl<>( delegate.createReactiveNamedQuery( queryName ) ); @@ -347,6 +359,21 @@ public SelectionQuery createNativeQuery(String queryString, Class resu return new StageSelectionQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultType ) ); } + @Override + public SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities) { + return new StageSelectionQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultType, affectedEntities ) ); + } + + @Override + public SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping) { + return new StageSelectionQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultSetMapping ) ); + } + + @Override + public SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities) { + return new StageSelectionQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultSetMapping, affectedEntities) ); + } + @Override public SelectionQuery createQuery(CriteriaQuery criteriaQuery) { return new StageSelectionQueryImpl<>( delegate.createReactiveQuery( criteriaQuery ) );