Skip to content

Commit 5e107b8

Browse files
committed
HHH-18979 add Restriction.apply() so it can be easily used with criteria queries
1 parent 7e14baa commit 5e107b8

File tree

6 files changed

+82
-7
lines changed

6 files changed

+82
-7
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,10 @@ public CriteriaDefinition(CriteriaDefinition<R> template) {
148148
*/
149149
public CriteriaDefinition(CriteriaDefinition<?> template, Class<R> resultType) {
150150
super( template.getCriteriaBuilder() );
151-
query = ((SqmSelectStatement<?>) template.query)
152-
.createCopy( SqmCopyContext.simpleContext(), resultType );
151+
if ( !(template.query instanceof SqmSelectStatement<?> selectStatement) ) {
152+
throw new IllegalArgumentException( "Not a SqmSelectStatement" );
153+
}
154+
query = selectStatement.createCopy( SqmCopyContext.simpleContext(), resultType );
153155
}
154156

155157
public CriteriaDefinition(SessionFactory factory, Class<R> resultType) {
@@ -220,6 +222,11 @@ public JpaCriteriaQuery<R> restrict(Predicate predicate) {
220222
return existing == null ? where( predicate ) : where( existing, predicate );
221223
}
222224

225+
@Override
226+
public HibernateCriteriaBuilder getCriteriaBuilder() {
227+
return query.getCriteriaBuilder();
228+
}
229+
223230
@Override
224231
public JpaCriteriaQuery<R> select(Selection<? extends R> selection) {
225232
return query.select(selection);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,6 @@ default List<Order> getOrderList() {
132132

133133
@Override
134134
<U> JpaSubQuery<U> subquery(EntityType<U> type);
135+
136+
HibernateCriteriaBuilder getCriteriaBuilder();
135137
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
package org.hibernate.query.restriction;
66

77
import jakarta.persistence.criteria.CriteriaBuilder;
8+
import jakarta.persistence.criteria.CriteriaQuery;
89
import jakarta.persistence.criteria.Predicate;
910
import jakarta.persistence.criteria.Root;
1011
import jakarta.persistence.metamodel.SingularAttribute;
1112
import org.hibernate.Incubating;
1213
import org.hibernate.Internal;
1314
import org.hibernate.query.Order;
1415
import org.hibernate.query.SelectionQuery;
16+
import org.hibernate.query.criteria.JpaCriteriaQuery;
1517
import org.hibernate.query.range.Range;
1618

1719
import java.util.List;
@@ -77,6 +79,17 @@ default Restriction<X> and(Restriction<X> restriction) {
7779
@Internal
7880
Predicate toPredicate(Root<? extends X> root, CriteriaBuilder builder);
7981

82+
/**
83+
* Apply this restriction to the given root entity of the given
84+
* {@linkplain CriteriaQuery criteria query}.
85+
*/
86+
default void apply(CriteriaQuery<?> query, Root<? extends X> root) {
87+
if ( !(query instanceof JpaCriteriaQuery<?> criteriaQuery) ) {
88+
throw new IllegalArgumentException( "Not a JpaCriteriaQuery" );
89+
}
90+
query.where( query.getRestriction(), toPredicate( root, criteriaQuery.getCriteriaBuilder() ) );
91+
}
92+
8093
/**
8194
* Restrict the allowed values of the given attribute to the given
8295
* {@linkplain Range range}.

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,13 @@ public SelectionQuery<R> setOrder(Order<? super R> order) {
150150
@Override
151151
public SelectionQuery<R> addRestriction(Restriction<? super R> restriction) {
152152
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext() );
153-
final Root<? extends R> root = (Root<? extends R>) selectStatement.getRootList().get( 0 );
154-
selectStatement.where( selectStatement.getRestriction(),
155-
restriction.toPredicate( root, selectStatement.nodeBuilder() ) );
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 );
156160
// TODO: when the QueryInterpretationCache can handle caching criteria queries,
157161
// simply cache the new SQM as if it were a criteria query, and remove this:
158162
getQueryOptions().setQueryPlanCachingEnabled( false );

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.hibernate.query.sqm.NodeBuilder;
2121
import org.hibernate.query.sqm.SemanticQueryWalker;
2222
import org.hibernate.query.sqm.SqmQuerySource;
23+
import org.hibernate.query.sqm.internal.ParameterCollector;
2324
import org.hibernate.query.sqm.internal.SqmUtil;
2425
import org.hibernate.query.sqm.tree.SqmCopyContext;
2526
import org.hibernate.query.sqm.tree.SqmStatement;
@@ -49,8 +50,8 @@
4950
/**
5051
* @author Steve Ebersole
5152
*/
52-
public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements JpaCriteriaQuery<T>, SqmStatement<T>,
53-
org.hibernate.query.sqm.internal.ParameterCollector {
53+
public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T>
54+
implements JpaCriteriaQuery<T>, SqmStatement<T>, ParameterCollector {
5455
private final SqmQuerySource querySource;
5556

5657
private Set<SqmParameter<?>> parameters;
@@ -160,6 +161,11 @@ public void validateResultType(Class<?> resultType) {
160161
SqmUtil.validateQueryReturnType( getQueryPart(), resultType );
161162
}
162163

164+
@Override
165+
public NodeBuilder getCriteriaBuilder() {
166+
return nodeBuilder();
167+
}
168+
163169
@Override
164170
public SqmQuerySource getQuerySource() {
165171
return querySource;

hibernate-core/src/test/java/org/hibernate/orm/test/query/restriction/RestrictionTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import jakarta.persistence.Id;
99
import jakarta.persistence.ManyToOne;
1010
import jakarta.persistence.Version;
11+
import jakarta.persistence.criteria.Root;
1112
import jakarta.persistence.metamodel.SingularAttribute;
1213
import org.hibernate.testing.orm.junit.DomainModel;
1314
import org.hibernate.testing.orm.junit.SessionFactory;
@@ -216,6 +217,48 @@ void testPath(SessionFactoryScope scope) {
216217
assertEquals( 2, unsafeTest.size() );
217218
}
218219

220+
@Test
221+
void testCriteria(SessionFactoryScope scope) {
222+
scope.getSessionFactory().getSchemaManager().truncate();
223+
scope.inTransaction( session -> {
224+
session.persist(new Book("9781932394153", "Hibernate in Action", 400));
225+
session.persist(new Book("9781617290459", "Java Persistence with Hibernate", 1000));
226+
});
227+
228+
var bookType = scope.getSessionFactory().getJpaMetamodel().findEntityType(Book.class);
229+
@SuppressWarnings( "unchecked" )
230+
var title = (SingularAttribute<? super Book, String>) bookType.findSingularAttribute("title");
231+
@SuppressWarnings( "unchecked" )
232+
var isbn = (SingularAttribute<? super Book, String>) bookType.findSingularAttribute("isbn");
233+
@SuppressWarnings( "unchecked" )
234+
var pages = (SingularAttribute<? super Book, Integer>) bookType.findSingularAttribute("pages");
235+
236+
scope.inSession( session -> {
237+
var query = session.getCriteriaBuilder().createQuery(String.class);
238+
var root = query.from( Book.class );
239+
like( title, "%Hibernate%" ).apply( query, root );
240+
query.select( root.get( title ) );
241+
List<String> titles = session.createQuery( query ).getResultList();
242+
assertEquals( 2, titles.size() );
243+
} );
244+
scope.inSession( session -> {
245+
var query = session.getCriteriaBuilder().createQuery(String.class);
246+
var root = query.from( Book.class );
247+
equal( isbn, "9781932394153" ).apply( query, root );
248+
query.select( root.get( title ) );
249+
List<String> titles = session.createQuery( query ).getResultList();
250+
assertEquals( 1, titles.size() );
251+
} );
252+
scope.inSession( session -> {
253+
var query = session.getCriteriaBuilder().createQuery("select title from Book", String.class);
254+
var root = (Root<Book>) query.getRootList().get(0);
255+
equal( isbn, "9781932394153" ).apply( query, root );
256+
List<String> titles = session.createQuery( query ).getResultList();
257+
assertEquals( 1, titles.size() );
258+
} );
259+
}
260+
261+
219262
@Entity(name="Book")
220263
static class Book {
221264
@Id

0 commit comments

Comments
 (0)