HHH-18979 add JpaCriteriaQuery.getRoot()

making it cleaner to manipulate criteria constructed from HQL

also clean up some dodgy unchecked casts
This commit is contained in:
Gavin King 2024-12-29 12:28:37 +01:00
parent 5e107b8981
commit 80fb5950ae
16 changed files with 202 additions and 148 deletions

View File

@ -919,7 +919,7 @@ It's even possible to transform a HQL query string to a criteria query, and modi
----
HibernateCriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
var query = builder.createQuery("from Book where year(publicationDate) > 2000", Book.class);
var root = (Root<Book>) query.getRootList().get(0);
var root = query.getRoot(0, Book.class);
query.where(builder.like(root.get(Book_.title), builder.literal("Hibernate%")));
query.orderBy(builder.asc(root.get(Book_.title)), builder.desc(root.get(Book_.isbn)));
List<Book> matchingBooks = session.createSelectionQuery(query).getResultList();

View File

@ -53,7 +53,7 @@ import java.util.function.Function;
* List&lt;Book&gt; books
* = new CriteriaDefinition&lt;&gt;(sessionFactory, Book.class,
* "from Book left join fetch authors where type = BOOK") {{
* var book = (JpaRoot&lt;Book&gt;) getSelection();
* var book = getRoot(0, Book.class);
* where(getRestriction(), like(book.get(Book_.title), "%Hibernate%"));
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
* }}
@ -408,10 +408,20 @@ public abstract class CriteriaDefinition<R>
}
@Override
public List<Root<?>> getRootList() {
public List<? extends JpaRoot<?>> getRootList() {
return query.getRootList();
}
@Override
public <E> JpaRoot<? extends E> getRoot(int position, Class<E> type) {
return query.getRoot( position, type );
}
@Override
public <E> JpaRoot<? extends E> getRoot(String alias, Class<E> type) {
return query.getRoot( alias, type );
}
@Override
public Collection<? extends JpaCteCriteria<?>> getCteCriterias() {
return query.getCteCriterias();

View File

@ -61,13 +61,33 @@ public interface JpaCriteriaQuery<T> extends CriteriaQuery<T>, JpaQueryableCrite
/**
* Return the {@linkplain #getRoots() roots} as a list.
*/
List<Root<?>> getRootList();
List<? extends JpaRoot<?>> getRootList();
@Override
@SuppressWarnings("unchecked")
default List<Order> getOrderList() {
return (List) getQueryPart().getSortSpecifications();
}
/**
* Get a {@linkplain Root query root} element at the given position
* with the given type.
*
* @param position the position of this root element
* @param type the type of the root entity
*
* @throws IllegalArgumentException if the root entity at the given
* position is not of the given type, or if there are not
* enough root entities in the query
*/
<E> JpaRoot<? extends E> getRoot(int position, Class<E> type);
/**
* Get a {@linkplain Root query root} element with the given alias
* and the given type.
*
* @param alias the identification variable of the root element
* @param type the type of the root entity
*
* @throws IllegalArgumentException if the root entity with the
* given alias is not of the given type, or if there is
* no root entities with the given alias
*/
<E> JpaRoot<? extends E> getRoot(String alias, Class<E> type);
/**
* {@inheritDoc}

View File

@ -66,9 +66,9 @@ public interface JpaQueryStructure<T> extends JpaQueryPart<T> {
List<? extends JpaExpression<?>> getGroupingExpressions();
JpaQueryStructure<T> setGroupingExpressions(List<? extends JpaExpression<?>> grouping);
JpaQueryStructure<T> setGroupingExpressions(List<? extends Expression<?>> grouping);
JpaQueryStructure<T> setGroupingExpressions(JpaExpression<?>... grouping);
JpaQueryStructure<T> setGroupingExpressions(Expression<?>... grouping);
JpaPredicate getGroupRestriction();

View File

@ -908,19 +908,19 @@ public interface NodeBuilder extends HibernateCriteriaBuilder, BindingContext {
SqmSelectStatement<Tuple> createTupleQuery();
@Override
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>[] selections);
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>... selections);
@Override
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, List<? extends JpaSelection<?>> arguments);
@Override
JpaCompoundSelection<Tuple> tuple(Selection<?>[] selections);
JpaCompoundSelection<Tuple> tuple(Selection<?>... selections);
@Override
JpaCompoundSelection<Tuple> tuple(List<Selection<?>> selections);
@Override
JpaCompoundSelection<Object[]> array(Selection<?>[] selections);
JpaCompoundSelection<Object[]> array(Selection<?>... selections);
@Override
JpaCompoundSelection<Object[]> array(List<Selection<?>> selections);

View File

@ -4,7 +4,6 @@
*/
package org.hibernate.query.sqm.internal;
import jakarta.persistence.criteria.Root;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.graph.spi.AppliedGraph;
@ -150,13 +149,7 @@ abstract class AbstractSqmSelectionQuery<R> extends AbstractSelectionQuery<R> {
@Override
public SelectionQuery<R> addRestriction(Restriction<? super R> restriction) {
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext() );
final Root<?> firstRoot = selectStatement.getRootList().get( 0 );
if ( !getExpectedResultType().isAssignableFrom( firstRoot.getJavaType() ) ) {
throw new IllegalStateException("First root entity of the query did not have the query result type");
}
@SuppressWarnings("unchecked") // safe, we just checked
final Root<? extends R> root = (Root<? extends R>) firstRoot;
restriction.apply( selectStatement, root );
restriction.apply( selectStatement, selectStatement.<R>getRoot( 0, getExpectedResultType() ) );
// TODO: when the QueryInterpretationCache can handle caching criteria queries,
// simply cache the new SQM as if it were a criteria query, and remove this:
getQueryOptions().setQueryPlanCachingEnabled( false );

View File

@ -111,7 +111,7 @@ public class KeyBasedPagination {
return builder.construct( resultClass, asList( selected, builder.construct(List.class, newItems ) ) );
}
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings("rawtypes")
private static <C extends Comparable<? super C>> SqmPredicate keyPredicate(
Expression<? extends C> key, C keyValue, SortDirection direction,
List<SqmPath<?>> previousKeys, List<Comparable<?>> keyValues,

View File

@ -40,7 +40,6 @@ import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.SessionFactoryRegistry;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
@ -167,6 +166,7 @@ import jakarta.persistence.metamodel.Bindable;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Arrays.asList;
import static org.hibernate.internal.util.collections.CollectionHelper.determineProperSizing;
import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType;
import static org.hibernate.query.sqm.TrimSpec.fromCriteriaTrimSpec;
@ -872,7 +872,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, Serializable {
}
@Override
public JpaCompoundSelection<Tuple> tuple(Selection<?>[] selections) {
public JpaCompoundSelection<Tuple> tuple(Selection<?>... selections) {
return tuple( Arrays.asList( selections ) );
}
@ -916,62 +916,69 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, Serializable {
}
@Override
public JpaCompoundSelection<Object[]> array(Selection<?>[] selections) {
return array( Arrays.asList( selections ) );
public JpaCompoundSelection<Object[]> array(Selection<?>... selections) {
return array( Object[].class,
Arrays.stream( selections ).map( selection -> (SqmSelectableNode<?>) selection ).toList() );
}
@Override
public JpaCompoundSelection<Object[]> array(List<Selection<?>> selections) {
//noinspection unchecked,rawtypes
return array( Object[].class, (List) selections );
return arrayInternal( Object[].class,
selections.stream().map( selection -> (SqmSelectableNode<?>) selection ).toList() );
}
@Override
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, Selection<?>[] selections) {
//noinspection unchecked
return array( resultClass, (List<SqmSelectableNode<?>>) (List<?>) Arrays.asList( selections ) );
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, Selection<?>... selections) {
return arrayInternal( resultClass,
Arrays.stream( selections ).map( selection -> (SqmSelectableNode<?>) selection ).toList() );
}
@Override
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, List<? extends JpaSelection<?>> selections) {
//noinspection rawtypes,unchecked
checkMultiselect( (List) selections );
return arrayInternal( resultClass,
selections.stream().map( selection -> (SqmSelectableNode<?>) selection ).toList() );
}
public <Y> JpaCompoundSelection<Y> arrayInternal(Class<Y> resultClass, List<? extends SqmSelectableNode<?>> selections) {
checkMultiselect( selections );
final JavaType<Y> javaType = getTypeConfiguration().getJavaTypeRegistry().getDescriptor( resultClass );
//noinspection unchecked
return new SqmJpaCompoundSelection<>( (List<SqmSelectableNode<?>>) selections, javaType, this );
return new SqmJpaCompoundSelection<>( selections, javaType, this );
}
@Override
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>[] arguments) {
//noinspection unchecked
return construct( resultClass, (List<JpaSelection<?>>) (List<?>) Arrays.asList( arguments ) );
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>... arguments) {
return constructInternal( resultClass,
Arrays.stream( arguments ).map( arg -> (SqmSelectableNode<?>) arg ).toList() );
}
@Override
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, List<? extends JpaSelection<?>> arguments) {
//noinspection unchecked,rawtypes
checkMultiselect( (List) arguments );
final SqmDynamicInstantiation<Y> instantiation;
return constructInternal( resultClass,
arguments.stream().map( arg -> (SqmSelectableNode<?>) arg ).toList() );
}
private <Y> JpaCompoundSelection<Y> constructInternal(Class<Y> resultClass, List<? extends SqmSelectableNode<?>> arguments) {
checkMultiselect( arguments );
final SqmDynamicInstantiation<Y> instantiation = createInstantiation( resultClass );
for ( SqmSelectableNode<?> argument : arguments ) {
final SqmDynamicInstantiationArgument<?> arg =
new SqmDynamicInstantiationArgument<>( argument, argument.getAlias(), this );
instantiation.addArgument( arg );
}
return instantiation;
}
@SuppressWarnings("unchecked")
private <Y> SqmDynamicInstantiation<Y> createInstantiation(Class<Y> resultClass) {
if ( List.class.equals( resultClass ) ) {
//noinspection unchecked
instantiation = (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forListInstantiation( this );
return (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forListInstantiation( this );
}
else if ( Map.class.equals( resultClass ) ) {
//noinspection unchecked
instantiation = (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forMapInstantiation( this );
return (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forMapInstantiation( this );
}
else {
instantiation = SqmDynamicInstantiation.forClassInstantiation( resultClass, this );
return SqmDynamicInstantiation.forClassInstantiation( resultClass, this );
}
for ( Selection<?> argument : arguments ) {
final SqmSelectableNode<?> arg = (SqmSelectableNode<?>) argument;
instantiation.addArgument(
new SqmDynamicInstantiationArgument<>( arg, argument.getAlias(), this )
);
}
return instantiation;
}
/**
@ -985,8 +992,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, Serializable {
* <i>&quot;An argument to the multiselect method must not be a tuple-
* or array-valued compound selection item.&quot;</i>
*/
void checkMultiselect(List<Selection<?>> selections) {
final HashSet<String> aliases = new HashSet<>( CollectionHelper.determineProperSizing( selections.size() ) );
void checkMultiselect(List<? extends Selection<?>> selections) {
final HashSet<String> aliases = new HashSet<>( determineProperSizing( selections.size() ) );
for ( Selection<?> it : selections ) {
final JpaSelection<?> selection = (JpaSelection<?>) it;

View File

@ -2437,7 +2437,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
offset = -subResult - i;
}
else if ( selectableNode instanceof SqmJpaCompoundSelection<?> compoundSelection ) {
final List<SqmSelectableNode<?>> selectionItems = compoundSelection.getSelectionItems();
final List<? extends SqmSelectableNode<?>> selectionItems = compoundSelection.getSelectionItems();
for ( int j = 0; j < selectionItems.size(); j++ ) {
if ( selectionItems.get( j ) == node ) {
return offset + i + j;

View File

@ -6,7 +6,6 @@ package org.hibernate.query.sqm.tree.from;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@ -14,6 +13,9 @@ import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
/**
* Contract representing a from clause.
* <p>
@ -50,7 +52,7 @@ public class SqmFromClause implements Serializable {
* mutate the roots
*/
public List<SqmRoot<?>> getRoots() {
return domainRoots == null ? Collections.emptyList() : Collections.unmodifiableList( domainRoots );
return domainRoots == null ? emptyList() : unmodifiableList( domainRoots );
}
/**

View File

@ -4,7 +4,6 @@
*/
package org.hibernate.query.sqm.tree.select;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
@ -12,6 +11,7 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.hibernate.AssertionFailure;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.criteria.JpaCteCriteria;
import org.hibernate.query.criteria.JpaFunctionRoot;
@ -40,11 +40,12 @@ import jakarta.persistence.criteria.Subquery;
import jakarta.persistence.metamodel.EntityType;
import static java.lang.Character.isAlphabetic;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableSet;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("unchecked")
public abstract class AbstractSqmSelectQuery<T>
extends AbstractSqmNode
implements SqmSelectQuery<T> {
@ -110,7 +111,7 @@ public abstract class AbstractSqmSelectQuery<T>
return cteStatements.values();
}
@Override
@Override @SuppressWarnings("unchecked")
public <X> JpaCteCriteria<X> getCteCriteria(String cteName) {
return (JpaCteCriteria<X>) cteStatements.get( cteName );
}
@ -222,14 +223,54 @@ public abstract class AbstractSqmSelectQuery<T>
}
@Override
@SuppressWarnings("rawtypes")
public Set<Root<?>> getRoots() {
return (Set) getQuerySpec().getRoots();
return unmodifiableSet( getQuerySpec().getRoots() );
}
@SuppressWarnings("rawtypes")
public List<Root<?>> getRootList() {
return (List) getQuerySpec().getRootList();
/**
* @see org.hibernate.query.criteria.JpaCriteriaQuery#getRootList()
*/
public List<? extends JpaRoot<?>> getRootList() {
return getQuerySpec().getRootList();
}
/**
* @see org.hibernate.query.criteria.JpaCriteriaQuery#getRoot(int, Class)
*/
public <E> JpaRoot<? extends E> getRoot(int position, Class<E> type) {
final List<SqmRoot<?>> rootList = getQuerySpec().getRootList();
if ( rootList.size() <= position ) {
throw new IllegalArgumentException( "Not enough root entities" );
}
return castRoot( rootList.get( position ), type );
}
/**
* @see org.hibernate.query.criteria.JpaCriteriaQuery#getRoot(String, Class)
*/
public <E> JpaRoot<? extends E> getRoot(String alias, Class<E> type) {
final List<SqmRoot<?>> rootList = getQuerySpec().getRootList();
for ( SqmRoot<?> root : rootList ) {
final String rootAlias = root.getAlias();
if ( rootAlias != null && rootAlias.equals( alias ) ) {
return castRoot( root, type );
}
}
throw new IllegalArgumentException( "No root entity with alias " + alias );
}
private static <E> JpaRoot<? extends E> castRoot(JpaRoot<?> root, Class<E> type) {
final Class<?> rootEntityType = root.getJavaType();
if ( rootEntityType == null ) {
throw new AssertionFailure( "Java type of root entity was null" );
}
if ( !type.isAssignableFrom( rootEntityType ) ) {
throw new IllegalArgumentException( "Root entity of type '" + rootEntityType.getTypeName()
+ "' did not have the given type '" + type.getTypeName() + "'");
}
@SuppressWarnings("unchecked") // safe, we just checked
final JpaRoot<? extends E> result = (JpaRoot<? extends E>) root;
return result;
}
@Override
@ -329,20 +370,19 @@ public abstract class AbstractSqmSelectQuery<T>
// Grouping
@Override
@SuppressWarnings("rawtypes")
public List<Expression<?>> getGroupList() {
return (List) getQuerySpec().getGroupingExpressions();
return unmodifiableList( getQuerySpec().getGroupingExpressions() );
}
@Override
public SqmSelectQuery<T> groupBy(Expression<?>... expressions) {
return groupBy( Arrays.asList( expressions ) );
getQuerySpec().setGroupingExpressions( List.of( expressions ) );
return this;
}
@Override
@SuppressWarnings("rawtypes")
public SqmSelectQuery<T> groupBy(List<Expression<?>> grouping) {
getQuerySpec().setGroupingExpressions( (List) grouping );
getQuerySpec().setGroupingExpressions( grouping );
return this;
}
@ -363,35 +403,6 @@ public abstract class AbstractSqmSelectQuery<T>
return this;
}
//
// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// // Limit
//
//
// @Override
// @SuppressWarnings("unchecked")
// public <X> ExpressionImplementor<X> getLimit() {
// return limit;
// }
//
// @Override
// public C setLimit(JpaExpression<?> limit) {
// this.limit = (ExpressionImplementor) limit;
// return this;
// }
//
// @Override
// @SuppressWarnings("unchecked")
// public <X> ExpressionImplementor<X> getOffset() {
// return offset;
// }
//
// @Override
// public C setOffset(JpaExpression offset) {
// this.offset = (ExpressionImplementor) offset;
// return this;
// }
public void appendHqlString(StringBuilder sb) {
if ( !cteStatements.isEmpty() ) {
sb.append( "with " );
@ -404,35 +415,28 @@ public abstract class AbstractSqmSelectQuery<T>
sqmQueryPart.appendHqlString( sb );
}
@SuppressWarnings("unchecked")
protected Selection<? extends T> getResultSelection(Selection<?>[] selections) {
final Selection<? extends T> resultSelection;
Class<T> resultType = getResultType();
final Class<T> resultType = getResultType();
if ( resultType == null || resultType == Object.class ) {
switch ( selections.length ) {
case 0: {
throw new IllegalArgumentException(
"empty selections passed to criteria query typed as Object"
);
}
case 1: {
resultSelection = ( Selection<? extends T> ) selections[0];
break;
}
default: {
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
}
case 0:
throw new IllegalArgumentException( "Empty selections passed to criteria query typed as Object" );
case 1:
return (Selection<? extends T>) selections[0];
default:
return (Selection<? extends T>) nodeBuilder().array( selections );
}
}
else if ( Tuple.class.isAssignableFrom( resultType ) ) {
resultSelection = ( Selection<? extends T> ) nodeBuilder().tuple( selections );
return (Selection<? extends T>) nodeBuilder().tuple( selections );
}
else if ( resultType.isArray() ) {
resultSelection = nodeBuilder().array( resultType, selections );
return nodeBuilder().array( resultType, selections );
}
else {
resultSelection = nodeBuilder().construct( resultType, selections );
return nodeBuilder().construct( resultType, selections );
}
return resultSelection;
}
}

View File

@ -56,11 +56,11 @@ public class SqmJpaCompoundSelection<T>
// can support using tuples in other clauses. If we keep the Easy way is to add a switch in creation of these
// whether `SqmJpaCompoundSelection` or `SqmTuple` is used based on `JpaCompliance#isJpaQueryComplianceEnabled`
private final List<SqmSelectableNode<?>> selectableNodes;
private final List<? extends SqmSelectableNode<?>> selectableNodes;
private final JavaType<T> javaType;
public SqmJpaCompoundSelection(
List<SqmSelectableNode<?>> selectableNodes,
List<? extends SqmSelectableNode<?>> selectableNodes,
JavaType<T> javaType,
NodeBuilder criteriaBuilder) {
super( null, criteriaBuilder );
@ -111,7 +111,7 @@ public class SqmJpaCompoundSelection<T>
}
@Override
public List<SqmSelectableNode<?>> getSelectionItems() {
public List<? extends SqmSelectableNode<?>> getSelectionItems() {
return selectableNodes;
}

View File

@ -395,10 +395,10 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
}
@Override
public SqmQuerySpec<T> setGroupingExpressions(List<? extends JpaExpression<?>> groupExpressions) {
public SqmQuerySpec<T> setGroupingExpressions(List<? extends Expression<?>> groupExpressions) {
this.hasPositionalGroupItem = false;
this.groupByClauseExpressions = new ArrayList<>( groupExpressions.size() );
for ( JpaExpression<?> groupExpression : groupExpressions ) {
for ( Expression<?> groupExpression : groupExpressions ) {
if ( groupExpression instanceof SqmAliasedNodeRef ) {
this.hasPositionalGroupItem = true;
}
@ -408,10 +408,10 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
}
@Override
public SqmQuerySpec<T> setGroupingExpressions(JpaExpression<?>... groupExpressions) {
public SqmQuerySpec<T> setGroupingExpressions(Expression<?>... groupExpressions) {
this.hasPositionalGroupItem = false;
this.groupByClauseExpressions = new ArrayList<>( groupExpressions.length );
for ( JpaExpression<?> groupExpression : groupExpressions ) {
for ( Expression<?> groupExpression : groupExpressions ) {
if ( groupExpression instanceof SqmAliasedNodeRef ) {
this.hasPositionalGroupItem = true;
}

View File

@ -41,6 +41,7 @@ import jakarta.persistence.criteria.Selection;
import jakarta.persistence.metamodel.EntityType;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableSet;
import static org.hibernate.query.sqm.spi.SqmCreationHelper.combinePredicates;
import static org.hibernate.query.sqm.SqmQuerySource.CRITERIA;
@ -166,6 +167,11 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T>
return nodeBuilder();
}
@Override
public List<Order> getOrderList() {
return unmodifiableList( getQueryPart().getSortSpecifications() );
}
@Override
public SqmQuerySource getQuerySource() {
return querySource;
@ -368,33 +374,26 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T>
}
@SuppressWarnings("unchecked")
private Selection<? extends T> getResultSelection(List<?> selectionList) {
private Selection<? extends T> getResultSelection(List<?> selections) {
final Class<T> resultType = getResultType();
//noinspection rawtypes
final List<? extends JpaSelection<?>> selections = (List) selectionList;
if ( resultType == null || resultType == Object.class ) {
switch ( selectionList.size() ) {
case 0: {
throw new IllegalArgumentException(
"empty selections passed to criteria query typed as Object"
);
}
case 1: {
return (Selection<? extends T>) selectionList.get( 0 );
}
default: {
return (Selection<? extends T>) nodeBuilder().array( (List) selectionList );
}
switch ( selections.size() ) {
case 0:
throw new IllegalArgumentException( "Empty selections passed to criteria query typed as Object" );
case 1:
return (Selection<? extends T>) selections.get( 0 );
default:
return (Selection<? extends T>) nodeBuilder().array( (List<Selection<?>>) selections );
}
}
else if ( Tuple.class.isAssignableFrom( resultType ) ) {
return (Selection<? extends T>) nodeBuilder().tuple( (List) selectionList );
return (Selection<? extends T>) nodeBuilder().tuple( (List<Selection<?>>) selections );
}
else if ( resultType.isArray() ) {
return nodeBuilder().array( resultType, selections );
return nodeBuilder().array( resultType, (List<? extends JpaSelection<?>>) selections );
}
else {
return nodeBuilder().construct( resultType, selections );
return nodeBuilder().construct( resultType, (List<? extends JpaSelection<?>>) selections );
}
}

View File

@ -51,6 +51,12 @@ public class CriteriaDefinitionTest {
orderBy(asc(message.get("text")));
}};
var query3prime = new CriteriaDefinition<>(factory, Message.class, "from Msg") {{
var message = getRoot( 0, Message.class );
where(ilike(message.get("text"), "%e%"));
orderBy(asc(message.get("text")));
}};
var query4 = new CriteriaDefinition<>(factory, Message.class) {{
var message = from(Message.class);
restrict(like(message.get("text"), "hell%"));
@ -77,6 +83,9 @@ public class CriteriaDefinitionTest {
var messages = session.createSelectionQuery(query3).getResultList();
assertEquals(2,messages.size());
var messagesPrime = session.createSelectionQuery(query3prime).getResultList();
assertEquals(2,messagesPrime.size());
var msg = session.createSelectionQuery(query4).getSingleResult();
assertNotNull(msg);
assertEquals(1L,msg.id);

View File

@ -8,7 +8,6 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Version;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.SingularAttribute;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
@ -251,11 +250,22 @@ public class RestrictionTest {
} );
scope.inSession( session -> {
var query = session.getCriteriaBuilder().createQuery("select title from Book", String.class);
var root = (Root<Book>) query.getRootList().get(0);
var root = query.getRoot(0, Book.class);
equal( isbn, "9781932394153" ).apply( query, root );
List<String> titles = session.createQuery( query ).getResultList();
assertEquals( 1, titles.size() );
} );
var builder = scope.getSessionFactory().getCriteriaBuilder();
var query = builder.createQuery("from Book where pages > 200", Book.class);
var root = query.getRoot(0, Book.class);
like( title, "Hibernate%" ).apply( query, root );
query.orderBy(builder.asc(root.get(title)), builder.desc(root.get(isbn)));
scope.inSession( session -> {
List<Book> matchingBooks = session.createSelectionQuery(query).getResultList();
assertEquals( 1, matchingBooks.size() );
});
}