mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-22 11:06:08 +00:00
HHH-18584 fix logic for deciding if something is implicitly selectable
implicit joins should not be added to the select list! Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
306991f8d9
commit
0c1a1e9832
@ -1286,10 +1286,10 @@ private SqmFromClause buildInferredFromClause(HqlParser.SelectClauseContext sele
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private EntityDomainType<R> getResultEntity() {
|
||||
final JpaMetamodelImplementor jpaMetamodel = creationContext.getJpaMetamodel();
|
||||
if ( expectedResultEntity != null ) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
final EntityDomainType entityDescriptor = jpaMetamodel.entity( expectedResultEntity );
|
||||
if ( entityDescriptor == null ) {
|
||||
throw new SemanticException( "Query has no 'from' clause, and the result type '"
|
||||
@ -1331,7 +1331,8 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
|
||||
// we found an entity with the alias 'this'
|
||||
// assigned explicitly, JPA says we should
|
||||
// infer the select list 'select this'
|
||||
SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
|
||||
final SqmSelectClause selectClause =
|
||||
new SqmSelectClause( false, 1, nodeBuilder );
|
||||
selectClause.addSelection( new SqmSelection<>( sqmRoot, "this", nodeBuilder) );
|
||||
return selectClause;
|
||||
}
|
||||
@ -1353,7 +1354,7 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
|
||||
// we may safely assume the query returns that entity
|
||||
if ( fromClause.getNumberOfRoots() == 1 ) {
|
||||
final SqmRoot<?> sqmRoot = fromClause.getRoots().get(0);
|
||||
if ( sqmRoot.hasTrueJoin() ) {
|
||||
if ( sqmRoot.hasImplicitlySelectableJoin() ) {
|
||||
// the entity has joins, and doesn't explicitly have
|
||||
// the alias 'this', so the 'select' list cannot be
|
||||
// inferred
|
||||
@ -1365,7 +1366,8 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
|
||||
// 'this', and that we should infer 'select this', but we
|
||||
// accept even the case where the entity has an explicit
|
||||
// alias, and infer 'select explicit_alias'
|
||||
SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
|
||||
final SqmSelectClause selectClause =
|
||||
new SqmSelectClause( false, 1, nodeBuilder );
|
||||
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), nodeBuilder) );
|
||||
return selectClause;
|
||||
}
|
||||
@ -1398,7 +1400,8 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
|
||||
else {
|
||||
// exactly one root entity, return it
|
||||
// (joined entities are not returned)
|
||||
final SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
|
||||
final SqmSelectClause selectClause =
|
||||
new SqmSelectClause( false, 1, nodeBuilder );
|
||||
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), nodeBuilder) );
|
||||
return selectClause;
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
import org.hibernate.query.criteria.JpaPredicate;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||
import org.hibernate.query.sqm.SqmJoinable;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
@ -33,48 +32,38 @@ public abstract class AbstractSqmAttributeJoin<L, R>
|
||||
extends AbstractSqmJoin<L, R>
|
||||
implements SqmAttributeJoin<L, R> {
|
||||
|
||||
private boolean fetched;
|
||||
private final boolean implicitJoin;
|
||||
private boolean fetchJoin;
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public AbstractSqmAttributeJoin(
|
||||
SqmFrom<?, L> lhs,
|
||||
SqmJoinable joinedNavigable,
|
||||
String alias,
|
||||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
NodeBuilder nodeBuilder) {
|
||||
//noinspection StringEquality
|
||||
this(
|
||||
lhs,
|
||||
joinedNavigable.createNavigablePath( lhs, alias ),
|
||||
joinedNavigable,
|
||||
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
|
||||
joinType,
|
||||
fetched,
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected AbstractSqmAttributeJoin(
|
||||
SqmFrom<?, L> lhs,
|
||||
NavigablePath navigablePath,
|
||||
SqmJoinable joinedNavigable,
|
||||
SqmPathSource<R> joinedNavigable,
|
||||
String alias,
|
||||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
boolean fetchJoin,
|
||||
NodeBuilder nodeBuilder) {
|
||||
//noinspection unchecked
|
||||
super(
|
||||
navigablePath,
|
||||
(SqmPathSource<R>) joinedNavigable,
|
||||
joinedNavigable,
|
||||
lhs,
|
||||
alias,
|
||||
isImplicitAlias( alias ) ? null : alias,
|
||||
joinType,
|
||||
nodeBuilder
|
||||
);
|
||||
this.fetched = fetched;
|
||||
this.fetchJoin = fetchJoin;
|
||||
validateFetchAlias( alias );
|
||||
implicitJoin = isImplicitAlias( alias ); //TODO: add a parameter
|
||||
}
|
||||
|
||||
@SuppressWarnings("StringEquality")
|
||||
private static boolean isImplicitAlias(String alias) {
|
||||
return alias == SqmCreationHelper.IMPLICIT_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImplicitJoin() {
|
||||
return implicitJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -89,7 +78,7 @@ public JavaType<R> getNodeJavaType() {
|
||||
|
||||
@Override
|
||||
public boolean isFetched() {
|
||||
return fetched;
|
||||
return fetchJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,11 +89,11 @@ public SqmAttributeJoin<L,R> alias(String name) {
|
||||
|
||||
@Override
|
||||
public void clearFetched() {
|
||||
fetched = false;
|
||||
fetchJoin = false;
|
||||
}
|
||||
|
||||
private void validateFetchAlias(String alias) {
|
||||
if ( fetched && alias != null && nodeBuilder().isJpaQueryComplianceEnabled() ) {
|
||||
if ( fetchJoin && alias != null && nodeBuilder().isJpaQueryComplianceEnabled() ) {
|
||||
throw new IllegalStateException(
|
||||
"The JPA specification does not permit specifying an alias for fetch joins."
|
||||
);
|
||||
|
@ -60,7 +60,6 @@
|
||||
import jakarta.persistence.metamodel.SetAttribute;
|
||||
import jakarta.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import static org.hibernate.metamodel.AttributeClassification.EMBEDDED;
|
||||
import static org.hibernate.query.sqm.internal.SqmUtil.findCompatibleFetchJoin;
|
||||
|
||||
/**
|
||||
@ -163,9 +162,8 @@ public SqmPath<?> resolvePathPart(
|
||||
SqmPath<?> resolvedPath = null;
|
||||
for ( SqmJoin<?, ?> sqmJoin : getSqmJoins() ) {
|
||||
// We can only match singular joins here, as plural path parts are interpreted like sub-queries
|
||||
if ( sqmJoin instanceof SqmSingularJoin<?, ?>
|
||||
if ( sqmJoin instanceof SqmSingularJoin<?, ?> attributeJoin
|
||||
&& name.equals( sqmJoin.getReferencedPathSource().getPathName() ) ) {
|
||||
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
|
||||
if ( attributeJoin.getOn() == null ) {
|
||||
// todo (6.0): to match the expectation of the JPA spec I think we also have to check
|
||||
// that the join type is INNER or the default join type for the attribute,
|
||||
@ -223,8 +221,7 @@ public void addSqmJoin(SqmJoin<T, ?> join) {
|
||||
public void removeLeftFetchJoins() {
|
||||
if ( joins != null ) {
|
||||
for ( SqmJoin<T, ?> join : new ArrayList<>(joins) ) {
|
||||
if ( join instanceof SqmAttributeJoin ) {
|
||||
final SqmAttributeJoin<T, ?> attributeJoin = (SqmAttributeJoin<T, ?>) join;
|
||||
if ( join instanceof SqmAttributeJoin<T, ?> attributeJoin ) {
|
||||
if ( attributeJoin.isFetched() ) {
|
||||
if ( join.getSqmJoinType() == SqmJoinType.LEFT ) {
|
||||
joins.remove( join );
|
||||
@ -306,11 +303,10 @@ public boolean isCorrelated() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTrueJoin() {
|
||||
public boolean hasImplicitlySelectableJoin() {
|
||||
return getSqmJoins().stream()
|
||||
.anyMatch( sqmJoin -> sqmJoin instanceof SqmAttributeJoin<?,?> attributeJoin
|
||||
&& !attributeJoin.isFetched()
|
||||
&& attributeJoin.getAttribute().getAttributeClassification()!=EMBEDDED );
|
||||
&& attributeJoin.isImplicitlySelectable() );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -642,8 +638,7 @@ public <X> JpaCrossJoin<X> crossJoin(Class<X> entityJavaType) {
|
||||
|
||||
@Override
|
||||
public <X> JpaCrossJoin<X> crossJoin(EntityDomainType<X> entity) {
|
||||
//noinspection unchecked
|
||||
final SqmCrossJoin<X> crossJoin = new SqmCrossJoin<>( entity, null, (SqmRoot<X>) findRoot() );
|
||||
final SqmCrossJoin<X> crossJoin = new SqmCrossJoin<>( entity, null, findRoot() );
|
||||
// noinspection unchecked
|
||||
addSqmJoin( (SqmJoin<T, ?>) crossJoin );
|
||||
return crossJoin;
|
||||
|
@ -35,7 +35,15 @@ public AbstractSqmPluralJoin(
|
||||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
NodeBuilder nodeBuilder) {
|
||||
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
|
||||
super(
|
||||
lhs,
|
||||
joinedNavigable.createNavigablePath( lhs, alias ),
|
||||
joinedNavigable,
|
||||
alias,
|
||||
joinType,
|
||||
fetched,
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
|
||||
protected AbstractSqmPluralJoin(
|
||||
|
@ -15,7 +15,6 @@
|
||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SqmJoinable;
|
||||
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
@ -25,6 +24,7 @@
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> implements SqmSingularValuedJoin<O,T> {
|
||||
|
||||
public SqmSingularJoin(
|
||||
SqmFrom<?,O> lhs,
|
||||
SingularPersistentAttribute<O, T> joinedNavigable,
|
||||
@ -32,17 +32,15 @@ public SqmSingularJoin(
|
||||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
NodeBuilder nodeBuilder) {
|
||||
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
|
||||
}
|
||||
|
||||
public SqmSingularJoin(
|
||||
SqmFrom<?,O> lhs,
|
||||
SqmJoinable<? extends O, T> joinedNavigable,
|
||||
String alias,
|
||||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
NodeBuilder nodeBuilder) {
|
||||
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
|
||||
super(
|
||||
lhs,
|
||||
joinedNavigable.createNavigablePath( lhs, alias ),
|
||||
joinedNavigable,
|
||||
alias,
|
||||
joinType,
|
||||
fetched,
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,7 +30,7 @@ public interface SqmAttributeJoin<O,T> extends SqmJoin<O,T>, JpaFetch<O,T>, JpaJ
|
||||
|
||||
@Override
|
||||
default boolean isImplicitlySelectable() {
|
||||
return !isFetched();
|
||||
return !isFetched() && !isImplicitJoin();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -39,8 +39,16 @@ default boolean isImplicitlySelectable() {
|
||||
@Override
|
||||
JavaType<T> getJavaTypeDescriptor();
|
||||
|
||||
/**
|
||||
* Is this a fetch join?
|
||||
*/
|
||||
boolean isFetched();
|
||||
|
||||
/**
|
||||
* Is this an implicit join inferred from a path expression?
|
||||
*/
|
||||
boolean isImplicitJoin();
|
||||
|
||||
@Internal
|
||||
void clearFetched();
|
||||
|
||||
|
@ -102,7 +102,7 @@ default boolean hasTreats() {
|
||||
<Y> SqmEntityJoin<R, Y> join(Class<Y> entityClass, JoinType joinType);
|
||||
|
||||
@Incubating
|
||||
boolean hasTrueJoin();
|
||||
boolean hasImplicitlySelectableJoin();
|
||||
|
||||
@Override
|
||||
<A> SqmSingularJoin<R, A> join(SingularAttribute<? super R, A> attribute);
|
||||
|
Loading…
x
Reference in New Issue
Block a user