Skip to content

Commit 80fb595

Browse files
committed
HHH-18979 add JpaCriteriaQuery.getRoot()
making it cleaner to manipulate criteria constructed from HQL also clean up some dodgy unchecked casts
1 parent 5e107b8 commit 80fb595

File tree

16 files changed

+202
-148
lines changed

16 files changed

+202
-148
lines changed

documentation/src/main/asciidoc/introduction/Interacting.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ It's even possible to transform a HQL query string to a criteria query, and modi
919919
----
920920
HibernateCriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
921921
var query = builder.createQuery("from Book where year(publicationDate) > 2000", Book.class);
922-
var root = (Root<Book>) query.getRootList().get(0);
922+
var root = query.getRoot(0, Book.class);
923923
query.where(builder.like(root.get(Book_.title), builder.literal("Hibernate%")));
924924
query.orderBy(builder.asc(root.get(Book_.title)), builder.desc(root.get(Book_.isbn)));
925925
List<Book> matchingBooks = session.createSelectionQuery(query).getResultList();

hibernate-core/src/main/java/org/hibernate/query/criteria/CriteriaDefinition.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
* List&lt;Book&gt; books
5454
* = new CriteriaDefinition&lt;&gt;(sessionFactory, Book.class,
5555
* "from Book left join fetch authors where type = BOOK") {{
56-
* var book = (JpaRoot&lt;Book&gt;) getSelection();
56+
* var book = getRoot(0, Book.class);
5757
* where(getRestriction(), like(book.get(Book_.title), "%Hibernate%"));
5858
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
5959
* }}
@@ -408,10 +408,20 @@ public FetchClauseType getFetchClauseType() {
408408
}
409409

