HHH-18569 Don´t implicitly cast when using a subtype attribute name with Criteria API

This commit is contained in:
Marco Belladelli 2024-09-20 11:37:12 +02:00
parent c1cbbf1fd5
commit 1c87d73d2e
36 changed files with 130 additions and 89 deletions

View File

@ -12,7 +12,6 @@ import org.hibernate.metamodel.internal.MetadataContext;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SimpleDomainType; import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
@ -87,11 +86,11 @@ public abstract class AbstractPluralAttribute<D, C, E>
} }
@Override @Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) { public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
if ( CollectionPart.Nature.ELEMENT.getName().equals( name ) ) { if ( CollectionPart.Nature.ELEMENT.getName().equals( name ) ) {
return elementPathSource; return elementPathSource;
} }
return elementPathSource.findSubPathSource( name, metamodel ); return elementPathSource.findSubPathSource( name, includeSubtypes );
} }
@Override @Override

View File

@ -5,7 +5,6 @@
package org.hibernate.metamodel.model.domain.internal; package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.SqmJoinable; import org.hibernate.query.sqm.SqmJoinable;
import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.SqmPathSource;
@ -42,8 +41,8 @@ public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> implements
} }
@Override @Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) { public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
return getSqmPathType().findSubPathSource( name, metamodel ); return getSqmPathType().findSubPathSource( name, includeSubtypes );
} }
@Override @Override

View File

@ -139,14 +139,13 @@ public class EntityTypeImpl<J>
@Override @Override
public SqmPathSource<?> findSubPathSource(String name) { public SqmPathSource<?> findSubPathSource(String name) {
final PersistentAttribute<? super J,?> attribute = findAttribute( name ); final PersistentAttribute<? super J,?> attribute = super.findAttribute( name );
if ( attribute != null ) { if ( attribute != null ) {
return (SqmPathSource<?>) attribute; return (SqmPathSource<?>) attribute;
} }
else if ( EntityIdentifierMapping.matchesRoleName( name ) ) { else if ( EntityIdentifierMapping.matchesRoleName( name ) ) {
return hasSingleIdAttribute() ? findIdAttribute() : getIdentifierDescriptor(); return hasSingleIdAttribute() ? findIdAttribute() : getIdentifierDescriptor();
} }
else if ( EntityDiscriminatorMapping.matchesRoleName( name ) ) { else if ( EntityDiscriminatorMapping.matchesRoleName( name ) ) {
return discriminatorPathSource; return discriminatorPathSource;
} }
@ -156,18 +155,19 @@ public class EntityTypeImpl<J>
} }
@Override @Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) { public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
final PersistentAttribute<? super J,?> attribute = super.findAttribute( name ); final PersistentAttribute<? super J,?> attribute = super.findAttribute( name );
if ( attribute != null ) { if ( attribute != null ) {
return (SqmPathSource<?>) attribute; return (SqmPathSource<?>) attribute;
} }
else { else {
//TODO: eliminate this cast! if ( includeSubtypes ) {
final PersistentAttribute<?, ?> subtypeAttribute = findSubtypeAttribute( name ); final PersistentAttribute<?, ?> subtypeAttribute = findSubtypeAttribute( name );
if ( subtypeAttribute != null ) { if ( subtypeAttribute != null ) {
return (SqmPathSource<?>) subtypeAttribute; return (SqmPathSource<?>) subtypeAttribute;
} }
else if ( EntityIdentifierMapping.matchesRoleName( name ) ) { }
if ( EntityIdentifierMapping.matchesRoleName( name ) ) {
return hasSingleIdAttribute() ? findIdAttribute() : getIdentifierDescriptor(); return hasSingleIdAttribute() ? findIdAttribute() : getIdentifierDescriptor();
} }
else if ( EntityDiscriminatorMapping.matchesRoleName( name ) ) { else if ( EntityDiscriminatorMapping.matchesRoleName( name ) ) {

View File

@ -8,7 +8,6 @@ import java.util.List;
import org.hibernate.metamodel.internal.MetadataContext; import org.hibernate.metamodel.internal.MetadataContext;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.ListPersistentAttribute; import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.hql.spi.SqmCreationState;
@ -60,7 +59,7 @@ public class ListAttributeImpl<X, E> extends AbstractPluralAttribute<X, List<E>,
} }
@Override @Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) { public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( name ); final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( name );
if ( nature != null ) { if ( nature != null ) {
switch ( nature ) { switch ( nature ) {
@ -70,7 +69,7 @@ public class ListAttributeImpl<X, E> extends AbstractPluralAttribute<X, List<E>,
return getElementPathSource(); return getElementPathSource();
} }
} }
return getElementPathSource().findSubPathSource( name, metamodel ); return getElementPathSource().findSubPathSource( name, includeSubtypes );
} }
@Override @Override

