Skip to content

Commit 4eee305

Browse files
committed
HHH-16747 nail down query result types and actually document the semantics
1 parent c5ecf5d commit 4eee305

File tree

13 files changed

+367
-108
lines changed

13 files changed

+367
-108
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ else if ( List.class.equals( resultClass ) ) {
861861
query.setTupleTransformer( NativeQueryListTransformer.INSTANCE );
862862
}
863863
else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) {
864-
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
864+
query.addEntity( resultClass, LockMode.READ );
865865
}
866866
else if ( resultClass != Object.class && resultClass != Object[].class ) {
867867
if ( isClass( resultClass )

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ public interface JpaMetamodel extends Metamodel {
8080

8181
/**
8282
* Returns a map that gives access to the enum literal expressions that can be used in queries.
83-
* The key is the short-hand enum literal. The value is a map, from enum class to the actual enum value.
84-
* This is needed for parsing short-hand enum literals that don't use FQNs.
83+
* The key is the shorthand enum literal. The value is a map, from enum class to the actual enum value.
84+
* This is needed for parsing shorthand enum literals that don't use FQNs.
8585
*/
8686
Map<String, Map<Class<?>, Enum<?>>> getAllowedEnumLiteralTexts();
8787

hibernate-core/src/main/java/org/hibernate/query/QueryProducer.java

Lines changed: 151 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,76 @@ public interface QueryProducer {
2929
/**
3030
* Create a {@link Query} instance for the given HQL query, or
3131
* HQL insert, update, or delete statement.
32+
* <p>
33+
* If a query has no explicit {@code select} list, the select list
34+
* is inferred:
35+
* <ul>
36+
* <li>if there is exactly one root entity in the {@code from}
37+
* clause, then that root entity is the only element of the
38+
* select list, or
39+
* <li>otherwise, if there are multiple root entities in the
40+
* {@code from} clause, then the select list contains every
41+
* root entity and every non-{@code fetch} joined entity.
42+
* </ul>
3243
*
3344
* @apiNote Returns a raw {@code Query} type instead of a wildcard
3445
* type {@code Query<?>}, to match the signature of the JPA method
3546
* {@link jakarta.persistence.EntityManager#createQuery(String)}.
3647
*
48+
* @implNote This method interprets some queries with an implicit
49+
* {@code select} list in a quite unintuitive way. In some future
50+
* release, this method will be modified to throw an exception
51+
* when passed a query with a missing {@code select}. For now, use
52+
* {@link #createQuery(String, Class)} to avoid ambiguity.
53+
*
3754
* @param queryString The HQL query
3855
*
3956
* @return The {@link Query} instance for manipulation and execution
4057
*
4158
* @see jakarta.persistence.EntityManager#createQuery(String)
4259
*
4360
* @deprecated use {@link #createQuery(String, Class)},
44-
* {@link #createSelectionQuery} or {@link #createMutationQuery(String)}
45-
* depending on intention
61+
* {@link #createSelectionQuery(String, Class)}, or
62+
* {@link #createMutationQuery(String)} depending on intention
4663
*/
4764
@Deprecated(since = "6.0") @SuppressWarnings("rawtypes")
4865
Query createQuery(String queryString);
4966

5067
/**
51-
* Create a typed {@link Query} instance for the given HQL query string.
68+
* Create a typed {@link Query} instance for the given HQL query
69+
* string and given query result type.
70+
* <ul>
71+
* <li>If the query has a single item in the {@code select} list,
72+
* then the select item must be assignable to the given result
73+
* type.
74+
* <li>Otherwise, if there are multiple select items, then the
75+
* select items will be packaged into an instance of the
76+
* result type. The result type must have an appropriate
77+
* constructor with parameter types matching the select items,
78+
* or it must be one of the types {@code Object[]},
79+
* {@link java.util.List}, {@link java.util.Map}, or
80+
* {@link jakarta.persistence.Tuple}.
81+
* </ul>
82+
* <p>
83+
* If a query has no explicit {@code select} list, the select list
84+
* is inferred from the given query result type:
85+
* <ul>
86+
* <li>if the result type is an entity type, the query must have
87+
* exactly one root entity in the {@code from} clause, it must
88+
* be assignable to the result type, and the inferred select
89+
* list will contain just that entity, or
90+
* <li>otherwise, the select list contains every root entity and
91+
* every non-{@code fetch} joined entity, and each query result
92+
* will be packaged into an instance of the result type, just
93+
* as specified above.
94+
* </ul>
5295
* <p>
5396
* The returned {@code Query} may be executed by calling
5497
* {@link Query#getResultList()} or {@link Query#getSingleResult()}.
5598
*
5699
* @param queryString The HQL query
57100
* @param resultClass The type of the query result
58-
* @return The Query instance for manipulation and execution
101+
* @return The {@link Query} instance for manipulation and execution
59102
*
60103
* @see jakarta.persistence.EntityManager#createQuery(String,Class)
61104
*/
@@ -98,29 +141,39 @@ public interface QueryProducer {
98141

99142
/**
100143
* Create a {@link NativeQuery} instance for the given native SQL query
101-
* using implicit mapping to the specified Java type.
102-
* <p>
103-
* If the given class is an entity class, this method is equivalent to
104-
* {@code createNativeQuery(sqlString).addEntity("alias1", resultClass)}.
144+
* using an implicit mapping to the specified Java type.
145+
* <ul>
146+
* <li>If the given class is an entity class, this method is equivalent
147+
* to {@code createNativeQuery(sqlString).addEntity(resultClass)}.
148+
* <li>If the given class has a registered
149+
* {@link org.hibernate.type.descriptor.java.JavaType}, then the
150+
* query must return a result set with a single column whose
151+
* {@code JdbcType} is compatible with that {@code JavaType}.
152+
* <li>Otherwise, the select items will be packaged into an instance of
153+
* the result type. The result type must have an appropriate
154+
* constructor with parameter types matching the select items, or it
155+
* must be one of the types {@code Object[]}, {@link java.util.List},
156+
* {@link java.util.Map}, or {@link jakarta.persistence.Tuple}.
157+
* </ul>
105158
*
106159
* @param sqlString The native (SQL) query string
107-
* @param resultClass The Java entity type to map results to
160+
* @param resultClass The Java type to map results to
108161
*
109-
* @return The NativeQuery instance for manipulation and execution
162+
* @return The {@link NativeQuery} instance for manipulation and execution
110163
*
111164
* @see jakarta.persistence.EntityManager#createNativeQuery(String,Class)
112165
*/
113166
<R> NativeQuery<R> createNativeQuery(String sqlString, Class<R> resultClass);
114167

115168
/**
116169
* Create a {@link NativeQuery} instance for the given native SQL query
117-
* using implicit mapping to the specified Java type.
170+
* using an implicit mapping to the specified Java entity type.
118171
* <p>
119-
* If the given class is an entity class, this method is equivalent to
172+
* The given class must be an entity class. This method is equivalent to
120173
* {@code createNativeQuery(sqlString).addEntity(tableAlias, resultClass)}.
121174
*
122175
* @param sqlString Native (SQL) query string
123-
* @param resultClass The Java entity type to map results to
176+
* @param resultClass The Java entity class to map results to
124177
* @param tableAlias The table alias for columns in the result set
125178
*
126179
* @return The {@link NativeQuery} instance for manipulation and execution
@@ -133,13 +186,13 @@ public interface QueryProducer {
133186
* Create a {@link NativeQuery} instance for the given native SQL query
134187
* using an explicit mapping to the specified Java type.
135188
* <p>
136-
* The given result set mapping name must identify a mapping defined by a
137-
* {@link jakarta.persistence.SqlResultSetMapping} annotation.
189+
* The given result set mapping name must identify a mapping defined by
190+
* a {@link jakarta.persistence.SqlResultSetMapping} annotation.
138191
*
139192
* @param sqlString The native (SQL) query string
140193
* @param resultSetMappingName The explicit result mapping name
141194
*
142-
* @return The NativeQuery instance for manipulation and execution
195+
* @return The {@link NativeQuery} instance for manipulation and execution
143196
*
144197
* @see jakarta.persistence.EntityManager#createNativeQuery(String,Class)
145198
* @see jakarta.persistence.SqlResultSetMapping
@@ -153,8 +206,8 @@ public interface QueryProducer {
153206
* Create a {@link NativeQuery} instance for the given native SQL query
154207
* using an explicit mapping to the specified Java type.
155208
* <p>
156-
* The given result set mapping name must identify a mapping defined by a
157-
* {@link jakarta.persistence.SqlResultSetMapping} annotation.
209+
* The given result set mapping name must identify a mapping defined by
210+
* a {@link jakarta.persistence.SqlResultSetMapping} annotation.
158211
*
159212
* @param sqlString The native (SQL) query string
160213
* @param resultSetMappingName The explicit result mapping name
@@ -167,21 +220,69 @@ public interface QueryProducer {
167220
<R> NativeQuery<R> createNativeQuery(String sqlString, String resultSetMappingName, Class<R> resultClass);
168221

169222
/**
170-
* Create a {@link SelectionQuery} reference for the given HQL.
171-
*
172-
* Only valid for select queries
173-
*
174-
* @see jakarta.persistence.EntityManager#createQuery(String)
223+
* Create a {@link SelectionQuery} reference for the given HQL
224+
* {@code select} statement.
225+
* <p>
226+
* If the statement has no explicit {@code select} list, the
227+
* select list is inferred:
228+
* <ul>
229+
* <li>if there is exactly one root entity in the {@code from}
230+
* clause, then that root entity is the only element of the
231+
* select list, or
232+
* <li>otherwise, if there are multiple root entities in the
233+
* {@code from} clause, then the select list contains every
234+
* root entity and every non-{@code fetch} joined entity.
235+
* </ul>
236+
*
237+
* @implNote This method interprets some queries with an implicit
238+
* {@code select} list in a quite unintuitive way. In some future
239+
* release, this method will be modified to throw an exception
240+
* when passed a query with a missing {@code select}. For now, use
241+
* {@link #createSelectionQuery(String, Class)} to avoid ambiguity.
175242
*
176243
* @throws IllegalSelectQueryException if the given HQL query
177244
* is an insert, update or delete query
245+
*
246+
* @deprecated Use {@link #createSelectionQuery(String, Class)}
178247
*/
248+
@Deprecated(since = "6.3")
179249
SelectionQuery<?> createSelectionQuery(String hqlString);
180250

181251
/**
182-
* Create a {@link SelectionQuery} reference for the given HQL.
183-
*
184-
* Only valid for select queries
252+
* Create a {@link SelectionQuery} instance for the given HQL query
253+
* string and given query result type.
254+
* <ul>
255+
* <li>If the query has a single item in the {@code select} list,
256+
* then the select item must be assignable to the given result
257+
* type.
258+
* <li>Otherwise, if there are multiple select items, then the
259+
* select items will be packaged into an instance of the
260+
* result type. The result type must have an appropriate
261+
* constructor with parameter types matching the select items,
262+
* or it must be one of the types {@code Object[]},
263+
* {@link java.util.List}, {@link java.util.Map}, or
264+
* {@link jakarta.persistence.Tuple}.
265+
* </ul>
266+
* <p>
267+
* If a query has no explicit {@code select} list, the select list
268+
* is inferred from the given query result type:
269+
* <ul>
270+
* <li>if the result type is an entity type, the query must have
271+
* exactly one root entity in the {@code from} clause, it must
272+
* be assignable to the result type, and the inferred select
273+
* list will contain just that entity, or
274+
* <li>otherwise, the select list contains every root entity and
275+
* every non-{@code fetch} joined entity, and each query result
276+
* will be packaged into an instance of the result type, just
277+
* as specified above.
278+
* </ul>
279+
* <p>
280+
* The returned {@code Query} may be executed by calling
281+
* {@link Query#getResultList()} or {@link Query#getSingleResult()}.
282+
283+
* @param hqlString The HQL query as a string
284+
* @param resultType The {@link Class} object representing the
285+
* query result type
185286
*
186287
* @see jakarta.persistence.EntityManager#createQuery(String)
187288
*
@@ -191,14 +292,15 @@ public interface QueryProducer {
191292
<R> SelectionQuery<R> createSelectionQuery(String hqlString, Class<R> resultType);
192293

193294
/**
194-
* Create a {@link SelectionQuery} reference for the given Criteria.
295+
* Create a {@link SelectionQuery} reference for the given
296+
* {@link CriteriaQuery}.
195297
*
196298
* @see jakarta.persistence.EntityManager#createQuery(CriteriaQuery)
197299
*/
198300
<R> SelectionQuery<R> createSelectionQuery(CriteriaQuery<R> criteria);
199301

200302
/**
201-
* Create a MutationQuery reference for the given HQL insert,
303+
* Create a {@link MutationQuery} reference for the given HQL insert,
202304
* update, or delete statement.
203305
*
204306
* @throws IllegalMutationQueryException if the given HQL query
@@ -234,7 +336,7 @@ public interface QueryProducer {
234336
* Create a typed {@link Query} instance for the given named query.
235337
* The named query might be defined in HQL or in native SQL.
236338
*
237-
* @param name the name of a pre-defined, named query
339+
* @param name the name of a predefined named query
238340
*
239341
* @return The {@link Query} instance for manipulation and execution
240342
*
@@ -244,8 +346,7 @@ public interface QueryProducer {
244346
*
245347
* @see jakarta.persistence.EntityManager#createNamedQuery(String)
246348
*
247-
* @deprecated use {@link #createNamedQuery(String, Class)} or
248-
* {@link #createNamedMutationQuery(String)}
349+
* @deprecated use {@link #createNamedQuery(String, Class)}
249350
*/
250351
@Deprecated(since = "6.0") @SuppressWarnings("rawtypes")
251352
Query createNamedQuery(String name);
@@ -269,20 +370,29 @@ public interface QueryProducer {
269370
<R> Query<R> createNamedQuery(String name, Class<R> resultClass);
270371

271372
/**
272-
* Create a {@link SelectionQuery} instance for the
273-
* named {@link jakarta.persistence.NamedQuery}
373+
* Create a {@link SelectionQuery} instance for the named
374+
* {@link jakarta.persistence.NamedQuery}.
274375
*
275-
* @throws IllegalSelectQueryException if the given HQL query is a select query
376+
* @implNote This method interprets some queries with an implicit
377+
* {@code select} list in a quite unintuitive way. In some future
378+
* release, this method will be modified to throw an exception
379+
* when passed a query with a missing {@code select}. For now, use
380+
* {@link #createNamedSelectionQuery(String, Class)} to avoid
381+
* ambiguity.
382+
*
383+
* @throws IllegalSelectQueryException if the given HQL query is not a select query
276384
* @throws UnknownNamedQueryException if no query has been defined with the given name
385+
*
386+
* @deprecated use {@link #createNamedSelectionQuery(String, Class)}
277387
*/
388+
@Deprecated(since = "6.3")
278389
SelectionQuery<?> createNamedSelectionQuery(String name);
279390

280391
/**
281-
* Create a {@link SelectionQuery} instance for the
282-
* named {@link jakarta.persistence.NamedQuery} with the expected
283-
* result-type
392+
* Create a {@link SelectionQuery} instance for the named
393+
* {@link jakarta.persistence.NamedQuery} with the given result type.
284394
*
285-
* @throws IllegalSelectQueryException if the given HQL query is a select query
395+
* @throws IllegalSelectQueryException if the given HQL query is not a select query
286396
* @throws UnknownNamedQueryException if no query has been defined with the given name
287397
*/
288398
<R> SelectionQuery<R> createNamedSelectionQuery(String name, Class<R> resultType);
@@ -301,7 +411,7 @@ public interface QueryProducer {
301411
/**
302412
* Create a {@link Query} instance for the named query.
303413
*
304-
* @param queryName the name of a pre-defined, named query
414+
* @param queryName the name of a predefined named query
305415
*
306416
* @return The {@link Query} instance for manipulation and execution
307417
*
@@ -317,7 +427,7 @@ public interface QueryProducer {
317427
/**
318428
* Get a {@link NativeQuery} instance for a named native SQL query
319429
*
320-
* @param name The name of the pre-defined query
430+
* @param name The name of the predefined query
321431
*
322432
* @return The {@link NativeQuery} instance for manipulation and execution
323433
*
@@ -329,7 +439,7 @@ public interface QueryProducer {
329439
/**
330440
* Get a {@link NativeQuery} instance for a named native SQL query
331441
*
332-
* @param name The name of the pre-defined query
442+
* @param name The name of the predefined query
333443
*
334444
* @return The {@link NativeQuery} instance for manipulation and execution
335445
*

0 commit comments

Comments
 (0)