410410
@Override
411-
public List<Root<?>> getRootList() {
411+
public List<? extends JpaRoot<?>> getRootList() {
412412
return query.getRootList();
413413
}
414414

415+
@Override
416+
public <E> JpaRoot<? extends E> getRoot(int position, Class<E> type) {
417+
return query.getRoot( position, type );
418+
}
419+
420+
@Override
421+
public <E> JpaRoot<? extends E> getRoot(String alias, Class<E> type) {
422+
return query.getRoot( alias, type );
423+
}
424+
415425
@Override
416426
public Collection<? extends JpaCteCriteria<?>> getCteCriterias() {
417427
return query.getCteCriterias();

hibernate-core/src/main/java/org/hibernate/query/criteria/JpaCriteriaQuery.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,33 @@ public interface JpaCriteriaQuery<T> extends CriteriaQuery<T>, JpaQueryableCrite
6161
/**
6262
* Return the {@linkplain #getRoots() roots} as a list.
6363
*/
64-
List<Root<?>> getRootList();
64+
List<? extends JpaRoot<?>> getRootList();
6565

66-
@Override
67-
@SuppressWarnings("unchecked")
68-
default List<Order> getOrderList() {
69-
return (List) getQueryPart().getSortSpecifications();
70-
}
66+
/**
67+
* Get a {@linkplain Root query root} element at the given position
68+
* with the given type.
69+
*
70+
* @param position the position of this root element
71+
* @param type the type of the root entity
72+
*
73+
* @throws IllegalArgumentException if the root entity at the given
74+
* position is not of the given type, or if there are not
75+
* enough root entities in the query
76+
*/
77+
<E> JpaRoot<? extends E> getRoot(int position, Class<E> type);
78+
79+
/**
80+
* Get a {@linkplain Root query root} element with the given alias
81+
* and the given type.
82+
*
83+
* @param alias the identification variable of the root element
84+
* @param type the type of the root entity
85+
*
86+
* @throws IllegalArgumentException if the root entity with the
87+
* given alias is not of the given type, or if there is
88+
* no root entities with the given alias
89+
*/
90+
<E> JpaRoot<? extends E> getRoot(String alias, Class<E> type);
7191

7292
/**
7393
* {@inheritDoc}

hibernate-core/src/main/java/org/hibernate/query/criteria/JpaQueryStructure.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ public interface JpaQueryStructure<T> extends JpaQueryPart<T> {
6666

6767
List<? extends JpaExpression<?>> getGroupingExpressions();
6868

69-
JpaQueryStructure<T> setGroupingExpressions(List<? extends JpaExpression<?>> grouping);
69+
JpaQueryStructure<T> setGroupingExpressions(List<? extends Expression<?>> grouping);
7070

71-
JpaQueryStructure<T> setGroupingExpressions(JpaExpression<?>... grouping);
71+
JpaQueryStructure<T> setGroupingExpressions(Expression<?>... grouping);
7272

7373
JpaPredicate getGroupRestriction();
7474

hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -908,19 +908,19 @@ <T> SqmJsonValueExpression<T> jsonValue(
908908
SqmSelectStatement<Tuple> createTupleQuery();
909909

910910
@Override
911-
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>[] selections);
911+
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>... selections);
912912

913913
@Override
914914
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, List<? extends JpaSelection<?>> arguments);
915915

916916
@Override
917-
JpaCompoundSelection<Tuple> tuple(Selection<?>[] selections);
917+
JpaCompoundSelection<Tuple> tuple(Selection<?>... selections);
918918

919919
@Override
920920
JpaCompoundSelection<Tuple> tuple(List<Selection<?>> selections);
921921

922922
@Override
923-
JpaCompoundSelection<Object[]> array(Selection<?>[] selections);
923+
JpaCompoundSelection<Object[]> array(Selection<?>... selections);
924924

925925
@Override
926926
JpaCompoundSelection<Object[]> array(List<Selection<?>> selections);

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55
package org.hibernate.query.sqm.internal;
66

7-
import jakarta.persistence.criteria.Root;
87
import org.hibernate.HibernateException;
98
import org.hibernate.engine.spi.SharedSessionContractImplementor;
109
import org.hibernate.graph.spi.AppliedGraph;
@@ -150,13 +149,7 @@ public SelectionQuery<R> setOrder(Order<? super R> order) {
150149
@Override
151150
public SelectionQuery<R> addRestriction(Restriction<? super R> restriction) {
152151
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext() );
153-
final Root<?> firstRoot = selectStatement.getRootList().get( 0 );
154-
if ( !getExpectedResultType().isAssignableFrom( firstRoot.getJavaType() ) ) {
155-
throw new IllegalStateException("First root entity of the query did not have the query result type");
156-
}
157-
@SuppressWarnings("unchecked") // safe, we just checked
158-
final Root<? extends R> root = (Root<? extends R>) firstRoot;
159-
restriction.apply( selectStatement, root );
152+
restriction.apply( selectStatement, selectStatement.<R>getRoot( 0, getExpectedResultType() ) );
160153
// TODO: when the QueryInterpretationCache can handle caching criteria queries,
161154
// simply cache the new SQM as if it were a criteria query, and remove this:
162155
getQueryOptions().setQueryPlanCachingEnabled( false );

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/KeyBasedPagination.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private static <R> JpaCompoundSelection<KeyedResult<R>> keyedResultConstructor(
111111
return builder.construct( resultClass, asList( selected, builder.construct(List.class, newItems ) ) );
112112
}
113113

114-
@SuppressWarnings({"rawtypes", "unchecked"})
114+
@SuppressWarnings("rawtypes")
115115
private static <C extends Comparable<? super C>> SqmPredicate keyPredicate(
116116
Expression<? extends C> key, C keyValue, SortDirection direction,
117117
List<SqmPath<?>> previousKeys, List<Comparable<?>> keyValues,

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import org.hibernate.internal.CoreMessageLogger;
4141
import org.hibernate.internal.SessionFactoryRegistry;
4242
import org.hibernate.internal.util.StringHelper;
43-
import org.hibernate.internal.util.collections.CollectionHelper;
4443
import org.hibernate.jpa.spi.JpaCompliance;
4544
import org.hibernate.metamodel.model.domain.DomainType;
4645
import org.hibernate.metamodel.model.domain.JpaMetamodel;
@@ -167,6 +166,7 @@
167166
import org.checkerframework.checker.nullness.qual.Nullable;
168167

169168
import static java.util.Arrays.asList;
169+
import static org.hibernate.internal.util.collections.CollectionHelper.determineProperSizing;
170170
import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType;
171171
import static org.hibernate.query.sqm.TrimSpec.fromCriteriaTrimSpec;
172172

@@ -872,7 +872,7 @@ public JpaSearchOrder desc(JpaCteCriteriaAttribute x, boolean nullsFirst) {
872872
}
873873

874874
@Override
875-
public JpaCompoundSelection<Tuple> tuple(Selection<?>[] selections) {
875+
public JpaCompoundSelection<Tuple> tuple(Selection<?>... selections) {
876876
return tuple( Arrays.asList( selections ) );
877877
}
878878

@@ -916,62 +916,69 @@ public <R> SqmTuple<R> tuple(SqmExpressible<R> tupleType, List<? extends SqmExpr
916916
}
917917

918918
@Override
919-
public JpaCompoundSelection<Object[]> array(Selection<?>[] selections) {
920-
return array( Arrays.asList( selections ) );
919+
public JpaCompoundSelection<Object[]> array(Selection<?>... selections) {
920+
return array( Object[].class,
921+
Arrays.stream( selections ).map( selection -> (SqmSelectableNode<?>) selection ).toList() );
921922
}
922923

923924
@Override
924925
public JpaCompoundSelection<Object[]> array(List<Selection<?>> selections) {
925-
//noinspection unchecked,rawtypes
926-
return array( Object[].class, (List) selections );
926+
return arrayInternal( Object[].class,
927+
selections.stream().map( selection -> (SqmSelectableNode<?>) selection ).toList() );
927928
}
928929

929930
@Override
930-
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, Selection<?>[] selections) {
931-
//noinspection unchecked
932-
return array( resultClass, (List<SqmSelectableNode<?>>) (List<?>) Arrays.asList( selections ) );
931+
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, Selection<?>... selections) {
932+
return arrayInternal( resultClass,
933+
Arrays.stream( selections ).map( selection -> (SqmSelectableNode<?>) selection ).toList() );
933934
}
934935

935936
@Override
936937
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, List<? extends JpaSelection<?>> selections) {
937-
//noinspection rawtypes,unchecked
938-
checkMultiselect( (List) selections );
938+
return arrayInternal( resultClass,
939+
selections.stream().map( selection -> (SqmSelectableNode<?>) selection ).toList() );
940+
}
941+
942+
public <Y> JpaCompoundSelection<Y> arrayInternal(Class<Y> resultClass, List<? extends SqmSelectableNode<?>> selections) {
943+
checkMultiselect( selections );
939944
final JavaType<Y> javaType = getTypeConfiguration().getJavaTypeRegistry().getDescriptor( resultClass );
940-
//noinspection unchecked
941-
return new SqmJpaCompoundSelection<>( (List<SqmSelectableNode<?>>) selections, javaType, this );
945+
return new SqmJpaCompoundSelection<>( selections, javaType, this );
942946
}
943947

944948
@Override
945-
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>[] arguments) {
946-
//noinspection unchecked
947-
return construct( resultClass, (List<JpaSelection<?>>) (List<?>) Arrays.asList( arguments ) );
949+
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>... arguments) {
950+
return constructInternal( resultClass,
951+
Arrays.stream( arguments ).map( arg -> (SqmSelectableNode<?>) arg ).toList() );
948952
}
949953

950954
@Override
951955
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, List<? extends JpaSelection<?>> arguments) {
952-
//noinspection unchecked,rawtypes
953-
checkMultiselect( (List) arguments );
954-
final SqmDynamicInstantiation<Y> instantiation;
956+
return constructInternal( resultClass,
957+
arguments.stream().map( arg -> (SqmSelectableNode<?>) arg ).toList() );
958+
}
959+
960+
private <Y> JpaCompoundSelection<Y> constructInternal(Class<Y> resultClass, List<? extends SqmSelectableNode<?>> arguments) {
961+
checkMultiselect( arguments );
962+
final SqmDynamicInstantiation<Y> instantiation = createInstantiation( resultClass );
963+
for ( SqmSelectableNode<?> argument : arguments ) {
964+
final SqmDynamicInstantiationArgument<?> arg =
965+
new SqmDynamicInstantiationArgument<>( argument, argument.getAlias(), this );
966+
instantiation.addArgument( arg );
967+
}
968+
return instantiation;
969+
}
970+
971+
@SuppressWarnings("unchecked")
972+
private <Y> SqmDynamicInstantiation<Y> createInstantiation(Class<Y> resultClass) {
955973
if ( List.class.equals( resultClass ) ) {
956-
//noinspection unchecked
957-
instantiation = (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forListInstantiation( this );
974+
return (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forListInstantiation( this );
958975
}
959976
else if ( Map.class.equals( resultClass ) ) {
960-
//noinspection unchecked
961-
instantiation = (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forMapInstantiation( this );
977+
return (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forMapInstantiation( this );
962978
}
963979
else {
964-
instantiation = SqmDynamicInstantiation.forClassInstantiation( resultClass, this );
980+
return SqmDynamicInstantiation.forClassInstantiation( resultClass, this );
965981
}
966-
967-
for ( Selection<?> argument : arguments ) {
968-
final SqmSelectableNode<?> arg = (SqmSelectableNode<?>) argument;
969-
instantiation.addArgument(
970-
new SqmDynamicInstantiationArgument<>( arg, argument.getAlias(), this )
971-
);
972-
}
973-
974-
return instantiation;
975982
}
976983

977984
/**
@@ -985,8 +992,8 @@ else if ( Map.class.equals( resultClass ) ) {
985992
* <i>&quot;An argument to the multiselect method must not be a tuple-
986993
* or array-valued compound selection item.&quot;</i>
987994
*/
988-
void checkMultiselect(List<Selection<?>> selections) {
989-
final HashSet<String> aliases = new HashSet<>( CollectionHelper.determineProperSizing( selections.size() ) );
995+
void checkMultiselect(List<? extends Selection<?>> selections) {
996+
final HashSet<String> aliases = new HashSet<>( determineProperSizing( selections.size() ) );
990997

991998
for ( Selection<?> it : selections ) {
992999
final JpaSelection<?> selection = (JpaSelection<?>) it;

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2437,7 +2437,7 @@ private int indexOfExpression(int offset, List<? extends SqmAliasedNode<?>> sele
24372437
offset = -subResult - i;
24382438
}
24392439
else if ( selectableNode instanceof SqmJpaCompoundSelection<?> compoundSelection ) {
2440-
final List<SqmSelectableNode<?>> selectionItems = compoundSelection.getSelectionItems();
2440+
final List<? extends SqmSelectableNode<?>> selectionItems = compoundSelection.getSelectionItems();
24412441
for ( int j = 0; j < selectionItems.size(); j++ ) {
24422442
if ( selectionItems.get( j ) == node ) {
24432443
return offset + i + j;

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFromClause.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66

77
import java.io.Serializable;
88
import java.util.ArrayList;
9-
import java.util.Collections;
109
import java.util.List;
1110
import java.util.function.Consumer;
1211

1312
import org.hibernate.internal.util.collections.CollectionHelper;
1413
import org.hibernate.query.sqm.tree.SqmCopyContext;
1514
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
1615

16+
import static java.util.Collections.emptyList;
17+
import static java.util.Collections.unmodifiableList;
18+
1719
/**
1820
* Contract representing a from clause.
1921
* <p>
@@ -50,7 +52,7 @@ public SqmFromClause copy(SqmCopyContext context) {
5052
* mutate the roots
5153
*/
5254
public List<SqmRoot<?>> getRoots() {
53-
return domainRoots == null ? Collections.emptyList() : Collections.unmodifiableList( domainRoots );
55+
return domainRoots == null ? emptyList() : unmodifiableList( domainRoots );
5456
}
5557

5658
/**

0 commit comments

Comments
 (0)