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:
parent
5e107b8981
commit
80fb5950ae
|
@ -919,7 +919,7 @@ It's even possible to transform a HQL query string to a criteria query, and modi
|
||||||
----
|
----
|
||||||
HibernateCriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
|
HibernateCriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
|
||||||
var query = builder.createQuery("from Book where year(publicationDate) > 2000", Book.class);
|
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.where(builder.like(root.get(Book_.title), builder.literal("Hibernate%")));
|
||||||
query.orderBy(builder.asc(root.get(Book_.title)), builder.desc(root.get(Book_.isbn)));
|
query.orderBy(builder.asc(root.get(Book_.title)), builder.desc(root.get(Book_.isbn)));
|
||||||
List<Book> matchingBooks = session.createSelectionQuery(query).getResultList();
|
List<Book> matchingBooks = session.createSelectionQuery(query).getResultList();
|
||||||
|
|
|
@ -53,7 +53,7 @@ import java.util.function.Function;
|
||||||
* List<Book> books
|
* List<Book> books
|
||||||
* = new CriteriaDefinition<>(sessionFactory, Book.class,
|
* = new CriteriaDefinition<>(sessionFactory, Book.class,
|
||||||
* "from Book left join fetch authors where type = BOOK") {{
|
* "from Book left join fetch authors where type = BOOK") {{
|
||||||
* var book = (JpaRoot<Book>) getSelection();
|
* var book = getRoot(0, Book.class);
|
||||||
* where(getRestriction(), like(book.get(Book_.title), "%Hibernate%"));
|
* where(getRestriction(), like(book.get(Book_.title), "%Hibernate%"));
|
||||||
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
|
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
|
||||||
* }}
|
* }}
|
||||||
|
@ -408,10 +408,20 @@ public abstract class CriteriaDefinition<R>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Root<?>> getRootList() {
|
public List<? extends JpaRoot<?>> getRootList() {
|
||||||
return query.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
|
@Override
|
||||||
public Collection<? extends JpaCteCriteria<?>> getCteCriterias() {
|
public Collection<? extends JpaCteCriteria<?>> getCteCriterias() {
|
||||||
return query.getCteCriterias();
|
return query.getCteCriterias();
|
||||||
|
|
|
@ -61,13 +61,33 @@ public interface JpaCriteriaQuery<T> extends CriteriaQuery<T>, JpaQueryableCrite
|
||||||
/**
|
/**
|
||||||
* Return the {@linkplain #getRoots() roots} as a list.
|
* Return the {@linkplain #getRoots() roots} as a list.
|
||||||
*/
|
*/
|
||||||
List<Root<?>> getRootList();
|
List<? extends JpaRoot<?>> getRootList();
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
@SuppressWarnings("unchecked")
|
* Get a {@linkplain Root query root} element at the given position
|
||||||
default List<Order> getOrderList() {
|
* with the given type.
|
||||||
return (List) getQueryPart().getSortSpecifications();
|
*
|
||||||
}
|
* @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}
|
* {@inheritDoc}
|
||||||
|
|
|
@ -66,9 +66,9 @@ public interface JpaQueryStructure<T> extends JpaQueryPart<T> {
|
||||||
|
|
||||||
List<? extends JpaExpression<?>> getGroupingExpressions();
|
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();
|
JpaPredicate getGroupRestriction();
|
||||||
|
|
||||||
|
|
|
@ -908,19 +908,19 @@ public interface NodeBuilder extends HibernateCriteriaBuilder, BindingContext {
|
||||||
SqmSelectStatement<Tuple> createTupleQuery();
|
SqmSelectStatement<Tuple> createTupleQuery();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>[] selections);
|
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>... selections);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, List<? extends JpaSelection<?>> arguments);
|
<Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, List<? extends JpaSelection<?>> arguments);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaCompoundSelection<Tuple> tuple(Selection<?>[] selections);
|
JpaCompoundSelection<Tuple> tuple(Selection<?>... selections);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaCompoundSelection<Tuple> tuple(List<Selection<?>> selections);
|
JpaCompoundSelection<Tuple> tuple(List<Selection<?>> selections);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaCompoundSelection<Object[]> array(Selection<?>[] selections);
|
JpaCompoundSelection<Object[]> array(Selection<?>... selections);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaCompoundSelection<Object[]> array(List<Selection<?>> selections);
|
JpaCompoundSelection<Object[]> array(List<Selection<?>> selections);
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.internal;
|
package org.hibernate.query.sqm.internal;
|
||||||
|
|
||||||
import jakarta.persistence.criteria.Root;
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.graph.spi.AppliedGraph;
|
import org.hibernate.graph.spi.AppliedGraph;
|
||||||
|
@ -150,13 +149,7 @@ abstract class AbstractSqmSelectionQuery<R> extends AbstractSelectionQuery<R> {
|
||||||
@Override
|
@Override
|
||||||
public SelectionQuery<R> addRestriction(Restriction<? super R> restriction) {
|
public SelectionQuery<R> addRestriction(Restriction<? super R> restriction) {
|
||||||
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext() );
|
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext() );
|
||||||
final Root<?> firstRoot = selectStatement.getRootList().get( 0 );
|
restriction.apply( selectStatement, selectStatement.<R>getRoot( 0, getExpectedResultType() ) );
|
||||||
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 );
|
|
||||||
// TODO: when the QueryInterpretationCache can handle caching criteria queries,
|
// TODO: when the QueryInterpretationCache can handle caching criteria queries,
|
||||||
// simply cache the new SQM as if it were a criteria query, and remove this:
|
// simply cache the new SQM as if it were a criteria query, and remove this:
|
||||||
getQueryOptions().setQueryPlanCachingEnabled( false );
|
getQueryOptions().setQueryPlanCachingEnabled( false );
|
||||||
|
|
|
@ -111,7 +111,7 @@ public class KeyBasedPagination {
|
||||||
return builder.construct( resultClass, asList( selected, builder.construct(List.class, newItems ) ) );
|
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(
|
private static <C extends Comparable<? super C>> SqmPredicate keyPredicate(
|
||||||
Expression<? extends C> key, C keyValue, SortDirection direction,
|
Expression<? extends C> key, C keyValue, SortDirection direction,
|
||||||
List<SqmPath<?>> previousKeys, List<Comparable<?>> keyValues,
|
List<SqmPath<?>> previousKeys, List<Comparable<?>> keyValues,
|
||||||
|
|
|
@ -40,7 +40,6 @@ import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.SessionFactoryRegistry;
|
import org.hibernate.internal.SessionFactoryRegistry;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
|
||||||
import org.hibernate.jpa.spi.JpaCompliance;
|
import org.hibernate.jpa.spi.JpaCompliance;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
import org.hibernate.metamodel.model.domain.JpaMetamodel;
|
import org.hibernate.metamodel.model.domain.JpaMetamodel;
|
||||||
|
@ -167,6 +166,7 @@ import jakarta.persistence.metamodel.Bindable;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
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.internal.QueryHelper.highestPrecedenceType;
|
||||||
import static org.hibernate.query.sqm.TrimSpec.fromCriteriaTrimSpec;
|
import static org.hibernate.query.sqm.TrimSpec.fromCriteriaTrimSpec;
|
||||||
|
|
||||||
|
@ -872,7 +872,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JpaCompoundSelection<Tuple> tuple(Selection<?>[] selections) {
|
public JpaCompoundSelection<Tuple> tuple(Selection<?>... selections) {
|
||||||
return tuple( Arrays.asList( selections ) );
|
return tuple( Arrays.asList( selections ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -916,62 +916,69 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JpaCompoundSelection<Object[]> array(Selection<?>[] selections) {
|
public JpaCompoundSelection<Object[]> array(Selection<?>... selections) {
|
||||||
return array( Arrays.asList( selections ) );
|
return array( Object[].class,
|
||||||
|
Arrays.stream( selections ).map( selection -> (SqmSelectableNode<?>) selection ).toList() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JpaCompoundSelection<Object[]> array(List<Selection<?>> selections) {
|
public JpaCompoundSelection<Object[]> array(List<Selection<?>> selections) {
|
||||||
//noinspection unchecked,rawtypes
|
return arrayInternal( Object[].class,
|
||||||
return array( Object[].class, (List) selections );
|
selections.stream().map( selection -> (SqmSelectableNode<?>) selection ).toList() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, Selection<?>[] selections) {
|
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, Selection<?>... selections) {
|
||||||
//noinspection unchecked
|
return arrayInternal( resultClass,
|
||||||
return array( resultClass, (List<SqmSelectableNode<?>>) (List<?>) Arrays.asList( selections ) );
|
Arrays.stream( selections ).map( selection -> (SqmSelectableNode<?>) selection ).toList() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, List<? extends JpaSelection<?>> selections) {
|
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, List<? extends JpaSelection<?>> selections) {
|
||||||
//noinspection rawtypes,unchecked
|
return arrayInternal( resultClass,
|
||||||
checkMultiselect( (List) selections );
|
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 );
|
final JavaType<Y> javaType = getTypeConfiguration().getJavaTypeRegistry().getDescriptor( resultClass );
|
||||||
//noinspection unchecked
|
return new SqmJpaCompoundSelection<>( selections, javaType, this );
|
||||||
return new SqmJpaCompoundSelection<>( (List<SqmSelectableNode<?>>) selections, javaType, this );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>[] arguments) {
|
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, Selection<?>... arguments) {
|
||||||
//noinspection unchecked
|
return constructInternal( resultClass,
|
||||||
return construct( resultClass, (List<JpaSelection<?>>) (List<?>) Arrays.asList( arguments ) );
|
Arrays.stream( arguments ).map( arg -> (SqmSelectableNode<?>) arg ).toList() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, List<? extends JpaSelection<?>> arguments) {
|
public <Y> JpaCompoundSelection<Y> construct(Class<Y> resultClass, List<? extends JpaSelection<?>> arguments) {
|
||||||
//noinspection unchecked,rawtypes
|
return constructInternal( resultClass,
|
||||||
checkMultiselect( (List) arguments );
|
arguments.stream().map( arg -> (SqmSelectableNode<?>) arg ).toList() );
|
||||||
final SqmDynamicInstantiation<Y> instantiation;
|
}
|
||||||
|
|
||||||
|
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 ) ) {
|
if ( List.class.equals( resultClass ) ) {
|
||||||
//noinspection unchecked
|
return (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forListInstantiation( this );
|
||||||
instantiation = (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forListInstantiation( this );
|
|
||||||
}
|
}
|
||||||
else if ( Map.class.equals( resultClass ) ) {
|
else if ( Map.class.equals( resultClass ) ) {
|
||||||
//noinspection unchecked
|
return (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forMapInstantiation( this );
|
||||||
instantiation = (SqmDynamicInstantiation<Y>) SqmDynamicInstantiation.forMapInstantiation( this );
|
|
||||||
}
|
}
|
||||||
else {
|
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>"An argument to the multiselect method must not be a tuple-
|
* <i>"An argument to the multiselect method must not be a tuple-
|
||||||
* or array-valued compound selection item."</i>
|
* or array-valued compound selection item."</i>
|
||||||
*/
|
*/
|
||||||
void checkMultiselect(List<Selection<?>> selections) {
|
void checkMultiselect(List<? extends Selection<?>> selections) {
|
||||||
final HashSet<String> aliases = new HashSet<>( CollectionHelper.determineProperSizing( selections.size() ) );
|
final HashSet<String> aliases = new HashSet<>( determineProperSizing( selections.size() ) );
|
||||||
|
|
||||||
for ( Selection<?> it : selections ) {
|
for ( Selection<?> it : selections ) {
|
||||||
final JpaSelection<?> selection = (JpaSelection<?>) it;
|
final JpaSelection<?> selection = (JpaSelection<?>) it;
|
||||||
|
|
|
@ -2437,7 +2437,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
offset = -subResult - i;
|
offset = -subResult - i;
|
||||||
}
|
}
|
||||||
else if ( selectableNode instanceof SqmJpaCompoundSelection<?> compoundSelection ) {
|
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++ ) {
|
for ( int j = 0; j < selectionItems.size(); j++ ) {
|
||||||
if ( selectionItems.get( j ) == node ) {
|
if ( selectionItems.get( j ) == node ) {
|
||||||
return offset + i + j;
|
return offset + i + j;
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.hibernate.query.sqm.tree.from;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
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.SqmCopyContext;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
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.
|
* Contract representing a from clause.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -50,7 +52,7 @@ public class SqmFromClause implements Serializable {
|
||||||
* mutate the roots
|
* mutate the roots
|
||||||
*/
|
*/
|
||||||
public List<SqmRoot<?>> getRoots() {
|
public List<SqmRoot<?>> getRoots() {
|
||||||
return domainRoots == null ? Collections.emptyList() : Collections.unmodifiableList( domainRoots );
|
return domainRoots == null ? emptyList() : unmodifiableList( domainRoots );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.tree.select;
|
package org.hibernate.query.sqm.tree.select;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -12,6 +11,7 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.query.criteria.JpaCteCriteria;
|
import org.hibernate.query.criteria.JpaCteCriteria;
|
||||||
import org.hibernate.query.criteria.JpaFunctionRoot;
|
import org.hibernate.query.criteria.JpaFunctionRoot;
|
||||||
|
@ -40,11 +40,12 @@ import jakarta.persistence.criteria.Subquery;
|
||||||
import jakarta.persistence.metamodel.EntityType;
|
import jakarta.persistence.metamodel.EntityType;
|
||||||
|
|
||||||
import static java.lang.Character.isAlphabetic;
|
import static java.lang.Character.isAlphabetic;
|
||||||
|
import static java.util.Collections.unmodifiableList;
|
||||||
|
import static java.util.Collections.unmodifiableSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public abstract class AbstractSqmSelectQuery<T>
|
public abstract class AbstractSqmSelectQuery<T>
|
||||||
extends AbstractSqmNode
|
extends AbstractSqmNode
|
||||||
implements SqmSelectQuery<T> {
|
implements SqmSelectQuery<T> {
|
||||||
|
@ -110,7 +111,7 @@ public abstract class AbstractSqmSelectQuery<T>
|
||||||
return cteStatements.values();
|
return cteStatements.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override @SuppressWarnings("unchecked")
|
||||||
public <X> JpaCteCriteria<X> getCteCriteria(String cteName) {
|
public <X> JpaCteCriteria<X> getCteCriteria(String cteName) {
|
||||||
return (JpaCteCriteria<X>) cteStatements.get( cteName );
|
return (JpaCteCriteria<X>) cteStatements.get( cteName );
|
||||||
}
|
}
|
||||||
|
@ -222,14 +223,54 @@ public abstract class AbstractSqmSelectQuery<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public Set<Root<?>> getRoots() {
|
public Set<Root<?>> getRoots() {
|
||||||
return (Set) getQuerySpec().getRoots();
|
return unmodifiableSet( getQuerySpec().getRoots() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
/**
|
||||||
public List<Root<?>> getRootList() {
|
* @see org.hibernate.query.criteria.JpaCriteriaQuery#getRootList()
|
||||||
return (List) getQuerySpec().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
|
@Override
|
||||||
|
@ -329,20 +370,19 @@ public abstract class AbstractSqmSelectQuery<T>
|
||||||
// Grouping
|
// Grouping
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public List<Expression<?>> getGroupList() {
|
public List<Expression<?>> getGroupList() {
|
||||||
return (List) getQuerySpec().getGroupingExpressions();
|
return unmodifiableList( getQuerySpec().getGroupingExpressions() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmSelectQuery<T> groupBy(Expression<?>... expressions) {
|
public SqmSelectQuery<T> groupBy(Expression<?>... expressions) {
|
||||||
return groupBy( Arrays.asList( expressions ) );
|
getQuerySpec().setGroupingExpressions( List.of( expressions ) );
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public SqmSelectQuery<T> groupBy(List<Expression<?>> grouping) {
|
public SqmSelectQuery<T> groupBy(List<Expression<?>> grouping) {
|
||||||
getQuerySpec().setGroupingExpressions( (List) grouping );
|
getQuerySpec().setGroupingExpressions( grouping );
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,35 +403,6 @@ public abstract class AbstractSqmSelectQuery<T>
|
||||||
return this;
|
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) {
|
public void appendHqlString(StringBuilder sb) {
|
||||||
if ( !cteStatements.isEmpty() ) {
|
if ( !cteStatements.isEmpty() ) {
|
||||||
sb.append( "with " );
|
sb.append( "with " );
|
||||||
|
@ -404,35 +415,28 @@ public abstract class AbstractSqmSelectQuery<T>
|
||||||
sqmQueryPart.appendHqlString( sb );
|
sqmQueryPart.appendHqlString( sb );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
protected Selection<? extends T> getResultSelection(Selection<?>[] selections) {
|
protected Selection<? extends T> getResultSelection(Selection<?>[] selections) {
|
||||||
final Selection<? extends T> resultSelection;
|
final Class<T> resultType = getResultType();
|
||||||
Class<T> resultType = getResultType();
|
|
||||||
if ( resultType == null || resultType == Object.class ) {
|
if ( resultType == null || resultType == Object.class ) {
|
||||||
switch ( selections.length ) {
|
switch ( selections.length ) {
|
||||||
case 0: {
|
case 0:
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException( "Empty selections passed to criteria query typed as Object" );
|
||||||
"empty selections passed to criteria query typed as Object"
|
case 1:
|
||||||
);
|
return (Selection<? extends T>) selections[0];
|
||||||
}
|
default:
|
||||||
case 1: {
|
return (Selection<? extends T>) nodeBuilder().array( selections );
|
||||||
resultSelection = ( Selection<? extends T> ) selections[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( Tuple.class.isAssignableFrom( resultType ) ) {
|
else if ( Tuple.class.isAssignableFrom( resultType ) ) {
|
||||||
resultSelection = ( Selection<? extends T> ) nodeBuilder().tuple( selections );
|
return (Selection<? extends T>) nodeBuilder().tuple( selections );
|
||||||
}
|
}
|
||||||
else if ( resultType.isArray() ) {
|
else if ( resultType.isArray() ) {
|
||||||
resultSelection = nodeBuilder().array( resultType, selections );
|
return nodeBuilder().array( resultType, selections );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resultSelection = nodeBuilder().construct( resultType, selections );
|
return nodeBuilder().construct( resultType, selections );
|
||||||
}
|
}
|
||||||
return resultSelection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
// 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`
|
// 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;
|
private final JavaType<T> javaType;
|
||||||
|
|
||||||
public SqmJpaCompoundSelection(
|
public SqmJpaCompoundSelection(
|
||||||
List<SqmSelectableNode<?>> selectableNodes,
|
List<? extends SqmSelectableNode<?>> selectableNodes,
|
||||||
JavaType<T> javaType,
|
JavaType<T> javaType,
|
||||||
NodeBuilder criteriaBuilder) {
|
NodeBuilder criteriaBuilder) {
|
||||||
super( null, criteriaBuilder );
|
super( null, criteriaBuilder );
|
||||||
|
@ -111,7 +111,7 @@ public class SqmJpaCompoundSelection<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SqmSelectableNode<?>> getSelectionItems() {
|
public List<? extends SqmSelectableNode<?>> getSelectionItems() {
|
||||||
return selectableNodes;
|
return selectableNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -395,10 +395,10 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmQuerySpec<T> setGroupingExpressions(List<? extends JpaExpression<?>> groupExpressions) {
|
public SqmQuerySpec<T> setGroupingExpressions(List<? extends Expression<?>> groupExpressions) {
|
||||||
this.hasPositionalGroupItem = false;
|
this.hasPositionalGroupItem = false;
|
||||||
this.groupByClauseExpressions = new ArrayList<>( groupExpressions.size() );
|
this.groupByClauseExpressions = new ArrayList<>( groupExpressions.size() );
|
||||||
for ( JpaExpression<?> groupExpression : groupExpressions ) {
|
for ( Expression<?> groupExpression : groupExpressions ) {
|
||||||
if ( groupExpression instanceof SqmAliasedNodeRef ) {
|
if ( groupExpression instanceof SqmAliasedNodeRef ) {
|
||||||
this.hasPositionalGroupItem = true;
|
this.hasPositionalGroupItem = true;
|
||||||
}
|
}
|
||||||
|
@ -408,10 +408,10 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmQuerySpec<T> setGroupingExpressions(JpaExpression<?>... groupExpressions) {
|
public SqmQuerySpec<T> setGroupingExpressions(Expression<?>... groupExpressions) {
|
||||||
this.hasPositionalGroupItem = false;
|
this.hasPositionalGroupItem = false;
|
||||||
this.groupByClauseExpressions = new ArrayList<>( groupExpressions.length );
|
this.groupByClauseExpressions = new ArrayList<>( groupExpressions.length );
|
||||||
for ( JpaExpression<?> groupExpression : groupExpressions ) {
|
for ( Expression<?> groupExpression : groupExpressions ) {
|
||||||
if ( groupExpression instanceof SqmAliasedNodeRef ) {
|
if ( groupExpression instanceof SqmAliasedNodeRef ) {
|
||||||
this.hasPositionalGroupItem = true;
|
this.hasPositionalGroupItem = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import jakarta.persistence.criteria.Selection;
|
||||||
import jakarta.persistence.metamodel.EntityType;
|
import jakarta.persistence.metamodel.EntityType;
|
||||||
|
|
||||||
import static java.util.Collections.emptySet;
|
import static java.util.Collections.emptySet;
|
||||||
|
import static java.util.Collections.unmodifiableList;
|
||||||
import static java.util.Collections.unmodifiableSet;
|
import static java.util.Collections.unmodifiableSet;
|
||||||
import static org.hibernate.query.sqm.spi.SqmCreationHelper.combinePredicates;
|
import static org.hibernate.query.sqm.spi.SqmCreationHelper.combinePredicates;
|
||||||
import static org.hibernate.query.sqm.SqmQuerySource.CRITERIA;
|
import static org.hibernate.query.sqm.SqmQuerySource.CRITERIA;
|
||||||
|
@ -166,6 +167,11 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T>
|
||||||
return nodeBuilder();
|
return nodeBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Order> getOrderList() {
|
||||||
|
return unmodifiableList( getQueryPart().getSortSpecifications() );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmQuerySource getQuerySource() {
|
public SqmQuerySource getQuerySource() {
|
||||||
return querySource;
|
return querySource;
|
||||||
|
@ -368,33 +374,26 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Selection<? extends T> getResultSelection(List<?> selectionList) {
|
private Selection<? extends T> getResultSelection(List<?> selections) {
|
||||||
final Class<T> resultType = getResultType();
|
final Class<T> resultType = getResultType();
|
||||||
//noinspection rawtypes
|
|
||||||
final List<? extends JpaSelection<?>> selections = (List) selectionList;
|
|
||||||
if ( resultType == null || resultType == Object.class ) {
|
if ( resultType == null || resultType == Object.class ) {
|
||||||
switch ( selectionList.size() ) {
|
switch ( selections.size() ) {
|
||||||
case 0: {
|
case 0:
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException( "Empty selections passed to criteria query typed as Object" );
|
||||||
"empty selections passed to criteria query typed as Object"
|
case 1:
|
||||||
);
|
return (Selection<? extends T>) selections.get( 0 );
|
||||||
}
|
default:
|
||||||
case 1: {
|
return (Selection<? extends T>) nodeBuilder().array( (List<Selection<?>>) selections );
|
||||||
return (Selection<? extends T>) selectionList.get( 0 );
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return (Selection<? extends T>) nodeBuilder().array( (List) selectionList );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( Tuple.class.isAssignableFrom( resultType ) ) {
|
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() ) {
|
else if ( resultType.isArray() ) {
|
||||||
return nodeBuilder().array( resultType, selections );
|
return nodeBuilder().array( resultType, (List<? extends JpaSelection<?>>) selections );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nodeBuilder().construct( resultType, selections );
|
return nodeBuilder().construct( resultType, (List<? extends JpaSelection<?>>) selections );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,12 @@ public class CriteriaDefinitionTest {
|
||||||
orderBy(asc(message.get("text")));
|
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 query4 = new CriteriaDefinition<>(factory, Message.class) {{
|
||||||
var message = from(Message.class);
|
var message = from(Message.class);
|
||||||
restrict(like(message.get("text"), "hell%"));
|
restrict(like(message.get("text"), "hell%"));
|
||||||
|
@ -77,6 +83,9 @@ public class CriteriaDefinitionTest {
|
||||||
var messages = session.createSelectionQuery(query3).getResultList();
|
var messages = session.createSelectionQuery(query3).getResultList();
|
||||||
assertEquals(2,messages.size());
|
assertEquals(2,messages.size());
|
||||||
|
|
||||||
|
var messagesPrime = session.createSelectionQuery(query3prime).getResultList();
|
||||||
|
assertEquals(2,messagesPrime.size());
|
||||||
|
|
||||||
var msg = session.createSelectionQuery(query4).getSingleResult();
|
var msg = session.createSelectionQuery(query4).getSingleResult();
|
||||||
assertNotNull(msg);
|
assertNotNull(msg);
|
||||||
assertEquals(1L,msg.id);
|
assertEquals(1L,msg.id);
|
||||||
|
|
|
@ -8,7 +8,6 @@ import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.Version;
|
import jakarta.persistence.Version;
|
||||||
import jakarta.persistence.criteria.Root;
|
|
||||||
import jakarta.persistence.metamodel.SingularAttribute;
|
import jakarta.persistence.metamodel.SingularAttribute;
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
@ -251,11 +250,22 @@ public class RestrictionTest {
|
||||||
} );
|
} );
|
||||||
scope.inSession( session -> {
|
scope.inSession( session -> {
|
||||||
var query = session.getCriteriaBuilder().createQuery("select title from Book", String.class);
|
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 );
|
equal( isbn, "9781932394153" ).apply( query, root );
|
||||||
List<String> titles = session.createQuery( query ).getResultList();
|
List<String> titles = session.createQuery( query ).getResultList();
|
||||||
assertEquals( 1, titles.size() );
|
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() );
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue