Skip to content

Commit ae4c730

Browse files
committed
HHH-18990 refinements to Path, add NotNull range
1 parent b3e614d commit ae4c730

File tree

20 files changed

+145
-36
lines changed

20 files changed

+145
-36
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,8 +1091,7 @@ The interface link:{doc-javadoc-url}org/hibernate/query/Path.html[`Path`] may be
10911091
----
10921092
List<Book> booksForPublisher =
10931093
session.createSelectionQuery("from Book", Book.class)
1094-
.addRestriction(Path.root(Book_.publisher)
1095-
.get(Publisher_.name)
1094+
.addRestriction(Path.from(Book.class).to(Book_.publisher).to(Publisher_.name)
10961095
.equalTo(publisherName))
10971096
.getResultList();
10981097
----

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import org.hibernate.query.range.Range;
1414

1515
/**
16-
* Restricts an attribute of an entity to a given {@link Range}.
16+
* Restricts an attribute of an entity to a given {@link Range},
17+
* using a stringly-typed attribute reference.
1718
*
1819
* @param <X> The entity type
1920
* @param <U> The attribute type
@@ -36,9 +37,10 @@ public Predicate toPredicate(Root<? extends X> root, CriteriaBuilder builder) {
3637
if ( !(attribute instanceof SingularAttribute) ) {
3738
throw new IllegalArgumentException( "Attribute '" + attributeName + "' is not singular" );
3839
}
39-
if ( range.getType()!=null && !range.getType().isAssignableFrom( attribute.getJavaType() ) ) {
40+
final Class<? extends U> rangeType = range.getType();
41+
if ( rangeType != null && !rangeType.isAssignableFrom( attribute.getJavaType() ) ) {
4042
throw new IllegalArgumentException( "Attribute '" + attributeName
41-
+ "' is not assignable to range of type '" + range.getType().getName() + "'" );
43+
+ "' is not assignable to range of type '" + rangeType.getName() + "'" );
4244
}
4345
return range.toPredicate( root.get( attributeName ), builder );
4446
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query;
6+
7+
import jakarta.persistence.criteria.Root;
8+
9+
/**
10+
* A non-root element of a {@link Path}, using a stringly-typed
11+
* attribute reference.
12+
*
13+
* @author Gavin King
14+
*/
15+
record NamedPathElement<X, U, V>(Path<? super X, U> parent, String attributeName, Class<V> attributeType)
16+
implements Path<X, V> {
17+
@Override
18+
public Class<V> getType() {
19+
return attributeType;
20+
}
21+
22+
@Override
23+
public jakarta.persistence.criteria.Path<V> path(Root<? extends X> root) {
24+
final jakarta.persistence.criteria.Path<V> path = parent.path( root ).get( attributeName );
25+
if ( !attributeType.isAssignableFrom( path.getJavaType() ) ) {
26+
throw new IllegalArgumentException( "Attribute '" + attributeName
27+
+ "' is not of type '" + attributeType.getName() + "'" );
28+
}
29+
return path;
30+
}
31+
}

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
* the root entity type of the query.
1919
* <pre>
2020
* session.createSelectionQuery("from Book", Book.class)
21-
* .addRestriction(root(publisher).get(name).equalTo("Manning"))
21+
* .addRestriction(from(Book.class).to(Book_.publisher).to(Publisher_.name)
22+
* .equalTo("Manning"))
2223
* .getResultList()
2324
* </pre>
2425
* A compound path-based restriction has the same semantics as the
@@ -37,15 +38,21 @@
3738
public interface Path<X,U> {
3839
jakarta.persistence.criteria.Path<U> path(Root<? extends X> root);
3940

40-
default <V> Path<X, V> get(SingularAttribute<? super U, V> attribute) {
41+
Class<U> getType();
42+
43+
default <V> Path<X, V> to(SingularAttribute<? super U, V> attribute) {
4144
return new PathElement<>( this, attribute );
4245
}
4346

44-
static <X, U> Path<X, U> root(SingularAttribute<? super X, U> attribute) {
45-
return new PathRoot<X>().get( attribute );
47+
default <V> Path<X, V> to(String attributeName, Class<V> attributeType) {
48+
return new NamedPathElement<>( this, attributeName, attributeType );
49+
}
50+
51+
static <X> Path<X, X> from(Class<X> type) {
52+
return new PathRoot<>( type );
4653
}
4754

48-
default Restriction<X> restrict(Range<U> range) {
55+
default Restriction<X> restrict(Range<? super U> range) {
4956
return new PathRange<>( this, range );
5057
}
5158

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

68-
//TODO: between, lessThan, greaterThan?
75+
default Restriction<X> notNull() {
76+
return restrict( Range.notNull( getType() ) );
77+
}
6978
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212
*
1313
* @author Gavin King
1414
*/
15-
record PathElement<X, U, V>(Path<? super X, U> parent, SingularAttribute<? super U, V> attribute) implements Path<X, V> {
15+
record PathElement<X, U, V>(Path<? super X, U> parent, SingularAttribute<? super U, V> attribute)
16+
implements Path<X, V> {
17+
@Override
18+
public Class<V> getType() {
19+
return attribute.getJavaType();
20+
}
21+
1622
@Override
1723
public jakarta.persistence.criteria.Path<V> path(Root<? extends X> root) {
1824
return parent.path( root ).get( attribute );

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*
1818
* @author Gavin King
1919
*/
20-
record PathRange<X, U>(Path<X, U> path, Range<U> range) implements Restriction<X> {
20+
record PathRange<X, U>(Path<X, U> path, Range<? super U> range) implements Restriction<X> {
2121
@Override
2222
public Restriction<X> negated() {
2323
return new Negation<>( this );

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
1111
*
1212
* @author Gavin King
1313
*/
14-
record PathRoot<X>() implements Path<X, X> {
14+
record PathRoot<X>(Class<X> type) implements Path<X, X> {
15+
@Override
16+
public Class<X> getType() {
17+
return type;
18+
}
19+
1520
@Override
1621
@SuppressWarnings("unchecked")
1722
public jakarta.persistence.criteria.Path<X> path(Root<? extends X> root) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ static <T> Restriction<T> notContains(SingularAttribute<T, String> attribute, St
203203
return contains( attribute, substring, caseSensitive ).negated();
204204
}
205205

206+
static <T, U> Restriction<T> notNull(SingularAttribute<T, U> attribute) {
207+
return restrict( attribute, Range.notNull( attribute.getJavaType() ) );
208+
}
209+
206210
static <T> Restriction<T> all(List<? extends Restriction<? super T>> restrictions) {
207211
return new Conjunction<>( restrictions );
208212
}

hibernate-core/src/main/java/org/hibernate/query/range/CaseInsensitiveValue.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ record CaseInsensitiveValue(String value) implements Range<String> {
2323
}
2424

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

3537
@Override

hibernate-core/src/main/java/org/hibernate/query/range/EmptyRange.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
*/
1616
record EmptyRange<U>(Class<U> type) implements Range<U> {
1717
@Override
18-
public Class<? extends U> getType() {
18+
public Class<U> getType() {
1919
return type;
2020
}
2121

2222
@Override
23-
public Predicate toPredicate(Path<U> path, CriteriaBuilder builder) {
23+
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
2424
return builder.disjunction();
2525
}
2626
}

0 commit comments

Comments
 (0)