HHH-16747 nail down query result types and actually document the semantics
This commit is contained in:
parent
c5ecf5d41c
commit
4eee30550c
|
@ -861,7 +861,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
query.setTupleTransformer( NativeQueryListTransformer.INSTANCE );
|
query.setTupleTransformer( NativeQueryListTransformer.INSTANCE );
|
||||||
}
|
}
|
||||||
else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) {
|
else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) {
|
||||||
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
query.addEntity( resultClass, LockMode.READ );
|
||||||
}
|
}
|
||||||
else if ( resultClass != Object.class && resultClass != Object[].class ) {
|
else if ( resultClass != Object.class && resultClass != Object[].class ) {
|
||||||
if ( isClass( resultClass )
|
if ( isClass( resultClass )
|
||||||
|
|
|
@ -80,8 +80,8 @@ public interface JpaMetamodel extends Metamodel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a map that gives access to the enum literal expressions that can be used in queries.
|
* Returns a map that gives access to the enum literal expressions that can be used in queries.
|
||||||
* The key is the short-hand enum literal. The value is a map, from enum class to the actual enum value.
|
* The key is the shorthand enum literal. The value is a map, from enum class to the actual enum value.
|
||||||
* This is needed for parsing short-hand enum literals that don't use FQNs.
|
* This is needed for parsing shorthand enum literals that don't use FQNs.
|
||||||
*/
|
*/
|
||||||
Map<String, Map<Class<?>, Enum<?>>> getAllowedEnumLiteralTexts();
|
Map<String, Map<Class<?>, Enum<?>>> getAllowedEnumLiteralTexts();
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,28 @@ public interface QueryProducer {
|
||||||
/**
|
/**
|
||||||
* Create a {@link Query} instance for the given HQL query, or
|
* Create a {@link Query} instance for the given HQL query, or
|
||||||
* HQL insert, update, or delete statement.
|
* HQL insert, update, or delete statement.
|
||||||
|
* <p>
|
||||||
|
* If a query has no explicit {@code select} list, the select list
|
||||||
|
* is inferred:
|
||||||
|
* <ul>
|
||||||
|
* <li>if there is exactly one root entity in the {@code from}
|
||||||
|
* clause, then that root entity is the only element of the
|
||||||
|
* select list, or
|
||||||
|
* <li>otherwise, if there are multiple root entities in the
|
||||||
|
* {@code from} clause, then the select list contains every
|
||||||
|
* root entity and every non-{@code fetch} joined entity.
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @apiNote Returns a raw {@code Query} type instead of a wildcard
|
* @apiNote Returns a raw {@code Query} type instead of a wildcard
|
||||||
* type {@code Query<?>}, to match the signature of the JPA method
|
* type {@code Query<?>}, to match the signature of the JPA method
|
||||||
* {@link jakarta.persistence.EntityManager#createQuery(String)}.
|
* {@link jakarta.persistence.EntityManager#createQuery(String)}.
|
||||||
*
|
*
|
||||||
|
* @implNote This method interprets some queries with an implicit
|
||||||
|
* {@code select} list in a quite unintuitive way. In some future
|
||||||
|
* release, this method will be modified to throw an exception
|
||||||
|
* when passed a query with a missing {@code select}. For now, use
|
||||||
|
* {@link #createQuery(String, Class)} to avoid ambiguity.
|
||||||
|
*
|
||||||
* @param queryString The HQL query
|
* @param queryString The HQL query
|
||||||
*
|
*
|
||||||
* @return The {@link Query} instance for manipulation and execution
|
* @return The {@link Query} instance for manipulation and execution
|
||||||
|
@ -41,21 +58,47 @@ public interface QueryProducer {
|
||||||
* @see jakarta.persistence.EntityManager#createQuery(String)
|
* @see jakarta.persistence.EntityManager#createQuery(String)
|
||||||
*
|
*
|
||||||
* @deprecated use {@link #createQuery(String, Class)},
|
* @deprecated use {@link #createQuery(String, Class)},
|
||||||
* {@link #createSelectionQuery} or {@link #createMutationQuery(String)}
|
* {@link #createSelectionQuery(String, Class)}, or
|
||||||
* depending on intention
|
* {@link #createMutationQuery(String)} depending on intention
|
||||||
*/
|
*/
|
||||||
@Deprecated(since = "6.0") @SuppressWarnings("rawtypes")
|
@Deprecated(since = "6.0") @SuppressWarnings("rawtypes")
|
||||||
Query createQuery(String queryString);
|
Query createQuery(String queryString);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a typed {@link Query} instance for the given HQL query string.
|
* Create a typed {@link Query} instance for the given HQL query
|
||||||
|
* string and given query result type.
|
||||||
|
* <ul>
|
||||||
|
* <li>If the query has a single item in the {@code select} list,
|
||||||
|
* then the select item must be assignable to the given result
|
||||||
|
* type.
|
||||||
|
* <li>Otherwise, if there are multiple select items, then the
|
||||||
|
* select items will be packaged into an instance of the
|
||||||
|
* result type. The result type must have an appropriate
|
||||||
|
* constructor with parameter types matching the select items,
|
||||||
|
* or it must be one of the types {@code Object[]},
|
||||||
|
* {@link java.util.List}, {@link java.util.Map}, or
|
||||||
|
* {@link jakarta.persistence.Tuple}.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* If a query has no explicit {@code select} list, the select list
|
||||||
|
* is inferred from the given query result type:
|
||||||
|
* <ul>
|
||||||
|
* <li>if the result type is an entity type, the query must have
|
||||||
|
* exactly one root entity in the {@code from} clause, it must
|
||||||
|
* be assignable to the result type, and the inferred select
|
||||||
|
* list will contain just that entity, or
|
||||||
|
* <li>otherwise, the select list contains every root entity and
|
||||||
|
* every non-{@code fetch} joined entity, and each query result
|
||||||
|
* will be packaged into an instance of the result type, just
|
||||||
|
* as specified above.
|
||||||
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* The returned {@code Query} may be executed by calling
|
* The returned {@code Query} may be executed by calling
|
||||||
* {@link Query#getResultList()} or {@link Query#getSingleResult()}.
|
* {@link Query#getResultList()} or {@link Query#getSingleResult()}.
|
||||||
*
|
*
|
||||||
* @param queryString The HQL query
|
* @param queryString The HQL query
|
||||||
* @param resultClass The type of the query result
|
* @param resultClass The type of the query result
|
||||||
* @return The Query instance for manipulation and execution
|
* @return The {@link Query} instance for manipulation and execution
|
||||||
*
|
*
|
||||||
* @see jakarta.persistence.EntityManager#createQuery(String,Class)
|
* @see jakarta.persistence.EntityManager#createQuery(String,Class)
|
||||||
*/
|
*/
|
||||||
|
@ -98,15 +141,25 @@ public interface QueryProducer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link NativeQuery} instance for the given native SQL query
|
* Create a {@link NativeQuery} instance for the given native SQL query
|
||||||
* using implicit mapping to the specified Java type.
|
* using an implicit mapping to the specified Java type.
|
||||||
* <p>
|
* <ul>
|
||||||
* If the given class is an entity class, this method is equivalent to
|
* <li>If the given class is an entity class, this method is equivalent
|
||||||
* {@code createNativeQuery(sqlString).addEntity("alias1", resultClass)}.
|
* to {@code createNativeQuery(sqlString).addEntity(resultClass)}.
|
||||||
|
* <li>If the given class has a registered
|
||||||
|
* {@link org.hibernate.type.descriptor.java.JavaType}, then the
|
||||||
|
* query must return a result set with a single column whose
|
||||||
|
* {@code JdbcType} is compatible with that {@code JavaType}.
|
||||||
|
* <li>Otherwise, the select items will be packaged into an instance of
|
||||||
|
* the result type. The result type must have an appropriate
|
||||||
|
* constructor with parameter types matching the select items, or it
|
||||||
|
* must be one of the types {@code Object[]}, {@link java.util.List},
|
||||||
|
* {@link java.util.Map}, or {@link jakarta.persistence.Tuple}.
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param sqlString The native (SQL) query string
|
* @param sqlString The native (SQL) query string
|
||||||
* @param resultClass The Java entity type to map results to
|
* @param resultClass The Java type to map results to
|
||||||
*
|
*
|
||||||
* @return The NativeQuery instance for manipulation and execution
|
* @return The {@link NativeQuery} instance for manipulation and execution
|
||||||
*
|
*
|
||||||
* @see jakarta.persistence.EntityManager#createNativeQuery(String,Class)
|
* @see jakarta.persistence.EntityManager#createNativeQuery(String,Class)
|
||||||
*/
|
*/
|
||||||
|
@ -114,13 +167,13 @@ public interface QueryProducer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link NativeQuery} instance for the given native SQL query
|
* Create a {@link NativeQuery} instance for the given native SQL query
|
||||||
* using implicit mapping to the specified Java type.
|
* using an implicit mapping to the specified Java entity type.
|
||||||
* <p>
|
* <p>
|
||||||
* If the given class is an entity class, this method is equivalent to
|
* The given class must be an entity class. This method is equivalent to
|
||||||
* {@code createNativeQuery(sqlString).addEntity(tableAlias, resultClass)}.
|
* {@code createNativeQuery(sqlString).addEntity(tableAlias, resultClass)}.
|
||||||
*
|
*
|
||||||
* @param sqlString Native (SQL) query string
|
* @param sqlString Native (SQL) query string
|
||||||
* @param resultClass The Java entity type to map results to
|
* @param resultClass The Java entity class to map results to
|
||||||
* @param tableAlias The table alias for columns in the result set
|
* @param tableAlias The table alias for columns in the result set
|
||||||
*
|
*
|
||||||
* @return The {@link NativeQuery} instance for manipulation and execution
|
* @return The {@link NativeQuery} instance for manipulation and execution
|
||||||
|
@ -133,13 +186,13 @@ public interface QueryProducer {
|
||||||
* Create a {@link NativeQuery} instance for the given native SQL query
|
* Create a {@link NativeQuery} instance for the given native SQL query
|
||||||
* using an explicit mapping to the specified Java type.
|
* using an explicit mapping to the specified Java type.
|
||||||
* <p>
|
* <p>
|
||||||
* The given result set mapping name must identify a mapping defined by a
|
* The given result set mapping name must identify a mapping defined by
|
||||||
* {@link jakarta.persistence.SqlResultSetMapping} annotation.
|
* a {@link jakarta.persistence.SqlResultSetMapping} annotation.
|
||||||
*
|
*
|
||||||
* @param sqlString The native (SQL) query string
|
* @param sqlString The native (SQL) query string
|
||||||
* @param resultSetMappingName The explicit result mapping name
|
* @param resultSetMappingName The explicit result mapping name
|
||||||
*
|
*
|
||||||
* @return The NativeQuery instance for manipulation and execution
|
* @return The {@link NativeQuery} instance for manipulation and execution
|
||||||
*
|
*
|
||||||
* @see jakarta.persistence.EntityManager#createNativeQuery(String,Class)
|
* @see jakarta.persistence.EntityManager#createNativeQuery(String,Class)
|
||||||
* @see jakarta.persistence.SqlResultSetMapping
|
* @see jakarta.persistence.SqlResultSetMapping
|
||||||
|
@ -153,8 +206,8 @@ public interface QueryProducer {
|
||||||
* Create a {@link NativeQuery} instance for the given native SQL query
|
* Create a {@link NativeQuery} instance for the given native SQL query
|
||||||
* using an explicit mapping to the specified Java type.
|
* using an explicit mapping to the specified Java type.
|
||||||
* <p>
|
* <p>
|
||||||
* The given result set mapping name must identify a mapping defined by a
|
* The given result set mapping name must identify a mapping defined by
|
||||||
* {@link jakarta.persistence.SqlResultSetMapping} annotation.
|
* a {@link jakarta.persistence.SqlResultSetMapping} annotation.
|
||||||
*
|
*
|
||||||
* @param sqlString The native (SQL) query string
|
* @param sqlString The native (SQL) query string
|
||||||
* @param resultSetMappingName The explicit result mapping name
|
* @param resultSetMappingName The explicit result mapping name
|
||||||
|
@ -167,21 +220,69 @@ public interface QueryProducer {
|
||||||
<R> NativeQuery<R> createNativeQuery(String sqlString, String resultSetMappingName, Class<R> resultClass);
|
<R> NativeQuery<R> createNativeQuery(String sqlString, String resultSetMappingName, Class<R> resultClass);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SelectionQuery} reference for the given HQL.
|
* Create a {@link SelectionQuery} reference for the given HQL
|
||||||
|
* {@code select} statement.
|
||||||
|
* <p>
|
||||||
|
* If the statement has no explicit {@code select} list, the
|
||||||
|
* select list is inferred:
|
||||||
|
* <ul>
|
||||||
|
* <li>if there is exactly one root entity in the {@code from}
|
||||||
|
* clause, then that root entity is the only element of the
|
||||||
|
* select list, or
|
||||||
|
* <li>otherwise, if there are multiple root entities in the
|
||||||
|
* {@code from} clause, then the select list contains every
|
||||||
|
* root entity and every non-{@code fetch} joined entity.
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* Only valid for select queries
|
* @implNote This method interprets some queries with an implicit
|
||||||
*
|
* {@code select} list in a quite unintuitive way. In some future
|
||||||
* @see jakarta.persistence.EntityManager#createQuery(String)
|
* release, this method will be modified to throw an exception
|
||||||
|
* when passed a query with a missing {@code select}. For now, use
|
||||||
|
* {@link #createSelectionQuery(String, Class)} to avoid ambiguity.
|
||||||
*
|
*
|
||||||
* @throws IllegalSelectQueryException if the given HQL query
|
* @throws IllegalSelectQueryException if the given HQL query
|
||||||
* is an insert, update or delete query
|
* is an insert, update or delete query
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #createSelectionQuery(String, Class)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.3")
|
||||||
SelectionQuery<?> createSelectionQuery(String hqlString);
|
SelectionQuery<?> createSelectionQuery(String hqlString);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SelectionQuery} reference for the given HQL.
|
* Create a {@link SelectionQuery} instance for the given HQL query
|
||||||
*
|
* string and given query result type.
|
||||||
* Only valid for select queries
|
* <ul>
|
||||||
|
* <li>If the query has a single item in the {@code select} list,
|
||||||
|
* then the select item must be assignable to the given result
|
||||||
|
* type.
|
||||||
|
* <li>Otherwise, if there are multiple select items, then the
|
||||||
|
* select items will be packaged into an instance of the
|
||||||
|
* result type. The result type must have an appropriate
|
||||||
|
* constructor with parameter types matching the select items,
|
||||||
|
* or it must be one of the types {@code Object[]},
|
||||||
|
* {@link java.util.List}, {@link java.util.Map}, or
|
||||||
|
* {@link jakarta.persistence.Tuple}.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* If a query has no explicit {@code select} list, the select list
|
||||||
|
* is inferred from the given query result type:
|
||||||
|
* <ul>
|
||||||
|
* <li>if the result type is an entity type, the query must have
|
||||||
|
* exactly one root entity in the {@code from} clause, it must
|
||||||
|
* be assignable to the result type, and the inferred select
|
||||||
|
* list will contain just that entity, or
|
||||||
|
* <li>otherwise, the select list contains every root entity and
|
||||||
|
* every non-{@code fetch} joined entity, and each query result
|
||||||
|
* will be packaged into an instance of the result type, just
|
||||||
|
* as specified above.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* The returned {@code Query} may be executed by calling
|
||||||
|
* {@link Query#getResultList()} or {@link Query#getSingleResult()}.
|
||||||
|
|
||||||
|
* @param hqlString The HQL query as a string
|
||||||
|
* @param resultType The {@link Class} object representing the
|
||||||
|
* query result type
|
||||||
*
|
*
|
||||||
* @see jakarta.persistence.EntityManager#createQuery(String)
|
* @see jakarta.persistence.EntityManager#createQuery(String)
|
||||||
*
|
*
|
||||||
|
@ -191,14 +292,15 @@ public interface QueryProducer {
|
||||||
<R> SelectionQuery<R> createSelectionQuery(String hqlString, Class<R> resultType);
|
<R> SelectionQuery<R> createSelectionQuery(String hqlString, Class<R> resultType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SelectionQuery} reference for the given Criteria.
|
* Create a {@link SelectionQuery} reference for the given
|
||||||
|
* {@link CriteriaQuery}.
|
||||||
*
|
*
|
||||||
* @see jakarta.persistence.EntityManager#createQuery(CriteriaQuery)
|
* @see jakarta.persistence.EntityManager#createQuery(CriteriaQuery)
|
||||||
*/
|
*/
|
||||||
<R> SelectionQuery<R> createSelectionQuery(CriteriaQuery<R> criteria);
|
<R> SelectionQuery<R> createSelectionQuery(CriteriaQuery<R> criteria);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a MutationQuery reference for the given HQL insert,
|
* Create a {@link MutationQuery} reference for the given HQL insert,
|
||||||
* update, or delete statement.
|
* update, or delete statement.
|
||||||
*
|
*
|
||||||
* @throws IllegalMutationQueryException if the given HQL query
|
* @throws IllegalMutationQueryException if the given HQL query
|
||||||
|
@ -234,7 +336,7 @@ public interface QueryProducer {
|
||||||
* Create a typed {@link Query} instance for the given named query.
|
* Create a typed {@link Query} instance for the given named query.
|
||||||
* The named query might be defined in HQL or in native SQL.
|
* The named query might be defined in HQL or in native SQL.
|
||||||
*
|
*
|
||||||
* @param name the name of a pre-defined, named query
|
* @param name the name of a predefined named query
|
||||||
*
|
*
|
||||||
* @return The {@link Query} instance for manipulation and execution
|
* @return The {@link Query} instance for manipulation and execution
|
||||||
*
|
*
|
||||||
|
@ -244,8 +346,7 @@ public interface QueryProducer {
|
||||||
*
|
*
|
||||||
* @see jakarta.persistence.EntityManager#createNamedQuery(String)
|
* @see jakarta.persistence.EntityManager#createNamedQuery(String)
|
||||||
*
|
*
|
||||||
* @deprecated use {@link #createNamedQuery(String, Class)} or
|
* @deprecated use {@link #createNamedQuery(String, Class)}
|
||||||
* {@link #createNamedMutationQuery(String)}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated(since = "6.0") @SuppressWarnings("rawtypes")
|
@Deprecated(since = "6.0") @SuppressWarnings("rawtypes")
|
||||||
Query createNamedQuery(String name);
|
Query createNamedQuery(String name);
|
||||||
|
@ -269,20 +370,29 @@ public interface QueryProducer {
|
||||||
<R> Query<R> createNamedQuery(String name, Class<R> resultClass);
|
<R> Query<R> createNamedQuery(String name, Class<R> resultClass);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SelectionQuery} instance for the
|
* Create a {@link SelectionQuery} instance for the named
|
||||||
* named {@link jakarta.persistence.NamedQuery}
|
* {@link jakarta.persistence.NamedQuery}.
|
||||||
*
|
*
|
||||||
* @throws IllegalSelectQueryException if the given HQL query is a select query
|
* @implNote This method interprets some queries with an implicit
|
||||||
|
* {@code select} list in a quite unintuitive way. In some future
|
||||||
|
* release, this method will be modified to throw an exception
|
||||||
|
* when passed a query with a missing {@code select}. For now, use
|
||||||
|
* {@link #createNamedSelectionQuery(String, Class)} to avoid
|
||||||
|
* ambiguity.
|
||||||
|
*
|
||||||
|
* @throws IllegalSelectQueryException if the given HQL query is not a select query
|
||||||
* @throws UnknownNamedQueryException if no query has been defined with the given name
|
* @throws UnknownNamedQueryException if no query has been defined with the given name
|
||||||
|
*
|
||||||
|
* @deprecated use {@link #createNamedSelectionQuery(String, Class)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.3")
|
||||||
SelectionQuery<?> createNamedSelectionQuery(String name);
|
SelectionQuery<?> createNamedSelectionQuery(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SelectionQuery} instance for the
|
* Create a {@link SelectionQuery} instance for the named
|
||||||
* named {@link jakarta.persistence.NamedQuery} with the expected
|
* {@link jakarta.persistence.NamedQuery} with the given result type.
|
||||||
* result-type
|
|
||||||
*
|
*
|
||||||
* @throws IllegalSelectQueryException if the given HQL query is a select query
|
* @throws IllegalSelectQueryException if the given HQL query is not a select query
|
||||||
* @throws UnknownNamedQueryException if no query has been defined with the given name
|
* @throws UnknownNamedQueryException if no query has been defined with the given name
|
||||||
*/
|
*/
|
||||||
<R> SelectionQuery<R> createNamedSelectionQuery(String name, Class<R> resultType);
|
<R> SelectionQuery<R> createNamedSelectionQuery(String name, Class<R> resultType);
|
||||||
|
@ -301,7 +411,7 @@ public interface QueryProducer {
|
||||||
/**
|
/**
|
||||||
* Create a {@link Query} instance for the named query.
|
* Create a {@link Query} instance for the named query.
|
||||||
*
|
*
|
||||||
* @param queryName the name of a pre-defined, named query
|
* @param queryName the name of a predefined named query
|
||||||
*
|
*
|
||||||
* @return The {@link Query} instance for manipulation and execution
|
* @return The {@link Query} instance for manipulation and execution
|
||||||
*
|
*
|
||||||
|
@ -317,7 +427,7 @@ public interface QueryProducer {
|
||||||
/**
|
/**
|
||||||
* Get a {@link NativeQuery} instance for a named native SQL query
|
* Get a {@link NativeQuery} instance for a named native SQL query
|
||||||
*
|
*
|
||||||
* @param name The name of the pre-defined query
|
* @param name The name of the predefined query
|
||||||
*
|
*
|
||||||
* @return The {@link NativeQuery} instance for manipulation and execution
|
* @return The {@link NativeQuery} instance for manipulation and execution
|
||||||
*
|
*
|
||||||
|
@ -329,7 +439,7 @@ public interface QueryProducer {
|
||||||
/**
|
/**
|
||||||
* Get a {@link NativeQuery} instance for a named native SQL query
|
* Get a {@link NativeQuery} instance for a named native SQL query
|
||||||
*
|
*
|
||||||
* @param name The name of the pre-defined query
|
* @param name The name of the predefined query
|
||||||
*
|
*
|
||||||
* @return The {@link NativeQuery} instance for manipulation and execution
|
* @return The {@link NativeQuery} instance for manipulation and execution
|
||||||
*
|
*
|
||||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.QueryException;
|
import org.hibernate.QueryException;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||||
|
@ -1157,31 +1158,56 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
|
protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
|
||||||
// for now, this is slightly different to the legacy behavior where
|
if ( fromClause.getNumberOfRoots() == 0 ) {
|
||||||
// the root and each non-fetched-join was selected. For now, here, we simply
|
// should be impossible to get here
|
||||||
// select the root
|
throw new AssertionFailure( "query has no 'select' clause, and no root entities");
|
||||||
final SqmSelectClause selectClause;
|
}
|
||||||
|
|
||||||
final boolean expectingArray = expectedResultType != null && expectedResultType.isArray();
|
final NodeBuilder nodeBuilder = creationContext.getNodeBuilder();
|
||||||
if ( expectingArray ) {
|
|
||||||
// triggers legacy interpretation of returning all roots
|
final SqmSelectClause selectClause;
|
||||||
// and non-fetched joins
|
final boolean singleEntityResult;
|
||||||
selectClause = new SqmSelectClause(
|
if ( expectedResultType == null ) {
|
||||||
false,
|
// no result type was specified
|
||||||
creationContext.getNodeBuilder()
|
// - if there is a single root entity return the entity,
|
||||||
);
|
// even if it has non-fetch joins (ugh!)
|
||||||
|
// - otherwise, return all entities in an Object[] array,
|
||||||
|
// including non-fetch joins
|
||||||
|
selectClause = new SqmSelectClause( false, nodeBuilder );
|
||||||
|
singleEntityResult = fromClause.getNumberOfRoots() == 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
selectClause = new SqmSelectClause(
|
singleEntityResult = creationContext.getJpaMetamodel().findEntityType( expectedResultType ) != null;
|
||||||
false,
|
if ( singleEntityResult ) {
|
||||||
fromClause.getNumberOfRoots(),
|
// the result type is an entity class
|
||||||
creationContext.getNodeBuilder()
|
if ( fromClause.getNumberOfRoots() > 1 ) {
|
||||||
);
|
// multiple root entities
|
||||||
|
throw new SemanticException( "query has no 'select' clause, and multiple root entities, but query result type is an entity class"
|
||||||
|
+ " (specify an explicit 'select' list, or a different result type, for example, 'Object[].class')");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final SqmRoot<?> sqmRoot = fromClause.getRoots().get(0);
|
||||||
|
if ( sqmRoot instanceof SqmCteRoot ) {
|
||||||
|
throw new SemanticException( "query has no 'select' clause, and the 'from' clause refers to a CTE, but query result type is an entity class"
|
||||||
|
+ " (specify an explicit 'select' list)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// exactly one root entity, return it
|
||||||
|
// (joined entities are not returned)
|
||||||
|
selectClause = new SqmSelectClause( false, 1, nodeBuilder );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// the result type is not an entity class
|
||||||
|
// return all root entities and non-fetch joins
|
||||||
|
selectClause = new SqmSelectClause( false, nodeBuilder );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fromClause.visitRoots( (sqmRoot) -> {
|
fromClause.visitRoots( (sqmRoot) -> {
|
||||||
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), creationContext.getNodeBuilder() ) );
|
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), nodeBuilder) );
|
||||||
if ( expectingArray ) {
|
if ( !singleEntityResult ) {
|
||||||
applyJoinsToInferredSelectClause( sqmRoot, selectClause );
|
applyJoinsToInferredSelectClause( sqmRoot, selectClause );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation
|
||||||
public HqlInterpretation resolveHqlInterpretation(String queryString, Class<?> expectedResultType, Function<String, SqmStatement<?>> creator) {
|
public HqlInterpretation resolveHqlInterpretation(String queryString, Class<?> expectedResultType, Function<String, SqmStatement<?>> creator) {
|
||||||
final StatisticsImplementor statistics = statisticsSupplier.get();
|
final StatisticsImplementor statistics = statisticsSupplier.get();
|
||||||
final boolean stats = statistics.isStatisticsEnabled();
|
final boolean stats = statistics.isStatisticsEnabled();
|
||||||
final long startTime = ( stats ) ? System.nanoTime() : 0L;
|
final long startTime = stats ? System.nanoTime() : 0L;
|
||||||
final SqmStatement<?> sqmStatement = creator.apply( queryString );
|
final SqmStatement<?> sqmStatement = creator.apply( queryString );
|
||||||
|
|
||||||
final DomainParameterXref domainParameterXref;
|
final DomainParameterXref domainParameterXref;
|
||||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.query.internal;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import jakarta.persistence.Tuple;
|
|
||||||
|
|
||||||
import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;
|
import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;
|
||||||
import org.hibernate.query.QueryLogging;
|
import org.hibernate.query.QueryLogging;
|
||||||
|
@ -23,6 +22,7 @@ import org.hibernate.query.spi.SimpleHqlInterpretationImpl;
|
||||||
import org.hibernate.query.sql.spi.ParameterInterpretation;
|
import org.hibernate.query.sql.spi.ParameterInterpretation;
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
@ -99,6 +99,45 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
|
||||||
log.tracef( "QueryPlan#cacheNonSelectQueryPlan(%s)", key );
|
log.tracef( "QueryPlan#cacheNonSelectQueryPlan(%s)", key );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String typedKey(String queryString, Class<?> expectedResultType) {
|
||||||
|
return expectedResultType.getName() + ":" + queryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putInCache(String queryString, Class<?> expectedResultType, HqlInterpretation hqlInterpretation) {
|
||||||
|
final SqmStatement<?> statement = hqlInterpretation.getSqmStatement();
|
||||||
|
if ( statement instanceof SqmSelectStatement && expectedResultType != null ) {
|
||||||
|
hqlInterpretationCache.put( typedKey( queryString, expectedResultType ), hqlInterpretation);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hqlInterpretationCache.put( queryString, hqlInterpretation );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HqlInterpretation getFromCache(String queryString, Class<?> expectedResultType) {
|
||||||
|
if ( expectedResultType != null ) {
|
||||||
|
final HqlInterpretation typedHqlInterpretation =
|
||||||
|
hqlInterpretationCache.get( typedKey( queryString, expectedResultType ) );
|
||||||
|
if ( typedHqlInterpretation != null ) {
|
||||||
|
return typedHqlInterpretation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final HqlInterpretation hqlInterpretation = hqlInterpretationCache.get( queryString );
|
||||||
|
if ( hqlInterpretation != null ) {
|
||||||
|
final SqmStatement<?> statement = hqlInterpretation.getSqmStatement();
|
||||||
|
if ( statement instanceof SqmSelectStatement && expectedResultType != null ) {
|
||||||
|
final Class<?> resultType = ((SqmSelectStatement<?>) statement).getSelection().getJavaType();
|
||||||
|
return expectedResultType.equals( resultType ) ? hqlInterpretation : null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return hqlInterpretation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HqlInterpretation resolveHqlInterpretation(
|
public HqlInterpretation resolveHqlInterpretation(
|
||||||
String queryString,
|
String queryString,
|
||||||
|
@ -106,17 +145,7 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
|
||||||
Function<String, SqmStatement<?>> creator) {
|
Function<String, SqmStatement<?>> creator) {
|
||||||
log.tracef( "QueryPlan#resolveHqlInterpretation( `%s` )", queryString );
|
log.tracef( "QueryPlan#resolveHqlInterpretation( `%s` )", queryString );
|
||||||
|
|
||||||
final String cacheKey;
|
final HqlInterpretation existing = getFromCache( queryString, expectedResultType );
|
||||||
if ( expectedResultType != null
|
|
||||||
&& ( expectedResultType.isArray() || Tuple.class.isAssignableFrom( expectedResultType ) ) ) {
|
|
||||||
cacheKey = "multi_" + queryString;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cacheKey = queryString;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
final HqlInterpretation existing = hqlInterpretationCache.get( cacheKey );
|
|
||||||
if ( existing != null ) {
|
if ( existing != null ) {
|
||||||
final StatisticsImplementor statistics = statisticsSupplier.get();
|
final StatisticsImplementor statistics = statisticsSupplier.get();
|
||||||
if ( statistics.isStatisticsEnabled() ) {
|
if ( statistics.isStatisticsEnabled() ) {
|
||||||
|
@ -124,10 +153,12 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
|
||||||
}
|
}
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
final HqlInterpretation hqlInterpretation = createHqlInterpretation( queryString, creator, statisticsSupplier );
|
final HqlInterpretation hqlInterpretation =
|
||||||
hqlInterpretationCache.put( cacheKey, hqlInterpretation );
|
createHqlInterpretation( queryString, creator, statisticsSupplier );
|
||||||
return hqlInterpretation;
|
putInCache( queryString, expectedResultType, hqlInterpretation );
|
||||||
|
return hqlInterpretation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static HqlInterpretation createHqlInterpretation(
|
protected static HqlInterpretation createHqlInterpretation(
|
||||||
|
@ -136,7 +167,7 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
|
||||||
Supplier<StatisticsImplementor> statisticsSupplier) {
|
Supplier<StatisticsImplementor> statisticsSupplier) {
|
||||||
final StatisticsImplementor statistics = statisticsSupplier.get();
|
final StatisticsImplementor statistics = statisticsSupplier.get();
|
||||||
final boolean stats = statistics.isStatisticsEnabled();
|
final boolean stats = statistics.isStatisticsEnabled();
|
||||||
final long startTime = ( stats ) ? System.nanoTime() : 0L;
|
final long startTime = stats ? System.nanoTime() : 0L;
|
||||||
|
|
||||||
final SqmStatement<?> sqmStatement = creator.apply( queryString );
|
final SqmStatement<?> sqmStatement = creator.apply( queryString );
|
||||||
final ParameterMetadataImplementor parameterMetadata;
|
final ParameterMetadataImplementor parameterMetadata;
|
||||||
|
|
|
@ -106,7 +106,7 @@ public class Builders {
|
||||||
Class<R> jdbcJavaType,
|
Class<R> jdbcJavaType,
|
||||||
AttributeConverter<O, R> converter,
|
AttributeConverter<O, R> converter,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
return new DynamicResultBuilderBasicConverted( columnAlias, domainJavaType, jdbcJavaType, converter, sessionFactory );
|
return new DynamicResultBuilderBasicConverted<>( columnAlias, domainJavaType, jdbcJavaType, converter, sessionFactory );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <R> ResultBuilder converted(
|
public static <R> ResultBuilder converted(
|
||||||
|
@ -123,7 +123,7 @@ public class Builders {
|
||||||
Class<R> jdbcJavaType,
|
Class<R> jdbcJavaType,
|
||||||
Class<? extends AttributeConverter<O,R>> converterJavaType,
|
Class<? extends AttributeConverter<O,R>> converterJavaType,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
return new DynamicResultBuilderBasicConverted( columnAlias, domainJavaType, jdbcJavaType, converterJavaType, sessionFactory );
|
return new DynamicResultBuilderBasicConverted<>( columnAlias, domainJavaType, jdbcJavaType, converterJavaType, sessionFactory );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResultBuilderBasicValued scalar(int position) {
|
public static ResultBuilderBasicValued scalar(int position) {
|
||||||
|
@ -233,10 +233,8 @@ public class Builders {
|
||||||
String entityName,
|
String entityName,
|
||||||
LockMode explicitLockMode,
|
LockMode explicitLockMode,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
final RuntimeMetamodels runtimeMetamodels = sessionFactory.getRuntimeMetamodels();
|
final EntityMappingType entityMapping = sessionFactory.getRuntimeMetamodels().getEntityMappingType( entityName );
|
||||||
final EntityMappingType entityMapping = runtimeMetamodels.getEntityMappingType( entityName );
|
return new DynamicResultBuilderEntityCalculated( entityMapping, tableAlias, explicitLockMode );
|
||||||
|
|
||||||
return new DynamicResultBuilderEntityCalculated( entityMapping, tableAlias, explicitLockMode, sessionFactory );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DynamicFetchBuilderLegacy fetch(String tableAlias, String ownerTableAlias, String joinPropertyName) {
|
public static DynamicFetchBuilderLegacy fetch(String tableAlias, String ownerTableAlias, String joinPropertyName) {
|
||||||
|
@ -252,9 +250,9 @@ public class Builders {
|
||||||
public static ResultBuilder resultClassBuilder(
|
public static ResultBuilder resultClassBuilder(
|
||||||
Class<?> resultMappingClass,
|
Class<?> resultMappingClass,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
final MappingMetamodelImplementor mappingMetamodel = sessionFactory
|
final MappingMetamodelImplementor mappingMetamodel =
|
||||||
.getRuntimeMetamodels()
|
sessionFactory.getRuntimeMetamodels()
|
||||||
.getMappingMetamodel();
|
.getMappingMetamodel();
|
||||||
final EntityMappingType entityMappingType = mappingMetamodel.findEntityDescriptor( resultMappingClass );
|
final EntityMappingType entityMappingType = mappingMetamodel.findEntityDescriptor( resultMappingClass );
|
||||||
if ( entityMappingType != null ) {
|
if ( entityMappingType != null ) {
|
||||||
// the resultClass is an entity
|
// the resultClass is an entity
|
||||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.query.results.dynamic;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.query.NativeQuery;
|
import org.hibernate.query.NativeQuery;
|
||||||
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
||||||
|
@ -35,18 +34,14 @@ public class DynamicResultBuilderEntityCalculated implements DynamicResultBuilde
|
||||||
private final String tableAlias;
|
private final String tableAlias;
|
||||||
private final LockMode explicitLockMode;
|
private final LockMode explicitLockMode;
|
||||||
|
|
||||||
private final SessionFactoryImplementor sessionFactory;
|
|
||||||
|
|
||||||
public DynamicResultBuilderEntityCalculated(
|
public DynamicResultBuilderEntityCalculated(
|
||||||
EntityMappingType entityMapping,
|
EntityMappingType entityMapping,
|
||||||
String tableAlias,
|
String tableAlias,
|
||||||
LockMode explicitLockMode,
|
LockMode explicitLockMode) {
|
||||||
SessionFactoryImplementor sessionFactory) {
|
|
||||||
this.entityMapping = entityMapping;
|
this.entityMapping = entityMapping;
|
||||||
this.navigablePath = new NavigablePath( entityMapping.getEntityName() );
|
this.navigablePath = new NavigablePath( entityMapping.getEntityName() );
|
||||||
this.tableAlias = tableAlias;
|
this.tableAlias = tableAlias;
|
||||||
this.explicitLockMode = explicitLockMode;
|
this.explicitLockMode = explicitLockMode;
|
||||||
this.sessionFactory = sessionFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -45,11 +45,13 @@ public class ImplicitResultClassBuilder implements ResultBuilder {
|
||||||
DomainResultCreationState domainResultCreationState) {
|
DomainResultCreationState domainResultCreationState) {
|
||||||
assert resultPosition == 0;
|
assert resultPosition == 0;
|
||||||
|
|
||||||
final SessionFactoryImplementor sessionFactory = domainResultCreationState.getSqlAstCreationState()
|
final SessionFactoryImplementor sessionFactory =
|
||||||
.getCreationContext()
|
domainResultCreationState.getSqlAstCreationState()
|
||||||
.getSessionFactory();
|
.getCreationContext()
|
||||||
|
.getSessionFactory();
|
||||||
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
|
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
|
||||||
final SqlExpressionResolver sqlExpressionResolver = domainResultCreationState.getSqlAstCreationState().getSqlExpressionResolver();
|
final SqlExpressionResolver sqlExpressionResolver =
|
||||||
|
domainResultCreationState.getSqlAstCreationState().getSqlExpressionResolver();
|
||||||
|
|
||||||
final int jdbcResultPosition = 1;
|
final int jdbcResultPosition = 1;
|
||||||
|
|
||||||
|
|
|
@ -1014,6 +1014,11 @@ public class NativeQueryImpl<R>
|
||||||
return addEntity( entityType.getName() );
|
return addEntity( entityType.getName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NativeQueryImplementor<R> addEntity(Class<R> entityType, LockMode lockMode) {
|
||||||
|
return addEntity( StringHelper.unqualify( entityType.getName() ), entityType.getName(), lockMode);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NativeQueryImplementor<R> addEntity(String tableAlias, @SuppressWarnings("rawtypes") Class entityClass) {
|
public NativeQueryImplementor<R> addEntity(String tableAlias, @SuppressWarnings("rawtypes") Class entityClass) {
|
||||||
return addEntity( tableAlias, entityClass.getName() );
|
return addEntity( tableAlias, entityClass.getName() );
|
||||||
|
|
|
@ -112,6 +112,8 @@ public interface NativeQueryImplementor<R> extends QueryImplementor<R>, NativeQu
|
||||||
@Override
|
@Override
|
||||||
NativeQueryImplementor<R> addEntity(@SuppressWarnings("rawtypes") Class entityType);
|
NativeQueryImplementor<R> addEntity(@SuppressWarnings("rawtypes") Class entityType);
|
||||||
|
|
||||||
|
NativeQueryImplementor<R> addEntity(Class<R> entityType, LockMode lockMode);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
NativeQueryImplementor<R> addEntity(String tableAlias, @SuppressWarnings("rawtypes") Class entityType);
|
NativeQueryImplementor<R> addEntity(String tableAlias, @SuppressWarnings("rawtypes") Class entityType);
|
||||||
|
|
||||||
|
|
|
@ -238,7 +238,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasJoins() {
|
public boolean hasJoins() {
|
||||||
return !( joins == null || joins.isEmpty() );
|
return joins != null && !joins.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -330,7 +330,6 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <A> SqmSingularJoin<T, A> join(SingularAttribute<? super T, A> attribute, JoinType jt) {
|
public <A> SqmSingularJoin<T, A> join(SingularAttribute<? super T, A> attribute, JoinType jt) {
|
||||||
final SqmSingularJoin<T, A> join = buildSingularJoin( (SingularPersistentAttribute<? super T, A>) attribute, SqmJoinType.from( jt ), false );
|
final SqmSingularJoin<T, A> join = buildSingularJoin( (SingularPersistentAttribute<? super T, A>) attribute, SqmJoinType.from( jt ), false );
|
||||||
addSqmJoin( join );
|
addSqmJoin( join );
|
||||||
|
@ -646,7 +645,6 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <A> SqmSingularJoin<T, A> fetch(SingularAttribute<? super T, A> attribute, JoinType jt) {
|
public <A> SqmSingularJoin<T, A> fetch(SingularAttribute<? super T, A> attribute, JoinType jt) {
|
||||||
final SqmSingularJoin<T, A> join = buildSingularJoin(
|
final SqmSingularJoin<T, A> join = buildSingularJoin(
|
||||||
(SingularPersistentAttribute<? super T, A>) attribute,
|
(SingularPersistentAttribute<? super T, A>) attribute,
|
||||||
|
@ -663,7 +661,6 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <A> SqmAttributeJoin<T, A> fetch(PluralAttribute<? super T, ?, A> attribute, JoinType jt) {
|
public <A> SqmAttributeJoin<T, A> fetch(PluralAttribute<? super T, ?, A> attribute, JoinType jt) {
|
||||||
return buildJoin(
|
return buildJoin(
|
||||||
(PluralPersistentAttribute<? super T, ?, A>) attribute,
|
(PluralPersistentAttribute<? super T, ?, A>) attribute,
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -34,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
public class ImplicitSelectWithJoinTests {
|
public class ImplicitSelectWithJoinTests {
|
||||||
private static final String HQL = "from Product p join p.vendor v where v.name like '%Steve%'";
|
private static final String HQL = "from Product p join p.vendor v where v.name like '%Steve%'";
|
||||||
private static final String HQL2 = "select p " + HQL;
|
private static final String HQL2 = "select p " + HQL;
|
||||||
|
private static final String HQL3 = "from Product q join q.vendor w, Product p join p.vendor v where v.name like '%Steve%' and w.name like '%Gavin%'";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoExpectedType(SessionFactoryScope scope) {
|
public void testNoExpectedType(SessionFactoryScope scope) {
|
||||||
|
@ -79,8 +81,93 @@ public class ImplicitSelectWithJoinTests {
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArrayResultNoResultType(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final SelectionQuery<?> query = session.createSelectionQuery( HQL3 );
|
||||||
|
|
||||||
|
{
|
||||||
|
final List<?> results = query.list();
|
||||||
|
assertThat( results ).hasSize( 1 );
|
||||||
|
final Object result = results.get( 0 );
|
||||||
|
assertThat( result ).isNotNull();
|
||||||
|
assertInstanceOf( Object[].class, result );
|
||||||
|
assertThat( (Object[]) result ).hasSize(4);
|
||||||
|
assertThat( (Object[]) result ).hasExactlyElementsOfTypes(Product.class, Vendor.class, Product.class, Vendor.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
final ScrollableResults<?> results = query.scroll();
|
||||||
|
assertThat( results.next() ).isTrue();
|
||||||
|
final Object result = results.get();
|
||||||
|
assertThat( result ).isNotNull();
|
||||||
|
assertInstanceOf( Object[].class, result );
|
||||||
|
assertThat( (Object[]) result ).hasSize(4);
|
||||||
|
assertThat( (Object[]) result ).hasExactlyElementsOfTypes(Product.class, Vendor.class, Product.class, Vendor.class);
|
||||||
|
assertThat( results.next() ).isFalse();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// frankly, this would be more consistent and more backward-compatible
|
||||||
|
// scope.inTransaction( (session) -> {
|
||||||
|
// final SelectionQuery<?> query = session.createSelectionQuery( HQL );
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// final List<?> results = query.list();
|
||||||
|
// assertThat( results ).hasSize( 1 );
|
||||||
|
// final Object result = results.get( 0 );
|
||||||
|
// assertThat( result ).isNotNull();
|
||||||
|
// assertInstanceOf( Object[].class, result );
|
||||||
|
// assertThat( (Object[]) result ).hasSize(2);
|
||||||
|
// assertThat( (Object[]) result ).hasExactlyElementsOfTypes(Product.class, Vendor.class);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// final ScrollableResults<?> results = query.scroll();
|
||||||
|
// assertThat( results.next() ).isTrue();
|
||||||
|
// final Object result = results.get();
|
||||||
|
// assertThat( result ).isNotNull();
|
||||||
|
// assertInstanceOf( Object[].class, result );
|
||||||
|
// assertThat( (Object[]) result ).hasSize(2);
|
||||||
|
// assertThat( (Object[]) result ).hasExactlyElementsOfTypes(Product.class, Vendor.class);
|
||||||
|
// assertThat( results.next() ).isFalse();
|
||||||
|
// }
|
||||||
|
// } );
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testArrayResult(SessionFactoryScope scope) {
|
public void testArrayResult(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final SelectionQuery<Object[]> query = session.createSelectionQuery( HQL3, Object[].class );
|
||||||
|
|
||||||
|
{
|
||||||
|
final List<Object[]> results = query.list();
|
||||||
|
assertThat( results ).hasSize( 1 );
|
||||||
|
final Object[] result = results.get( 0 );
|
||||||
|
assertThat( result ).isNotNull();
|
||||||
|
assertThat( result ).hasSize( 4 );
|
||||||
|
assertThat( result[ 0 ] ).isNotNull();
|
||||||
|
assertThat( result[ 1 ] ).isNotNull();
|
||||||
|
assertThat( result[ 2 ] ).isNotNull();
|
||||||
|
assertThat( result[ 3 ] ).isNotNull();
|
||||||
|
assertThat( result ).hasExactlyElementsOfTypes(Product.class, Vendor.class, Product.class, Vendor.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
final ScrollableResults<Object[]> results = query.scroll();
|
||||||
|
assertThat( results.next() ).isTrue();
|
||||||
|
final Object[] result = results.get();
|
||||||
|
assertThat( results.next() ).isFalse();
|
||||||
|
assertThat( result ).isNotNull();
|
||||||
|
assertThat( result ).hasSize( 4 );
|
||||||
|
assertThat( result[ 0 ] ).isNotNull();
|
||||||
|
assertThat( result[ 1 ] ).isNotNull();
|
||||||
|
assertThat( result[ 2 ] ).isNotNull();
|
||||||
|
assertThat( result[ 3 ] ).isNotNull();
|
||||||
|
assertThat( result ).hasExactlyElementsOfTypes(Product.class, Vendor.class, Product.class, Vendor.class);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final SelectionQuery<Object[]> query = session.createSelectionQuery( HQL, Object[].class );
|
final SelectionQuery<Object[]> query = session.createSelectionQuery( HQL, Object[].class );
|
||||||
|
|
||||||
|
@ -92,6 +179,7 @@ public class ImplicitSelectWithJoinTests {
|
||||||
assertThat( result ).hasSize( 2 );
|
assertThat( result ).hasSize( 2 );
|
||||||
assertThat( result[ 0 ] ).isNotNull();
|
assertThat( result[ 0 ] ).isNotNull();
|
||||||
assertThat( result[ 1 ] ).isNotNull();
|
assertThat( result[ 1 ] ).isNotNull();
|
||||||
|
assertThat( result ).hasExactlyElementsOfTypes(Product.class, Vendor.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -103,6 +191,7 @@ public class ImplicitSelectWithJoinTests {
|
||||||
assertThat( result ).hasSize( 2 );
|
assertThat( result ).hasSize( 2 );
|
||||||
assertThat( result[ 0 ] ).isNotNull();
|
assertThat( result[ 0 ] ).isNotNull();
|
||||||
assertThat( result[ 1 ] ).isNotNull();
|
assertThat( result[ 1 ] ).isNotNull();
|
||||||
|
assertThat( result ).hasExactlyElementsOfTypes(Product.class, Vendor.class);
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
@ -161,10 +250,14 @@ public class ImplicitSelectWithJoinTests {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void prepareTestData(SessionFactoryScope scope) {
|
public void prepareTestData(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final Vendor vendor = new Vendor( 1, "Steve's Curios", "Acme Corp." );
|
final Vendor steve = new Vendor( 1, "Steve's Curios", "Acme Corp." );
|
||||||
final Product product = new Product( 10, UUID.fromString( "53886a8a-7082-4879-b430-25cb94415be8" ), vendor );
|
final Product product1 = new Product( 10, UUID.fromString( "53886a8a-7082-4879-b430-25cb94415be8" ), steve );
|
||||||
session.persist( vendor );
|
final Vendor gavin = new Vendor( 2, "Gavin & Associates", "Acme Corp." );
|
||||||
session.persist( product );
|
final Product product2 = new Product( 11, UUID.fromString( "53886a8b-3083-4879-b431-25cb95515be9" ), gavin );
|
||||||
|
session.persist( steve );
|
||||||
|
session.persist( product1 );
|
||||||
|
session.persist( gavin );
|
||||||
|
session.persist( product2 );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue