Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1091,8 +1091,7 @@ The interface link:{doc-javadoc-url}org/hibernate/query/Path.html[`Path`] may be
----
List<Book> booksForPublisher =
session.createSelectionQuery("from Book", Book.class)
.addRestriction(Path.root(Book_.publisher)
.get(Publisher_.name)
.addRestriction(Path.from(Book.class).to(Book_.publisher).to(Publisher_.name)
.equalTo(publisherName))
.getResultList();
----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import org.hibernate.query.range.Range;

/**
* Restricts an attribute of an entity to a given {@link Range}.
* Restricts an attribute of an entity to a given {@link Range},
* using a stringly-typed attribute reference.
*
* @param <X> The entity type
* @param <U> The attribute type
Expand All @@ -36,9 +37,10 @@ public Predicate toPredicate(Root<? extends X> root, CriteriaBuilder builder) {
if ( !(attribute instanceof SingularAttribute) ) {
throw new IllegalArgumentException( "Attribute '" + attributeName + "' is not singular" );
}
if ( range.getType()!=null && !range.getType().isAssignableFrom( attribute.getJavaType() ) ) {
final Class<? extends U> rangeType = range.getType();
if ( rangeType != null && !rangeType.isAssignableFrom( attribute.getJavaType() ) ) {
throw new IllegalArgumentException( "Attribute '" + attributeName
+ "' is not assignable to range of type '" + range.getType().getName() + "'" );
+ "' is not assignable to range of type '" + rangeType.getName() + "'" );
}
return range.toPredicate( root.get( attributeName ), builder );
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.query;

import jakarta.persistence.criteria.Root;

/**
* A non-root element of a {@link Path}, using a stringly-typed
* attribute reference.
*
* @author Gavin King
*/
record NamedPathElement<X, U, V>(Path<? super X, U> parent, String attributeName, Class<V> attributeType)
implements Path<X, V> {
@Override
public Class<V> getType() {
return attributeType;
}

@Override
public jakarta.persistence.criteria.Path<V> path(Root<? extends X> root) {
final jakarta.persistence.criteria.Path<V> path = parent.path( root ).get( attributeName );
if ( !attributeType.isAssignableFrom( path.getJavaType() ) ) {
throw new IllegalArgumentException( "Attribute '" + attributeName
+ "' is not of type '" + attributeType.getName() + "'" );
}
return path;
}
}
21 changes: 15 additions & 6 deletions hibernate-core/src/main/java/org/hibernate/query/Path.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
* the root entity type of the query.
* <pre>
* session.createSelectionQuery("from Book", Book.class)
* .addRestriction(root(publisher).get(name).equalTo("Manning"))
* .addRestriction(from(Book.class).to(Book_.publisher).to(Publisher_.name)
* .equalTo("Manning"))
* .getResultList()
* </pre>
* A compound path-based restriction has the same semantics as the
Expand All @@ -37,15 +38,21 @@
public interface Path<X,U> {
jakarta.persistence.criteria.Path<U> path(Root<? extends X> root);

default <V> Path<X, V> get(SingularAttribute<? super U, V> attribute) {
Class<U> getType();

default <V> Path<X, V> to(SingularAttribute<? super U, V> attribute) {
return new PathElement<>( this, attribute );
}

static <X, U> Path<X, U> root(SingularAttribute<? super X, U> attribute) {
return new PathRoot<X>().get( attribute );
default <V> Path<X, V> to(String attributeName, Class<V> attributeType) {
return new NamedPathElement<>( this, attributeName, attributeType );
}

static <X> Path<X, X> from(Class<X> type) {
return new PathRoot<>( type );
}

default Restriction<X> restrict(Range<U> range) {
default Restriction<X> restrict(Range<? super U> range) {
return new PathRange<>( this, range );
}

Expand All @@ -65,5 +72,7 @@ default Restriction<X> notIn(List<U> values) {
return in( values ).negated();
}

//TODO: between, lessThan, greaterThan?
default Restriction<X> notNull() {
return restrict( Range.notNull( getType() ) );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
*
* @author Gavin King
*/
record PathElement<X, U, V>(Path<? super X, U> parent, SingularAttribute<? super U, V> attribute) implements Path<X, V> {
record PathElement<X, U, V>(Path<? super X, U> parent, SingularAttribute<? super U, V> attribute)
implements Path<X, V> {
@Override
public Class<V> getType() {
return attribute.getJavaType();
}

@Override
public jakarta.persistence.criteria.Path<V> path(Root<? extends X> root) {
return parent.path( root ).get( attribute );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
* @author Gavin King
*/
record PathRange<X, U>(Path<X, U> path, Range<U> range) implements Restriction<X> {
record PathRange<X, U>(Path<X, U> path, Range<? super U> range) implements Restriction<X> {
@Override
public Restriction<X> negated() {
return new Negation<>( this );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
*
* @author Gavin King
*/
record PathRoot<X>() implements Path<X, X> {
record PathRoot<X>(Class<X> type) implements Path<X, X> {
@Override
public Class<X> getType() {
return type;
}

@Override
@SuppressWarnings("unchecked")
public jakarta.persistence.criteria.Path<X> path(Root<? extends X> root) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ static <T> Restriction<T> notContains(SingularAttribute<T, String> attribute, St
return contains( attribute, substring, caseSensitive ).negated();
}

static <T, U> Restriction<T> notNull(SingularAttribute<T, U> attribute) {
return restrict( attribute, Range.notNull( attribute.getJavaType() ) );
}

static <T> Restriction<T> all(List<? extends Restriction<? super T>> restrictions) {
return new Conjunction<>( restrictions );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,7 @@ public SqmQueryImplementor<E> toQuery(SharedSessionContractImplementor session)

@Override
public <T> SqmSelectionQuery<T> toSelectionQuery(Class<T> resultType, SharedSessionContractImplementor session) {
return new SqmSelectionQueryImpl<>(
this,
resultType,
session
);
return new SqmSelectionQueryImpl<>( this, resultType, session );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,7 @@ public SqmQueryImplementor<R> toQuery(SharedSessionContractImplementor session)

@Override
public <T> SqmSelectionQuery<T> toSelectionQuery(Class<T> resultType, SharedSessionContractImplementor session) {
return new SqmSelectionQueryImpl<>(
this,
resultType,
session
);
return new SqmSelectionQueryImpl<>( this, resultType, session );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ record CaseInsensitiveValue(String value) implements Range<String> {
}

@Override
public Predicate toPredicate(Path<String> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends String> path, CriteriaBuilder builder) {
// TODO: it would be much better to not do use literal,
// and let it be treated as a parameter, but we
// we run into the usual bug with parameters in
// manipulated SQM trees
@SuppressWarnings("unchecked")
final Path<String> stringPath = (Path<String>) path; // safe, because String is final
final Expression<String> literal = builder.literal( value.toLowerCase( Locale.ROOT ) );
return builder.lower( path ).equalTo( literal );
return builder.lower( stringPath ).equalTo( literal );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
*/
record EmptyRange<U>(Class<U> type) implements Range<U> {
@Override
public Class<? extends U> getType() {
public Class<U> getType() {
return type;
}

@Override
public Predicate toPredicate(Path<U> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
return builder.disjunction();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
*/
record FullRange<U>(Class<U> type) implements Range<U> {
@Override
public Class<? extends U> getType() {
public Class<U> getType() {
return type;
}

@Override
public Predicate toPredicate(Path<U> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
return builder.conjunction();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
record Interval<U extends Comparable<U>>(LowerBound<U> lowerBound, UpperBound<U> upperBound)
implements Range<U> {
@Override
public Predicate toPredicate(Path<U> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
return lowerBound.open() || upperBound.open()
? builder.and( lowerBound.toPredicate( path, builder ), upperBound.toPredicate( path, builder ) )
: builder.between( path, builder.literal( lowerBound.bound() ), builder.literal( upperBound.bound() ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ record LowerBound<U extends Comparable<U>>(U bound, boolean open) implements Ran
}

@Override
public Predicate toPredicate(Path<U> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
// TODO: it would be much better to not do use literal,
// and let it be treated as a parameter, but we
// we run into the usual bug with parameters in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.query.range;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;

/**
* A {@link Range} containing every value of the given type,
* except the null value.
*
* @author Gavin King
*/
record NotNull<U>(Class<U> type) implements Range<U> {
@Override
public Class<U> getType() {
return type;
}

@Override
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
return path.isNotNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ record Pattern(String pattern, boolean caseSensitive) implements Range<String> {
}

@Override
public Predicate toPredicate(Path<String> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends String> path, CriteriaBuilder builder) {
@SuppressWarnings("unchecked")
final Path<String> stringPath = (Path<String>) path; // safe, because String is final
return caseSensitive
? builder.like( path, builder.literal( pattern ), '\\' )
: builder.like( builder.lower( path ),
? builder.like( stringPath, builder.literal( pattern ), '\\' )
: builder.like( builder.lower( stringPath ),
builder.literal( pattern.toLowerCase( Locale.ROOT ) ),
'\\' );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public interface Range<U> {
* values.
*/
@Internal
Predicate toPredicate(Path<U> path, CriteriaBuilder builder);
Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder);

static <U> Range<U> singleValue(U value) {
return new Value<>( value );
Expand Down Expand Up @@ -123,6 +123,10 @@ static <U> Range<U> full(Class<U> type) {
return new FullRange<>( type );
}

static <U> Range<U> notNull(Class<U> type) {
return new NotNull<>( type );
}

private static String escape(String literal) {
final var result = new StringBuilder();
for ( int i = 0; i < literal.length(); i++ ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ record UpperBound<U extends Comparable<U>>(U bound, boolean open) implements Ran
}

@Override
public Predicate toPredicate(Path<U> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
// TODO: it would be much better to not do use literal,
// and let it be treated as a parameter, but we
// we run into the usual bug with parameters in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ record Value<U>(U value) implements Range<U> {
}

@Override
public Predicate toPredicate(Path<U> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
// TODO: it would be much better to not do use literal,
// and let it be treated as a parameter, but we
// we run into the usual bug with parameters in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ record ValueList<U>(List<U> values) implements Range<U> {
}

@Override
public Predicate toPredicate(Path<U> path, CriteriaBuilder builder) {
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
return path.in( values.stream().map( builder::literal ).toList() );
}

Expand Down
Loading
Loading