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

View File

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

View File

@ -139,14 +139,13 @@ public class EntityTypeImpl<J>
@Override
public SqmPathSource<?> findSubPathSource(String name) {
final PersistentAttribute<? super J,?> attribute = findAttribute( name );
final PersistentAttribute<? super J,?> attribute = super.findAttribute( name );
if ( attribute != null ) {
return (SqmPathSource<?>) attribute;
}
else if ( EntityIdentifierMapping.matchesRoleName( name ) ) {
return hasSingleIdAttribute() ? findIdAttribute() : getIdentifierDescriptor();
}
else if ( EntityDiscriminatorMapping.matchesRoleName( name ) ) {
return discriminatorPathSource;
}
@ -156,18 +155,19 @@ public class EntityTypeImpl<J>
}
@Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) {
public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
final PersistentAttribute<? super J,?> attribute = super.findAttribute( name );
if ( attribute != null ) {
return (SqmPathSource<?>) attribute;
}
else {
//TODO: eliminate this cast!
final PersistentAttribute<?, ?> subtypeAttribute = findSubtypeAttribute( name );
if ( subtypeAttribute != null ) {
return (SqmPathSource<?>) subtypeAttribute;
if ( includeSubtypes ) {
final PersistentAttribute<?, ?> subtypeAttribute = findSubtypeAttribute( name );
if ( subtypeAttribute != null ) {
return (SqmPathSource<?>) subtypeAttribute;
}
}
else if ( EntityIdentifierMapping.matchesRoleName( name ) ) {
if ( EntityIdentifierMapping.matchesRoleName( name ) ) {
return hasSingleIdAttribute() ? findIdAttribute() : getIdentifierDescriptor();
}
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.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.hql.spi.SqmCreationState;
@ -60,7 +59,7 @@ public class ListAttributeImpl<X, E> extends AbstractPluralAttribute<X, List<E>,
}
@Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) {
public SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( name );
if ( nature != null ) {
switch ( nature ) {
@ -70,7 +69,7 @@ public class ListAttributeImpl<X, E> extends AbstractPluralAttribute<X, List<E>,
return getElementPathSource();
}
}
return getElementPathSource().findSubPathSource( name, metamodel );
return getElementPathSource().findSubPathSource( name, includeSubtypes );
}
@Override

View File

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

View File

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

View File

@ -188,10 +188,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
boolean isTerminal,
boolean allowReuse,
SqmCreationState creationState) {
final SqmPathSource<?> subPathSource = lhs.getResolvedModel().getSubPathSource(
name,
creationState.getCreationContext().getJpaMetamodel()
);
final SqmPathSource<?> subPathSource = lhs.getResolvedModel().getSubPathSource( name, true );
if ( allowReuse ) {
if ( !isTerminal ) {
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()
+ "' 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<?> ) {
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) {
return !( containerType.getSqmType() instanceof BasicDomainType )
&& containerType.findSubPathSource( name, getJpaMetamodel() ) != null;
&& containerType.findSubPathSource( name, true ) != null;
}
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.
*
* @param name the name of the path source to find
* @return null if the subPathSource is not found
*
* @throws IllegalStateException to indicate that this source cannot be de-referenced
*/
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.
*
* @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
*/
default SqmPathSource<?> findSubPathSource(String name, JpaMetamodel metamodel) {
default SqmPathSource<?> findSubPathSource(String name, boolean includeSubtypes) {
return findSubPathSource( name );
}
/**
* 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 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 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) {
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 ) {
throw new PathElementException(
String.format(

View File

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

View File

@ -193,11 +193,18 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
@Override
public <Y> SqmPath<Y> get(String attributeName) {
//noinspection unchecked
final SqmPathSource<Y> subNavigable = (SqmPathSource<Y>)
getResolvedModel().getSubPathSource( attributeName, nodeBuilder().getJpaMetamodel() );
final SqmPathSource<Y> subNavigable = (SqmPathSource<Y>) getResolvedModel().getSubPathSource( attributeName );
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) {
//noinspection unchecked
return resolvePath( attribute.getName(), (SqmPathSource<X>) attribute );

View File

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

View File

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

View File

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

View File

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

View File

@ -83,7 +83,7 @@ public class SqmFkExpression<T> extends AbstractSqmPath<T> {
@Override
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 );
return sqmPath;
}

View File

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

View File

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

View File

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

View File

@ -175,6 +175,16 @@ public interface SqmPath<T> extends SqmExpression<T>, SemanticPathPart, JpaPath<
@Override
<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
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
+ "' 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 );
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.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
/**
@ -157,11 +155,4 @@ public class SqmSetJoin<O, E>
}
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
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 );
return sqmPath;
}

View File

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

View File

@ -494,9 +494,9 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
}
for ( SqmRoot<?> root : roots ) {
validateFetchOwners( selectedFromSet, root, root );
validateFetchOwners( selectedFromSet, root );
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() ) {
if ( sqmJoin instanceof SqmAttributeJoin<?, ?> ) {
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
if ( attributeJoin.isFetched() ) {
assertFetchOwner( selectedFromSet, owner, sqmJoin );
assertFetchOwner( selectedFromSet, attributeJoin.getLhs(), sqmJoin );
// Only need to check the first level
continue;
}
@ -557,14 +557,14 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
if ( sqmTreat instanceof SqmAttributeJoin<?, ?> ) {
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmTreat;
if ( attributeJoin.isFetched() ) {
assertFetchOwner( selectedFromSet, owner, attributeJoin );
assertFetchOwner( selectedFromSet, attributeJoin.getLhs(), attributeJoin );
// Only need to check the first level
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 );
}
@Test
public void testJoin(SessionFactoryScope scope) {
executeQuery( scope, criteria -> criteria.from( MyEntity1.class ), false );
}
@Test
public void testJoinOnTreatedSubtype(SessionFactoryScope scope) {
executeQuery( scope, criteria -> criteria.from( MySubEntity1.class ), true );

View File

@ -106,7 +106,7 @@ public class JoinedSubclassTest {
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.createCriteria( Person.class )

View File

@ -99,12 +99,15 @@ public class CorrelatedPluralJoinInheritanceTest {
}
@Test
public void testImplicitTreat(EntityManagerFactoryScope scope) {
public void testExplicitTreat(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<ContinuousData> criteriaQuery = cb.createQuery( 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();
assertThat( resultList ).hasSize( 1 );
} );

View File

@ -177,7 +177,7 @@ public class DiscriminatorTest {
CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder();
CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( 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.createCriteria(Person.class)
// .add( Property.forName( "salary").gt( new BigDecimal( 100) ) )

View File

@ -182,7 +182,7 @@ public class SimpleInheritanceTest {
CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder();
CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( 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.createCriteria( Person.class )

View File

@ -92,7 +92,7 @@ public class CriteriaInheritanceJoinTest {
final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<Address> cq = cb.createQuery( 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" ) );
final Address result = session.createQuery( cq ).getSingleResult();
assertThat( result ).isInstanceOf( StreetAddress.class );

View File

@ -160,7 +160,7 @@ public class UnionSubclassTest {
CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder();
CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( 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();

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