Skip to content

Commit 227caea

Browse files
committed
HHH-19116 Implement support for explicit joins in fk() function
1 parent a0fa18c commit 227caea

File tree

8 files changed

+169
-60
lines changed

8 files changed

+169
-60
lines changed

documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ Hibernate does support a means to refer specifically to the key column (`Person.
542542
using the special `fk(..)` function. E.g.
543543

544544
[[associations-not-found-fk-function-example]]
545-
.Implicit join example
545+
.FK example
546546
====
547547
[source,java]
548548
----

hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@
125125
import org.hibernate.query.sqm.tree.domain.SqmCteRoot;
126126
import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
127127
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
128-
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
129128
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
130129
import org.hibernate.query.sqm.tree.domain.SqmFunctionRoot;
131130
import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
@@ -3579,7 +3578,7 @@ public SqmFkExpression<?> visitToOneFkReference(HqlParser.ToOneFkReferenceContex
35793578
);
35803579

35813580
}
3582-
return new SqmFkExpression<>( (SqmEntityValuedSimplePath<?>) sqmPath );
3581+
return new SqmFkExpression<>( sqmPath );
35833582
}
35843583

35853584
@Override

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.HashSet;
2929
import java.util.LinkedHashMap;
3030
import java.util.List;
31+
import java.util.Locale;
3132
import java.util.Map;
3233
import java.util.ServiceLoader;
3334
import java.util.Set;
@@ -45,6 +46,7 @@
4546
import org.hibernate.metamodel.model.domain.JpaMetamodel;
4647
import org.hibernate.metamodel.model.domain.PersistentAttribute;
4748
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
49+
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
4850
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
4951
import org.hibernate.query.BindableType;
5052
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
@@ -98,7 +100,6 @@
98100
import org.hibernate.query.sqm.tree.cte.SqmSearchClauseSpecification;
99101
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
100102
import org.hibernate.query.sqm.tree.domain.SqmBagJoin;
101-
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
102103
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
103104
import org.hibernate.query.sqm.tree.domain.SqmListJoin;
104105
import org.hibernate.query.sqm.tree.domain.SqmMapJoin;
@@ -595,12 +596,22 @@ public <T extends HibernateCriteriaBuilder> T unwrap(Class<T> clazz) {
595596

596597
@Override
597598
public <P, F> SqmExpression<F> fk(Path<P> path) {
598-
if ( path.getModel().getBindableType() != Bindable.BindableType.SINGULAR_ATTRIBUTE
599-
|| ! ( path instanceof SqmEntityValuedSimplePath ) ) {
600-
throw new FunctionArgumentException( "Path '" + path + "' does not refer to a single-valued association" );
599+
final SqmPath<P> sqmPath = (SqmPath<P>) path;
600+
final SqmPathSource<?> toOneReference = sqmPath.getReferencedPathSource();
601+
final boolean validToOneRef =
602+
toOneReference.getBindableType() == Bindable.BindableType.SINGULAR_ATTRIBUTE
603+
&& toOneReference instanceof EntitySqmPathSource;
604+
if ( !validToOneRef ) {
605+
throw new FunctionArgumentException(
606+
String.format(
607+
Locale.ROOT,
608+
"Argument '%s' of 'fk()' function is not a single-valued association",
609+
sqmPath.getNavigablePath()
610+
)
611+
);
601612
}
602613

603-
return new SqmFkExpression<>( (SqmEntityValuedSimplePath<?>) path );
614+
return new SqmFkExpression<>( sqmPath );
604615
}
605616

606617
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ public Object visitNonAggregatedCompositeValuedPath(NonAggregatedCompositeSimple
745745

746746
@Override
747747
public Object visitFkExpression(SqmFkExpression<?> fkExpression) {
748-
logWithIndentation( "-> [fk-ref] - `%s`", fkExpression.getToOnePath().getNavigablePath() );
748+
logWithIndentation( "-> [fk-ref] - `%s`", fkExpression.getLhs().getNavigablePath() );
749749

750750
return null;
751751
}

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4567,11 +4567,12 @@ public Expression visitPluralValuedPath(SqmPluralValuedSimplePath<?> sqmPath) {
45674567

45684568
@Override
45694569
public Object visitFkExpression(SqmFkExpression<?> fkExpression) {
4570-
final SqmPath<?> lhs = fkExpression.getToOnePath().getLhs();
4570+
final SqmPath<?> toOnePath = fkExpression.getLhs();
4571+
final SqmPath<?> lhs = toOnePath.getLhs();
45714572
prepareReusablePath( lhs, () -> null );
45724573
final TableGroup tableGroup = getFromClauseIndex().findTableGroup( lhs.getNavigablePath() );
45734574
final ModelPart subPart = tableGroup.getModelPart()
4574-
.findSubPart( fkExpression.getToOnePath().getModel().getPathName(), null );
4575+
.findSubPart( toOnePath.getReferencedPathSource().getPathName(), null );
45754576
assert subPart instanceof ToOneAttributeMapping;
45764577

45774578
final ToOneAttributeMapping toOneMapping = (ToOneAttributeMapping) subPart;

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.query.sqm.tree.domain;
66

77
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
8+
import org.hibernate.metamodel.model.domain.DomainType;
89
import org.hibernate.metamodel.model.domain.EntityDomainType;
910
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
1011
import org.hibernate.query.hql.spi.SqmCreationState;
@@ -21,15 +22,14 @@
2122
* @author Steve Ebersole
2223
*/
2324
public class SqmFkExpression<T> extends AbstractSqmPath<T> {
24-
25-
public SqmFkExpression(SqmEntityValuedSimplePath<?> toOnePath) {
25+
public SqmFkExpression(SqmPath<?> toOnePath) {
2626
this( toOnePath.getNavigablePath().append( ForeignKeyDescriptor.PART_NAME ), toOnePath );
2727
}
2828

2929
@SuppressWarnings("unchecked")
3030
private SqmFkExpression(
3131
NavigablePath navigablePath,
32-
SqmEntityValuedSimplePath<?> toOnePath) {
32+
SqmPath<?> toOnePath) {
3333
super(
3434
navigablePath,
3535
(SqmPathSource<T>) pathDomainType( toOnePath ).getIdentifierDescriptor(),
@@ -38,12 +38,14 @@ private SqmFkExpression(
3838
);
3939
}
4040

41-
private static IdentifiableDomainType<?> pathDomainType(SqmEntityValuedSimplePath<?> toOnePath) {
42-
return (IdentifiableDomainType<?>) toOnePath.getNodeType();
43-
}
44-
45-
public SqmEntityValuedSimplePath<?> getToOnePath() {
46-
return (SqmEntityValuedSimplePath<?>) getLhs();
41+
private static IdentifiableDomainType<?> pathDomainType(SqmPath<?> toOnePath) {
42+
final DomainType<?> domainType = toOnePath.getReferencedPathSource().getSqmPathType();
43+
if ( domainType instanceof IdentifiableDomainType<?> identifiableDomainType ) {
44+
return identifiableDomainType;
45+
}
46+
else {
47+
throw new IllegalArgumentException( "Invalid path provided to 'fk()' function: " + toOnePath.getNavigablePath() );
48+
}
4749
}
4850

4951
@Override

hibernate-core/src/test/java/org/hibernate/orm/test/associations/NotFoundTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public void queryTestFk(EntityManagerFactoryScope scope) {
125125
assertThat( nullResults ).isEmpty();
126126

127127
final List<String> nonNullResults = entityManager
128-
.createQuery( "select p.name from Person p where fk( p.city ) is not null", String.class )
128+
.createQuery( "select p.name from Person p left join p.city c where fk( c ) is not null", String.class )
129129
.getResultList();
130130
assertThat( nonNullResults ).hasSize( 1 );
131131
assertThat( nonNullResults.get( 0 ) ).isEqualTo( "John Doe" );

0 commit comments

Comments
 (0)