View File

@ -8,7 +8,6 @@ import java.util.Map;
import org.hibernate.metamodel.internal.MetadataContext; import org.hibernate.metamodel.internal.MetadataContext;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.MapPersistentAttribute; import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.SimpleDomainType; import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.SqmPathSource;
@ -70,7 +69,7 @@ public class MapAttributeImpl<X, K, V> extends AbstractPluralAttribute<X, Map<K,
} }
@Override @Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) { public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( name ); final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( name );
if ( nature != null ) { if ( nature != null ) {
switch ( nature ) { switch ( nature ) {
@ -80,7 +79,7 @@ public class MapAttributeImpl<X, K, V> extends AbstractPluralAttribute<X, Map<K,
return getElementPathSource(); return getElementPathSource();
} }
} }
return getElementPathSource().findSubPathSource( name, metamodel ); return getElementPathSource().findSubPathSource( name, includeSubtypes );
} }
@Override @Override

View File

@ -14,7 +14,6 @@ import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.IdentifiableDomainType; import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SimpleDomainType; import org.hibernate.metamodel.model.domain.SimpleDomainType;
@ -132,8 +131,8 @@ public class SingularAttributeImpl<D,J>
} }
@Override @Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) { public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
return sqmPathSource.findSubPathSource( name, metamodel ); return sqmPathSource.findSubPathSource( name, includeSubtypes );
} }
@Override @Override

View File

@ -145,7 +145,7 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
if ( pathRootByExposedNavigable != null ) { if ( pathRootByExposedNavigable != null ) {
// identifier is an "unqualified attribute reference" // identifier is an "unqualified attribute reference"
validateAsRoot( pathRootByExposedNavigable ); validateAsRoot( pathRootByExposedNavigable );
final SqmPath<?> sqmPath = pathRootByExposedNavigable.get( identifier ); final SqmPath<?> sqmPath = pathRootByExposedNavigable.get( identifier, true );
return isTerminal ? sqmPath : new DomainPathPart( sqmPath ); return isTerminal ? sqmPath : new DomainPathPart( sqmPath );
} }
} }

View File

