diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java index a04b86cc80..1ce621cd69 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java @@ -103,9 +103,12 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder { JpaCriteriaQuery except(boolean all, CriteriaQuery query1, CriteriaQuery... queries); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Paths + JpaExpression fk(Path

path); + @Override JpaPath treat(Path path, Class type); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java index 4747e1b70a..d1fc51a033 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/NodeBuilder.java @@ -23,6 +23,7 @@ import org.hibernate.query.criteria.JpaCoalesce; import org.hibernate.query.criteria.JpaCompoundSelection; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaParameterExpression; +import org.hibernate.query.criteria.JpaPath; import org.hibernate.query.criteria.JpaSearchedCase; import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.criteria.JpaSimpleCase; @@ -146,6 +147,9 @@ public interface NodeBuilder extends HibernateCriteriaBuilder { @Override SqmPredicate wrap(Expression... expressions); + @Override + SqmExpression fk(Path

path); + @Override SqmPath treat(Path path, Class type); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index 9e83c1fc85..fee2f9c393 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -39,6 +39,7 @@ import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; import org.hibernate.query.ReturnableType; import org.hibernate.query.BindableType; +import org.hibernate.query.SemanticException; import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.NullPrecedence; @@ -64,6 +65,8 @@ import org.hibernate.query.sqm.spi.SqmCreationContext; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.domain.SqmBagJoin; +import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; +import org.hibernate.query.sqm.tree.domain.SqmFkExpression; import org.hibernate.query.sqm.tree.domain.SqmListJoin; import org.hibernate.query.sqm.tree.domain.SqmMapJoin; import org.hibernate.query.sqm.tree.domain.SqmPath; @@ -132,6 +135,7 @@ import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.Selection; import jakarta.persistence.criteria.SetJoin; import jakarta.persistence.criteria.Subquery; +import jakarta.persistence.metamodel.Bindable; import static java.util.Arrays.asList; import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType; @@ -342,6 +346,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } + @Override + public SqmExpression fk(Path

path) { + if ( path.getModel().getBindableType() != Bindable.BindableType.SINGULAR_ATTRIBUTE ) { + throw new SemanticException( "Path should refer to a to-one attribute : " + path ); + } + + if ( ! ( path instanceof SqmEntityValuedSimplePath ) ) { + throw new SemanticException( "Path should refer to a to-one attribute : " + path ); + } + + return new SqmFkExpression<>( (SqmEntityValuedSimplePath) path, this ); + } + @Override public SqmPath treat(Path path, Class type) { return ( (SqmPath) path ).treatAs( type ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/NodeBuilderTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/NodeBuilderTests.java new file mode 100644 index 0000000000..cb58880dfa --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/NodeBuilderTests.java @@ -0,0 +1,30 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.criteria; + +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +/** + * Simple {@link org.hibernate.query.sqm.NodeBuilder} tests + * + * @author Steve Ebersole + */ +@DomainModel( standardModels = StandardDomainModel.RETAIL ) +@SessionFactory +public class NodeBuilderTests { + @Test + public void testFkExpression(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + final String hql = "from Order o where fk(o.salesAssociate) = 1"; + session.createSelectionQuery( hql ).getResultList(); + } ); + } +}