@ -188,10 +188,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
boolean isTerminal, boolean isTerminal,
boolean allowReuse, boolean allowReuse,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPathSource<?> subPathSource = lhs.getResolvedModel().getSubPathSource( final SqmPathSource<?> subPathSource = lhs.getResolvedModel().getSubPathSource( name, true );
name,
creationState.getCreationContext().getJpaMetamodel()
);
if ( allowReuse ) { if ( allowReuse ) {
if ( !isTerminal ) { if ( !isTerminal ) {
for ( SqmJoin<?, ?> sqmJoin : lhs.getSqmJoins() ) { for ( SqmJoin<?, ?> sqmJoin : lhs.getSqmJoins() ) {

View File

@ -3587,7 +3587,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
+ "' of 'id()' is a '" + identifiableType.getTypeName() + "' of 'id()' is a '" + identifiableType.getTypeName()
+ "' and does not have a well-defined '@Id' attribute" ); + "' and does not have a well-defined '@Id' attribute" );
} }
return sqmPath.get( identifierDescriptor.getPathName() ); return sqmPath.get( identifierDescriptor.getPathName(), true );
} }
else if ( sqmPath instanceof SqmAnyValuedSimplePath<?> ) { else if ( sqmPath instanceof SqmAnyValuedSimplePath<?> ) {
return sqmPath.resolvePathPart( AnyKeyPart.KEY_NAME, true, processingStateStack.getCurrent().getCreationState() ); return sqmPath.resolvePathPart( AnyKeyPart.KEY_NAME, true, processingStateStack.getCurrent().getCreationState() );

View File

@ -335,7 +335,7 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
private boolean definesAttribute(SqmPathSource<?> containerType, String name) { private boolean definesAttribute(SqmPathSource<?> containerType, String name) {
return !( containerType.getSqmType() instanceof BasicDomainType ) return !( containerType.getSqmType() instanceof BasicDomainType )
&& containerType.findSubPathSource( name, getJpaMetamodel() ) != null; && containerType.findSubPathSource( name, true ) != null;
} }
private JpaMetamodel getJpaMetamodel() { private JpaMetamodel getJpaMetamodel() {

View File

@ -42,8 +42,8 @@ public interface SqmPathSource<J> extends SqmExpressible<J>, Bindable<J>, SqmExp
/** /**
* Find a {@link SqmPathSource} by name relative to this source. * Find a {@link SqmPathSource} by name relative to this source.
* *
* @param name the name of the path source to find
* @return null if the subPathSource is not found * @return null if the subPathSource is not found
*
* @throws IllegalStateException to indicate that this source cannot be de-referenced * @throws IllegalStateException to indicate that this source cannot be de-referenced
*/ */
SqmPathSource<?> findSubPathSource(String name); SqmPathSource<?> findSubPathSource(String name);
@ -52,16 +52,31 @@ public interface SqmPathSource<J> extends SqmExpressible<J>, Bindable<J>, SqmExp
* Find a {@link SqmPathSource} by name relative to this source. * Find a {@link SqmPathSource} by name relative to this source.
* *
* @return null if the subPathSource is not found * @return null if the subPathSource is not found
* @throws IllegalStateException to indicate that this source cannot be de-referenced
* @deprecated Use {@link #findSubPathSource(String, boolean)} instead
*/
@Deprecated(forRemoval = true, since = "7.0")
default SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) {
return findSubPathSource( name, true );
}
/**
* Find a {@link SqmPathSource} by name relative to this source. If {@code includeSubtypes} is set
* to {@code true} and this path source is polymorphic, also try finding subtype attributes.
* *
* @param name the name of the path source to find
* @param includeSubtypes flag indicating whether to consider subtype attributes
* @return null if the subPathSource is not found
* @throws IllegalStateException to indicate that this source cannot be de-referenced * @throws IllegalStateException to indicate that this source cannot be de-referenced
*/ */
default SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) { default SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
return findSubPathSource( name ); return findSubPathSource( name );
} }
/** /**
* Find a {@link SqmPathSource} by name relative to this source. * Find a {@link SqmPathSource} by name relative to this source.
* *
* @param name the name of the path source to find
* @throws IllegalStateException to indicate that this source cannot be de-referenced * @throws IllegalStateException to indicate that this source cannot be de-referenced
* @throws IllegalArgumentException if the subPathSource is not found * @throws IllegalArgumentException if the subPathSource is not found
*/ */
@ -81,13 +96,39 @@ public interface SqmPathSource<J> extends SqmExpressible<J>, Bindable<J>, SqmExp
} }
/** /**
* Find a {@link SqmPathSource} by name relative to this source. * Find a {@link SqmPathSource} by name relative to this source and all its subtypes.
* *
* @throws IllegalStateException to indicate that this source cannot be de-referenced * @throws IllegalStateException to indicate that this source cannot be de-referenced
* @throws IllegalArgumentException if the subPathSource is not found * @throws IllegalArgumentException if the subPathSource is not found
* @deprecated Use #{@link #getSubPathSource(String, boolean)} instead
*/ */
@Deprecated(forRemoval = true, since = "7.0")
default SqmPathSource<?> getSubPathSource(String name, JpaMetamodel metamodel) { default SqmPathSource<?> getSubPathSource(String name, JpaMetamodel metamodel) {
final SqmPathSource<?> subPathSource = findSubPathSource( name, metamodel ); final SqmPathSource<?> subPathSource = findSubPathSource( name, true );
if ( subPathSource == null ) {
throw new PathElementException(
String.format(
Locale.ROOT,
"Could not resolve attribute '%s' of '%s'",
name,
getExpressible().getTypeName()
)
);
}
return subPathSource;
}
/**
* Find a {@link SqmPathSource} by name relative to this source. If {@code subtypes} is set
* to {@code true} and this path source is polymorphic, also try finding subtype attributes.
*
* @param name the name of the path source to find
* @param subtypes flag indicating whether to consider subtype attributes
* @throws IllegalStateException to indicate that this source cannot be de-referenced
* @throws IllegalArgumentException if the subPathSource is not found
*/
default SqmPathSource<?> getSubPathSource(String name, boolean subtypes) {
final SqmPathSource<?> subPathSource = findSubPathSource( name, true );
if ( subPathSource == null ) { if ( subPathSource == null ) {
throw new PathElementException( throw new PathElementException(
String.format( String.format(

View File

@ -196,7 +196,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
if ( resolvedPath != null ) { if ( resolvedPath != null ) {
return resolvedPath; return resolvedPath;
} }
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }
@ -417,8 +417,8 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <X, Y> SqmAttributeJoin<X, Y> join(String attributeName, JoinType jt) { public <X, Y> SqmAttributeJoin<X, Y> join(String attributeName, JoinType jt) {
final SqmPathSource<?> subPathSource = final SqmPathSource<Y> subPathSource = (SqmPathSource<Y>) getReferencedPathSource()
getReferencedPathSource().getSubPathSource( attributeName, nodeBuilder().getJpaMetamodel() ); .getSubPathSource( attributeName );
return (SqmAttributeJoin<X, Y>) buildJoin( subPathSource, SqmJoinType.from( jt ), false ); return (SqmAttributeJoin<X, Y>) buildJoin( subPathSource, SqmJoinType.from( jt ), false );
} }
@ -430,8 +430,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <X, Y> SqmBagJoin<X, Y> joinCollection(String attributeName, JoinType jt) { public <X, Y> SqmBagJoin<X, Y> joinCollection(String attributeName, JoinType jt) {
final SqmPathSource<?> joinedPathSource = final SqmPathSource<?> joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName );
getReferencedPathSource().getSubPathSource( attributeName, nodeBuilder().getJpaMetamodel() );
if ( joinedPathSource instanceof BagPersistentAttribute ) { if ( joinedPathSource instanceof BagPersistentAttribute ) {
final SqmBagJoin<T, Y> join = buildBagJoin( final SqmBagJoin<T, Y> join = buildBagJoin(
@ -462,8 +461,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <X, Y> SqmSetJoin<X, Y> joinSet(String attributeName, JoinType jt) { public <X, Y> SqmSetJoin<X, Y> joinSet(String attributeName, JoinType jt) {
final SqmPathSource<?> joinedPathSource = final SqmPathSource<?> joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName );
getReferencedPathSource().getSubPathSource( attributeName, nodeBuilder().getJpaMetamodel() );
if ( joinedPathSource instanceof SetPersistentAttribute ) { if ( joinedPathSource instanceof SetPersistentAttribute ) {
final SqmSetJoin<T, Y> join = buildSetJoin( final SqmSetJoin<T, Y> join = buildSetJoin(
@ -494,8 +492,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <X, Y> SqmListJoin<X, Y> joinList(String attributeName, JoinType jt) { public <X, Y> SqmListJoin<X, Y> joinList(String attributeName, JoinType jt) {
final SqmPathSource<?> joinedPathSource = final SqmPathSource<?> joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName );
getReferencedPathSource().getSubPathSource( attributeName, nodeBuilder().getJpaMetamodel() );
if ( joinedPathSource instanceof ListPersistentAttribute ) { if ( joinedPathSource instanceof ListPersistentAttribute ) {
final SqmListJoin<T, Y> join = buildListJoin( final SqmListJoin<T, Y> join = buildListJoin(
@ -526,8 +523,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <X, K, V> SqmMapJoin<X, K, V> joinMap(String attributeName, JoinType jt) { public <X, K, V> SqmMapJoin<X, K, V> joinMap(String attributeName, JoinType jt) {
final SqmPathSource<?> joinedPathSource = final SqmPathSource<?> joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName );
getReferencedPathSource().getSubPathSource( attributeName, nodeBuilder().getJpaMetamodel() );
if ( joinedPathSource instanceof MapPersistentAttribute<?, ?, ?> ) { if ( joinedPathSource instanceof MapPersistentAttribute<?, ?, ?> ) {
final SqmMapJoin<T, K, V> join = buildMapJoin( final SqmMapJoin<T, K, V> join = buildMapJoin(

View File

@ -193,11 +193,18 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
@Override @Override
public <Y> SqmPath<Y> get(String attributeName) { public <Y> SqmPath<Y> get(String attributeName) {
//noinspection unchecked //noinspection unchecked
final SqmPathSource<Y> subNavigable = (SqmPathSource<Y>) final SqmPathSource<Y> subNavigable = (SqmPathSource<Y>) getResolvedModel().getSubPathSource( attributeName );
getResolvedModel().getSubPathSource( attributeName, nodeBuilder().getJpaMetamodel() );
return resolvePath( attributeName, subNavigable ); return resolvePath( attributeName, subNavigable );
} }
@Override
public <Y> SqmPath<Y> get(String attributeName, boolean includeSubtypes) {
//noinspection unchecked
final SqmPathSource<Y> subPathSource = (SqmPathSource<Y>)
getResolvedModel().getSubPathSource( attributeName, includeSubtypes );
return resolvePath( attributeName, subPathSource );
}
protected <X> SqmPath<X> resolvePath(PersistentAttribute<?, X> attribute) { protected <X> SqmPath<X> resolvePath(PersistentAttribute<?, X> attribute) {
//noinspection unchecked //noinspection unchecked
return resolvePath( attribute.getName(), (SqmPathSource<X>) attribute ); return resolvePath( attribute.getName(), (SqmPathSource<X>) attribute );

View File

@ -77,7 +77,7 @@ public class SqmAnyValuedSimplePath<T> extends AbstractSqmSimplePath<T> {
String name, String name,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -100,7 +100,7 @@ public class SqmElementAggregateFunction<T> extends AbstractSqmSpecificPluralPar
String name, String name,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -84,7 +84,7 @@ public class SqmEmbeddedValuedSimplePath<T>
String name, String name,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -51,7 +51,7 @@ public class SqmEntityValuedSimplePath<T> extends AbstractSqmSimplePath<T> {
String name, String name,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -83,7 +83,7 @@ public class SqmFkExpression<T> extends AbstractSqmPath<T> {
@Override @Override
public SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) { public SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -105,7 +105,7 @@ public class SqmFunctionPath<T> extends AbstractSqmPath<T> {
String name, String name,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -102,7 +102,7 @@ public class SqmIndexAggregateFunction<T> extends AbstractSqmSpecificPluralPartP
String name, String name,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -66,7 +66,7 @@ public class SqmIndexedCollectionAccessPath<T> extends AbstractSqmPath<T> implem
String name, String name,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -175,6 +175,16 @@ public interface SqmPath<T> extends SqmExpression<T>, SemanticPathPart, JpaPath<
@Override @Override
<Y> SqmPath<Y> get(String attributeName); <Y> SqmPath<Y> get(String attributeName);
/**
* Same as {@link #get(String)}, but if {@code includeSubtypes} is set to {@code true}
* and this path is polymorphic, also try finding subtype attributes.
*
* @see SqmPathSource#findSubPathSource(String, boolean)
*/
default <Y> SqmPath<Y> get(String attributeName, boolean includeSubtypes) {
return get( attributeName );
}
@Override @Override
SqmPath<T> copy(SqmCopyContext context); SqmPath<T> copy(SqmCopyContext context);

View File

@ -101,7 +101,7 @@ public class SqmPluralValuedSimplePath<E> extends AbstractSqmSimplePath<E> {
+ "' refers to a collection and so element attribute '" + name + "' refers to a collection and so element attribute '" + name
+ "' may not be referenced directly (use element() function)" ); + "' may not be referenced directly (use element() function)" );
} }
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -18,11 +18,9 @@ import org.hibernate.query.criteria.JpaSetJoin;
import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmFrom;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
/** /**
@ -157,11 +155,4 @@ public class SqmSetJoin<O, E>
} }
return treat; return treat;
} }
@Override
public <X, Y> SqmAttributeJoin<X, Y> fetch(String attributeName) {
return fetch( attributeName, JoinType.INNER);
}
} }

View File

@ -103,7 +103,7 @@ public class SqmTreatedEmbeddedValuedSimplePath<T, S extends T> extends SqmEmbed
@Override @Override
public SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) { public SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -110,7 +110,7 @@ public class SqmTreatedRoot extends SqmRoot implements SqmTreatedFrom {
String name, String name,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name ); final SqmPath<?> sqmPath = get( name, true );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath; return sqmPath;
} }

View File

@ -494,9 +494,9 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
} }
for ( SqmRoot<?> root : roots ) { for ( SqmRoot<?> root : roots ) {
validateFetchOwners( selectedFromSet, root, root ); validateFetchOwners( selectedFromSet, root );
for ( SqmFrom<?, ?> sqmTreat : root.getSqmTreats() ) { for ( SqmFrom<?, ?> sqmTreat : root.getSqmTreats() ) {
validateFetchOwners( selectedFromSet, root, sqmTreat ); validateFetchOwners( selectedFromSet, sqmTreat );
} }
} }
} }
@ -543,12 +543,12 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
} }
} }
private void validateFetchOwners(Set<SqmFrom<?, ?>> selectedFromSet, SqmFrom<?, ?> owner, SqmFrom<?, ?> joinContainer) { private void validateFetchOwners(Set<SqmFrom<?, ?>> selectedFromSet, SqmFrom<?, ?> joinContainer) {
for ( SqmJoin<?, ?> sqmJoin : joinContainer.getSqmJoins() ) { for ( SqmJoin<?, ?> sqmJoin : joinContainer.getSqmJoins() ) {
if ( sqmJoin instanceof SqmAttributeJoin<?, ?> ) { if ( sqmJoin instanceof SqmAttributeJoin<?, ?> ) {
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin; final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
if ( attributeJoin.isFetched() ) { if ( attributeJoin.isFetched() ) {
assertFetchOwner( selectedFromSet, owner, sqmJoin ); assertFetchOwner( selectedFromSet, attributeJoin.getLhs(), sqmJoin );
// Only need to check the first level // Only need to check the first level
continue; continue;
} }
@ -557,14 +557,14 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
if ( sqmTreat instanceof SqmAttributeJoin<?, ?> ) { if ( sqmTreat instanceof SqmAttributeJoin<?, ?> ) {
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmTreat; final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmTreat;
if ( attributeJoin.isFetched() ) { if ( attributeJoin.isFetched() ) {
assertFetchOwner( selectedFromSet, owner, attributeJoin ); assertFetchOwner( selectedFromSet, attributeJoin.getLhs(), attributeJoin );
// Only need to check the first level // Only need to check the first level
continue; continue;
} }
} }
validateFetchOwners( selectedFromSet, sqmJoin, sqmTreat ); validateFetchOwners( selectedFromSet, sqmTreat );
} }
validateFetchOwners( selectedFromSet, sqmJoin, sqmJoin ); validateFetchOwners( selectedFromSet, sqmJoin );
} }
} }

View File

@ -61,11 +61,6 @@ public class TreatedSubclassSameTypeTest {
executeQuery( scope, criteria -> criteria.from( MyEntity1.class ), true ); executeQuery( scope, criteria -> criteria.from( MyEntity1.class ), true );
} }
@Test
public void testJoin(SessionFactoryScope scope) {
executeQuery( scope, criteria -> criteria.from( MyEntity1.class ), false );
}
@Test @Test
public void testJoinOnTreatedSubtype(SessionFactoryScope scope) { public void testJoinOnTreatedSubtype(SessionFactoryScope scope) {
executeQuery( scope, criteria -> criteria.from( MySubEntity1.class ), true ); executeQuery( scope, criteria -> criteria.from( MySubEntity1.class ), true );

View File

@ -106,7 +106,7 @@ public class JoinedSubclassTest {
Root<Person> root = criteria.from( Person.class ); Root<Person> root = criteria.from( Person.class );
criteria.where( criteriaBuilder.gt( root.get( "salary" ), new BigDecimal( 100 ) ) ); criteria.where( criteriaBuilder.gt( criteriaBuilder.treat( root, Employee.class ).get( "salary" ), new BigDecimal( 100 ) ) );
result = s.createQuery( criteria ).list(); result = s.createQuery( criteria ).list();
// result = s.createCriteria( Person.class ) // result = s.createCriteria( Person.class )

View File

@ -99,12 +99,15 @@ public class CorrelatedPluralJoinInheritanceTest {
} }
@Test @Test
public void testImplicitTreat(EntityManagerFactoryScope scope) { public void testExplicitTreat(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> { scope.inTransaction( entityManager -> {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<ContinuousData> criteriaQuery = cb.createQuery( ContinuousData.class ); final CriteriaQuery<ContinuousData> criteriaQuery = cb.createQuery( ContinuousData.class );
final Root<ContinuousData> root = criteriaQuery.from( ContinuousData.class ); final Root<ContinuousData> root = criteriaQuery.from( ContinuousData.class );
criteriaQuery.where( createExistsPredicate( root, criteriaQuery, cb, "storedTags" ) ); final Subquery<String> subquery = criteriaQuery.subquery( String.class );
final Root<ContinuousData> root2 = subquery.correlate( root );
subquery.select( root2.get( "id" ) ).where( cb.treat( root2, StoredContinuousData.class ).join( "storedTags" ).get( "id" ).in( "a", "b" ) );
criteriaQuery.where( cb.exists( subquery ) );
final List<ContinuousData> resultList = entityManager.createQuery( criteriaQuery ).getResultList(); final List<ContinuousData> resultList = entityManager.createQuery( criteriaQuery ).getResultList();
assertThat( resultList ).hasSize( 1 ); assertThat( resultList ).hasSize( 1 );
} ); } );

View File

@ -177,7 +177,7 @@ public class DiscriminatorTest {
CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder();
CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( Person.class ); CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( Person.class );
Root<Person> root = criteria.from( Person.class ); Root<Person> root = criteria.from( Person.class );
criteria.where( criteriaBuilder.gt( root.get( "salary" ), new BigDecimal( 100 ) ) ); criteria.where( criteriaBuilder.gt( criteriaBuilder.treat( root, Employee.class ).get( "salary" ), new BigDecimal( 100 ) ) );
result = s.createQuery( criteria ).list(); result = s.createQuery( criteria ).list();
// result = s.createCriteria(Person.class) // result = s.createCriteria(Person.class)
// .add( Property.forName( "salary").gt( new BigDecimal( 100) ) ) // .add( Property.forName( "salary").gt( new BigDecimal( 100) ) )

View File

@ -182,7 +182,7 @@ public class SimpleInheritanceTest {
CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder();
CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( Person.class ); CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( Person.class );
Root<Person> root = criteria.from( Person.class ); Root<Person> root = criteria.from( Person.class );
criteria.where( criteriaBuilder.gt( root.get( "salary" ), new BigDecimal( 100 ) ) ); criteria.where( criteriaBuilder.gt( criteriaBuilder.treat( root, Employee.class ).get( "salary" ), new BigDecimal( 100 ) ) );
result = s.createQuery( criteria ).list(); result = s.createQuery( criteria ).list();
// result = s.createCriteria( Person.class ) // result = s.createCriteria( Person.class )

View File

@ -92,7 +92,7 @@ public class CriteriaInheritanceJoinTest {
final CriteriaBuilder cb = session.getCriteriaBuilder(); final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<Address> cq = cb.createQuery( Address.class ); final CriteriaQuery<Address> cq = cb.createQuery( Address.class );
final Root<Address> addressRoot = cq.from( Address.class ); final Root<Address> addressRoot = cq.from( Address.class );
final Join<Address, Street> join = addressRoot.join( "street" ); final Join<Address, Street> join = cb.treat( addressRoot, StreetAddress.class ).join( "street" );
cq.select( addressRoot ).where( cb.equal( join.get( "name" ), "Via Roma" ) ); cq.select( addressRoot ).where( cb.equal( join.get( "name" ), "Via Roma" ) );
final Address result = session.createQuery( cq ).getSingleResult(); final Address result = session.createQuery( cq ).getSingleResult();
assertThat( result ).isInstanceOf( StreetAddress.class ); assertThat( result ).isInstanceOf( StreetAddress.class );

View File

@ -160,7 +160,7 @@ public class UnionSubclassTest {
CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder();
CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( Person.class ); CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( Person.class );
Root<Person> root = criteria.from( Person.class ); Root<Person> root = criteria.from( Person.class );
criteria.where( criteriaBuilder.gt( root.get( "salary" ), new BigDecimal( 100 ) ) ); criteria.where( criteriaBuilder.gt( criteriaBuilder.treat( root, Employee.class ).get( "salary" ), new BigDecimal( 100 ) ) );
result = s.createQuery( criteria ).list(); result = s.createQuery( criteria ).list();

View File

@ -376,6 +376,13 @@ In Hibernate 7, the configuration property `hibernate.jdbc.batch_size` now has n
Automatic batching may be enabled by explicitly calling `setJdbcBatchSize()`. Automatic batching may be enabled by explicitly calling `setJdbcBatchSize()`.
However, the preferred approach is to explicitly batch operations via `insertMultiple()`, `updateMultiple()`, or `deleteMultiple()`. However, the preferred approach is to explicitly batch operations via `insertMultiple()`, `updateMultiple()`, or `deleteMultiple()`.
[[criteria-implicit-treat]]
== Criteria API and inheritance subtypes attributes
It was previously possible to use the string version of the `jakarta.persistence.criteria.Path#get` and `jakarta.persistence.criteria.From#join` methods with names of attributes defined in an inheritance subtype of the type represented by the path expression. This was handled internally by implicitly treating the path as the subtype which defines said attribute. Since Hibernate 7.0, aligning with the JPA specification, the Criteria API will no longer allow retrieving subtype attributes this way, and it's going to require an explicit `jakarta.persistence.criteria.CriteriaBuilder#treat` to be called on the path first to downcast it to the subtype which defines the attribute.
Implicit treats are still going to be applied when an HQL query dereferences a path belonging to an inheritance subtype.
[[hbm-transform]] [[hbm-transform]]
== hbm.xml Transformation == hbm.xml Transformation

View File

@ -62,7 +62,6 @@ import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType; import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
@ -973,14 +972,14 @@ public abstract class MockSessionFactory
} }
@Override @Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) { public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
switch (name) { switch (name) {
case EntityIdentifierMapping.ID_ROLE_NAME: case EntityIdentifierMapping.ID_ROLE_NAME:
return getIdentifierDescriptor(); return getIdentifierDescriptor();
case "{version}": case "{version}":
return findVersionAttribute(); return findVersionAttribute();
} }
final SqmPathSource<?> source = super.findSubPathSource(name, metamodel); final SqmPathSource<?> source = super.findSubPathSource(name, includeSubtypes);
if ( source != null ) { if ( source != null ) {
return source; return source;
} }