HHH-15875 Fix join fetch support for associations within embedded ids
This commit is contained in:
parent
2271e18ba5
commit
a71e26e333
|
@ -362,7 +362,6 @@ public class ToOneAttributeMapping
|
||||||
isInternalLoadNullable = isNullable();
|
isInternalLoadNullable = isNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( referencedPropertyName == null ) {
|
if ( referencedPropertyName == null ) {
|
||||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||||
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||||
|
@ -380,18 +379,12 @@ public class ToOneAttributeMapping
|
||||||
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
|
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
|
||||||
&& compositeType.getPropertyNames().length == 1 ) {
|
&& compositeType.getPropertyNames().length == 1 ) {
|
||||||
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
||||||
addPrefixedPropertyNames(
|
addPrefixedPropertyPaths(
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
targetKeyPropertyName,
|
targetKeyPropertyName,
|
||||||
compositeType.getSubtypes()[0],
|
compositeType.getSubtypes()[0],
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
ForeignKeyDescriptor.PART_NAME,
|
|
||||||
compositeType.getSubtypes()[0],
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
|
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
|
||||||
|
@ -401,52 +394,34 @@ public class ToOneAttributeMapping
|
||||||
propertyType,
|
propertyType,
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
addPrefixedPropertyNames(
|
addPrefixedPropertyPaths(
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
targetKeyPropertyName,
|
targetKeyPropertyName,
|
||||||
propertyType,
|
propertyType,
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
ForeignKeyDescriptor.PART_NAME,
|
|
||||||
propertyType,
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.targetKeyPropertyName = entityBinding.getIdentifierProperty().getName();
|
this.targetKeyPropertyName = entityBinding.getIdentifierProperty().getName();
|
||||||
addPrefixedPropertyNames(
|
addPrefixedPropertyPaths(
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
targetKeyPropertyName,
|
targetKeyPropertyName,
|
||||||
propertyType,
|
propertyType,
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
ForeignKeyDescriptor.PART_NAME,
|
|
||||||
propertyType,
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||||
}
|
}
|
||||||
else if ( bootValue.isReferenceToPrimaryKey() ) {
|
else if ( bootValue.isReferenceToPrimaryKey() ) {
|
||||||
this.targetKeyPropertyName = referencedPropertyName;
|
this.targetKeyPropertyName = referencedPropertyName;
|
||||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||||
addPrefixedPropertyNames(
|
addPrefixedPropertyPaths(
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
targetKeyPropertyName,
|
targetKeyPropertyName,
|
||||||
bootValue.getType(),
|
bootValue.getType(),
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
ForeignKeyDescriptor.PART_NAME,
|
|
||||||
bootValue.getType(),
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -458,18 +433,12 @@ public class ToOneAttributeMapping
|
||||||
&& compositeType.getPropertyNames().length == 1 ) {
|
&& compositeType.getPropertyNames().length == 1 ) {
|
||||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||||
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
||||||
addPrefixedPropertyNames(
|
addPrefixedPropertyPaths(
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
targetKeyPropertyName,
|
targetKeyPropertyName,
|
||||||
compositeType.getSubtypes()[0],
|
compositeType.getSubtypes()[0],
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
ForeignKeyDescriptor.PART_NAME,
|
|
||||||
compositeType.getSubtypes()[0],
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -480,18 +449,12 @@ public class ToOneAttributeMapping
|
||||||
if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) {
|
if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) {
|
||||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||||
targetKeyPropertyNames.add( targetKeyPropertyName );
|
targetKeyPropertyNames.add( targetKeyPropertyName );
|
||||||
addPrefixedPropertyNames(
|
addPrefixedPropertyPaths(
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
mapsIdAttributeName,
|
mapsIdAttributeName,
|
||||||
entityMappingType.getEntityPersister().getIdentifierType(),
|
entityMappingType.getEntityPersister().getIdentifierType(),
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
ForeignKeyDescriptor.PART_NAME,
|
|
||||||
entityMappingType.getEntityPersister().getIdentifierType(),
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -666,6 +629,31 @@ public class ToOneAttributeMapping
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addPrefixedPropertyPaths(
|
||||||
|
Set<String> targetKeyPropertyNames,
|
||||||
|
String prefix,
|
||||||
|
Type type,
|
||||||
|
SessionFactoryImplementor factory) {
|
||||||
|
addPrefixedPropertyNames(
|
||||||
|
targetKeyPropertyNames,
|
||||||
|
prefix,
|
||||||
|
type,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
addPrefixedPropertyNames(
|
||||||
|
targetKeyPropertyNames,
|
||||||
|
ForeignKeyDescriptor.PART_NAME,
|
||||||
|
type,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
addPrefixedPropertyNames(
|
||||||
|
targetKeyPropertyNames,
|
||||||
|
EntityIdentifierMapping.ROLE_LOCAL_NAME,
|
||||||
|
type,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static void addPrefixedPropertyNames(
|
public static void addPrefixedPropertyNames(
|
||||||
Set<String> targetKeyPropertyNames,
|
Set<String> targetKeyPropertyNames,
|
||||||
String prefix,
|
String prefix,
|
||||||
|
|
|
@ -13,20 +13,25 @@ import java.util.function.Supplier;
|
||||||
import org.hibernate.graph.spi.GraphHelper;
|
import org.hibernate.graph.spi.GraphHelper;
|
||||||
import org.hibernate.metamodel.AttributeClassification;
|
import org.hibernate.metamodel.AttributeClassification;
|
||||||
import org.hibernate.metamodel.internal.MetadataContext;
|
import org.hibernate.metamodel.internal.MetadataContext;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
|
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.ManagedDomainType;
|
import org.hibernate.metamodel.model.domain.ManagedDomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||||
import org.hibernate.metamodel.model.domain.SimpleDomainType;
|
import org.hibernate.metamodel.model.domain.SimpleDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
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;
|
||||||
import org.hibernate.query.sqm.internal.SqmMappingModelHelper;
|
import org.hibernate.query.sqm.internal.SqmMappingModelHelper;
|
||||||
|
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
||||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
|
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
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 org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||||
|
import org.hibernate.spi.NavigablePath;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,6 +167,20 @@ public class SingularAttributeImpl<D,J>
|
||||||
metadataContext
|
metadataContext
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigablePath createNavigablePath(SqmPath parent, String alias) {
|
||||||
|
if ( parent == null ) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"`lhs` cannot be null for a sub-navigable reference - " + parent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
NavigablePath navigablePath = parent.getNavigablePath();
|
||||||
|
if ( parent.getReferencedPathSource() instanceof PluralPersistentAttribute<?, ?, ?> ) {
|
||||||
|
navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() );
|
||||||
|
}
|
||||||
|
return new EntityIdentifierNavigablePath( navigablePath, SqmCreationHelper.determineAlias( alias ), getName() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,10 +7,12 @@
|
||||||
package org.hibernate.query.sqm;
|
package org.hibernate.query.sqm;
|
||||||
|
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
|
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
||||||
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.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||||
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialization for attributes that that can be used in creating SQM joins
|
* Specialization for attributes that that can be used in creating SQM joins
|
||||||
|
@ -29,4 +31,8 @@ public interface SqmJoinable<O, E> {
|
||||||
SqmCreationState creationState);
|
SqmCreationState creationState);
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
|
default NavigablePath createNavigablePath(SqmPath<?> parent, String alias) {
|
||||||
|
return SqmCreationHelper.buildSubNavigablePath( parent, getName(), alias );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class SqmCreationHelper {
|
||||||
return lhs.append( base, determineAlias( alias ) );
|
return lhs.append( base, determineAlias( alias ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String determineAlias(String alias) {
|
public static String determineAlias(String alias) {
|
||||||
// Make sure we always create a unique alias, otherwise we might use a wrong table group for the same join
|
// Make sure we always create a unique alias, otherwise we might use a wrong table group for the same join
|
||||||
if ( alias == null ) {
|
if ( alias == null ) {
|
||||||
return Long.toString( System.nanoTime() );
|
return Long.toString( System.nanoTime() );
|
||||||
|
|
|
@ -272,6 +272,7 @@ import org.hibernate.query.sqm.tree.select.SqmSubQuery;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmAssignment;
|
import org.hibernate.query.sqm.tree.update.SqmAssignment;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
|
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||||
import org.hibernate.spi.NavigablePath;
|
import org.hibernate.spi.NavigablePath;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
|
@ -382,6 +383,7 @@ import org.hibernate.sql.results.graph.Fetch;
|
||||||
import org.hibernate.sql.results.graph.FetchParent;
|
import org.hibernate.sql.results.graph.FetchParent;
|
||||||
import org.hibernate.sql.results.graph.Fetchable;
|
import org.hibernate.sql.results.graph.Fetchable;
|
||||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||||
|
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
|
||||||
import org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiation;
|
import org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiation;
|
||||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||||
|
@ -7162,7 +7164,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
// .getOrMakeJavaDescriptor( namedClass );
|
// .getOrMakeJavaDescriptor( namedClass );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFetch(ImmutableFetchList.Builder fetches, FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) {
|
@Override
|
||||||
|
public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
|
||||||
|
final EntityIdentifierMapping identifierMapping = fetchParent.getEntityValuedModelPart()
|
||||||
|
.getEntityMappingType()
|
||||||
|
.getIdentifierMapping();
|
||||||
|
final Fetchable fetchableIdentifierMapping = (Fetchable) identifierMapping;
|
||||||
|
return createFetch( fetchParent, fetchableIdentifierMapping, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) {
|
||||||
final NavigablePath resolvedNavigablePath = fetchParent.resolveNavigablePath( fetchable );
|
final NavigablePath resolvedNavigablePath = fetchParent.resolveNavigablePath( fetchable );
|
||||||
final Map.Entry<Integer, List<SqlSelection>> sqlSelectionsToTrack = trackedFetchSelectionsForGroup.get( resolvedNavigablePath );
|
final Map.Entry<Integer, List<SqlSelection>> sqlSelectionsToTrack = trackedFetchSelectionsForGroup.get( resolvedNavigablePath );
|
||||||
final int sqlSelectionStartIndexForFetch;
|
final int sqlSelectionStartIndexForFetch;
|
||||||
|
@ -7308,8 +7319,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( biDirectionalFetch != null ) {
|
if ( biDirectionalFetch != null ) {
|
||||||
fetches.add( biDirectionalFetch );
|
return biDirectionalFetch;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Fetch fetch = buildFetch(
|
final Fetch fetch = buildFetch(
|
||||||
|
@ -7344,8 +7354,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
currentBagRole = fetchable.getNavigableRole().getNavigableName();
|
currentBagRole = fetchable.getNavigableRole().getNavigableName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetches.add( fetch );
|
|
||||||
}
|
}
|
||||||
|
return fetch;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if ( incrementFetchDepth ) {
|
if ( incrementFetchDepth ) {
|
||||||
|
@ -7374,10 +7384,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
final int size = referencedMappingContainer.getNumberOfFetchables();
|
final int size = referencedMappingContainer.getNumberOfFetchables();
|
||||||
final ImmutableFetchList.Builder fetches = new ImmutableFetchList.Builder( referencedMappingContainer );
|
final ImmutableFetchList.Builder fetches = new ImmutableFetchList.Builder( referencedMappingContainer );
|
||||||
for ( int i = 0; i < keySize; i++ ) {
|
for ( int i = 0; i < keySize; i++ ) {
|
||||||
addFetch( fetches, fetchParent, referencedMappingContainer.getKeyFetchable( i ), true );
|
final Fetch fetch = createFetch( fetchParent, referencedMappingContainer.getKeyFetchable( i ), true );
|
||||||
|
if ( fetch != null ) {
|
||||||
|
fetches.add( fetch );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for ( int i = 0; i < size; i++ ) {
|
for ( int i = 0; i < size; i++ ) {
|
||||||
addFetch( fetches, fetchParent, referencedMappingContainer.getFetchable( i ), false );
|
final Fetch fetch = createFetch( fetchParent, referencedMappingContainer.getFetchable( i ), false );
|
||||||
|
if ( fetch != null ) {
|
||||||
|
fetches.add( fetch );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fetches.build();
|
return fetches.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ public abstract class AbstractSqmAttributeJoin<O,T>
|
||||||
NodeBuilder nodeBuilder) {
|
NodeBuilder nodeBuilder) {
|
||||||
this(
|
this(
|
||||||
lhs,
|
lhs,
|
||||||
SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ),
|
joinedNavigable.createNavigablePath( lhs, alias ),
|
||||||
joinedNavigable,
|
joinedNavigable,
|
||||||
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
|
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
|
||||||
joinType,
|
joinType,
|
||||||
|
|
|
@ -24,6 +24,11 @@ public class EntityIdentifierNavigablePath extends NavigablePath {
|
||||||
this.identifierAttributeName = identifierAttributeName;
|
this.identifierAttributeName = identifierAttributeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EntityIdentifierNavigablePath(NavigablePath parent, String alias, String identifierAttributeName) {
|
||||||
|
super( parent, EntityIdentifierMapping.ROLE_LOCAL_NAME, alias );
|
||||||
|
this.identifierAttributeName = identifierAttributeName;
|
||||||
|
}
|
||||||
|
|
||||||
public String getIdentifierAttributeName() {
|
public String getIdentifierAttributeName() {
|
||||||
return identifierAttributeName;
|
return identifierAttributeName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.hibernate.Incubating;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
|
@ -36,7 +37,7 @@ public interface FetchParent extends DomainResultGraphNode {
|
||||||
|
|
||||||
default NavigablePath resolveNavigablePath(Fetchable fetchable) {
|
default NavigablePath resolveNavigablePath(Fetchable fetchable) {
|
||||||
final String fetchableName = fetchable.getFetchableName();
|
final String fetchableName = fetchable.getFetchableName();
|
||||||
if ( NavigablePath.IDENTIFIER_MAPPER_PROPERTY.equals( fetchableName ) ) {
|
if ( NavigablePath.IDENTIFIER_MAPPER_PROPERTY.equals( fetchableName ) || fetchable instanceof EntityIdentifierMapping ) {
|
||||||
return new EntityIdentifierNavigablePath( getNavigablePath(), fetchableName );
|
return new EntityIdentifierNavigablePath( getNavigablePath(), fetchableName );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -53,8 +54,7 @@ public interface FetchParent extends DomainResultGraphNode {
|
||||||
else {
|
else {
|
||||||
fetchParentType = fetchableEntityType;
|
fetchParentType = fetchableEntityType;
|
||||||
}
|
}
|
||||||
if ( fetchParentType != fetchableEntityType ) {
|
if ( fetchParentType != null && !fetchParentType.isTypeOrSuperType( fetchableEntityType ) ) {
|
||||||
// todo (6.0): if the fetchParentType is a subtype of fetchableEntityType this shouldn't be necessary
|
|
||||||
return getNavigablePath().treatAs( fetchableEntityType.getEntityName() )
|
return getNavigablePath().treatAs( fetchableEntityType.getEntityName() )
|
||||||
.append( fetchableName );
|
.append( fetchableName );
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E
|
||||||
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
||||||
final NavigablePath navigablePath = tableGroupJoin.getNavigablePath();
|
final NavigablePath navigablePath = tableGroupJoin.getNavigablePath();
|
||||||
if ( tableGroupJoin.getJoinedGroup().isFetched()
|
if ( tableGroupJoin.getJoinedGroup().isFetched()
|
||||||
&& fetchable.getFetchableName().equals( navigablePath.getLocalName() )
|
&& fetchable.getNavigableRole().getLocalName().equals( navigablePath.getLocalName() )
|
||||||
&& tableGroupJoin.getJoinedGroup().getModelPart() == fetchable ) {
|
&& tableGroupJoin.getJoinedGroup().getModelPart() == fetchable ) {
|
||||||
return navigablePath;
|
return navigablePath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.graph.spi.GraphImplementor;
|
||||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||||
import org.hibernate.graph.spi.SubGraphImplementor;
|
import org.hibernate.graph.spi.SubGraphImplementor;
|
||||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||||
import org.hibernate.sql.results.graph.EntityGraphTraversalState;
|
import org.hibernate.sql.results.graph.EntityGraphTraversalState;
|
||||||
|
@ -46,6 +47,9 @@ public class StandardEntityGraphTraversalStateImpl implements EntityGraphTravers
|
||||||
@Override
|
@Override
|
||||||
public TraversalResult traverse(FetchParent fetchParent, Fetchable fetchable, boolean exploreKeySubgraph) {
|
public TraversalResult traverse(FetchParent fetchParent, Fetchable fetchable, boolean exploreKeySubgraph) {
|
||||||
assert !(fetchable instanceof CollectionPart);
|
assert !(fetchable instanceof CollectionPart);
|
||||||
|
if ( fetchable instanceof NonAggregatedIdentifierMapping ) {
|
||||||
|
return new TraversalResult( currentGraphContext, FetchTiming.IMMEDIATE, true );
|
||||||
|
}
|
||||||
|
|
||||||
final GraphImplementor previousContextRoot = currentGraphContext;
|
final GraphImplementor previousContextRoot = currentGraphContext;
|
||||||
AttributeNodeImplementor attributeNode = null;
|
AttributeNodeImplementor attributeNode = null;
|
||||||
|
|
|
@ -0,0 +1,271 @@
|
||||||
|
package org.hibernate.orm.test.jpa.cdi;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.Jpa;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Embeddable;
|
||||||
|
import jakarta.persistence.EmbeddedId;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
import jakarta.persistence.criteria.Fetch;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@Jpa(
|
||||||
|
annotatedClasses = {
|
||||||
|
FetchEmbeddedIdTest.User.class,
|
||||||
|
FetchEmbeddedIdTest.GroupType.class,
|
||||||
|
FetchEmbeddedIdTest.Group.class,
|
||||||
|
FetchEmbeddedIdTest.UserGroup.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@TestForIssue( jiraKey = "HHH-15875")
|
||||||
|
public class FetchEmbeddedIdTest {
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public void setUp(EntityManagerFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
entityManager -> {
|
||||||
|
User user = new User( 1l, "user name" );
|
||||||
|
|
||||||
|
GroupType groupType = new GroupType( 1l, "group type" );
|
||||||
|
Group group = new Group( 1l, "user group", groupType );
|
||||||
|
|
||||||
|
UserGroupId userGroupId = new UserGroupId( user, group );
|
||||||
|
|
||||||
|
UserGroup userGroup = new UserGroup( userGroupId, "value" );
|
||||||
|
|
||||||
|
entityManager.persist( groupType );
|
||||||
|
entityManager.persist( group );
|
||||||
|
entityManager.persist( user );
|
||||||
|
entityManager.persist( userGroup );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCriteriaFetch(EntityManagerFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
entityManager -> {
|
||||||
|
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<UserGroup> query = criteriaBuilder.createQuery( UserGroup.class );
|
||||||
|
|
||||||
|
Root<UserGroup> root = query.from( UserGroup.class );
|
||||||
|
|
||||||
|
Fetch<?, ?> userGroupFetch = root.fetch( "userGroupId" );
|
||||||
|
userGroupFetch.fetch( "user" );
|
||||||
|
userGroupFetch.fetch( "group" ).fetch( "groupType" );
|
||||||
|
|
||||||
|
List<UserGroup> results = entityManager.createQuery( query ).getResultList();
|
||||||
|
assertThat( results ).hasSize( 1 );
|
||||||
|
|
||||||
|
UserGroup userGroup = results.get( 0 );
|
||||||
|
UserGroupId userGroupId = userGroup.getUserGroupId();
|
||||||
|
Group group = userGroupId.getGroup();
|
||||||
|
assertTrue( Hibernate.isInitialized( group ) );
|
||||||
|
String name = group.getName();
|
||||||
|
assertThat( name ).isEqualTo( "user group" );
|
||||||
|
|
||||||
|
User user = userGroupId.getUser();
|
||||||
|
assertTrue( Hibernate.isInitialized( user ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHqlFetch(EntityManagerFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
entityManager -> {
|
||||||
|
|
||||||
|
List<UserGroup> results = entityManager.createQuery( "select ug from UserGroup ug join fetch ug.userGroupId ugi join fetch ugi.group join fetch ugi.user" ).getResultList();
|
||||||
|
assertThat( results ).hasSize( 1 );
|
||||||
|
|
||||||
|
UserGroup userGroup = results.get( 0 );
|
||||||
|
UserGroupId userGroupId = userGroup.getUserGroupId();
|
||||||
|
Group group = userGroupId.getGroup();
|
||||||
|
assertTrue( Hibernate.isInitialized( group ) );
|
||||||
|
String name = group.getName();
|
||||||
|
assertThat( name ).isEqualTo( "user group" );
|
||||||
|
|
||||||
|
User user = userGroupId.getUser();
|
||||||
|
assertTrue( Hibernate.isInitialized( user ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "UserGroup")
|
||||||
|
public static class UserGroup {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
private UserGroupId userGroupId;
|
||||||
|
|
||||||
|
private String joinedPropertyValue;
|
||||||
|
|
||||||
|
public UserGroup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserGroup(UserGroupId userGroupId, String joinedPropertyValue) {
|
||||||
|
this.userGroupId = userGroupId;
|
||||||
|
this.joinedPropertyValue = joinedPropertyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserGroupId getUserGroupId() {
|
||||||
|
return userGroupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJoinedPropertyValue() {
|
||||||
|
return joinedPropertyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public static class UserGroupId implements Serializable {
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
|
private Group group;
|
||||||
|
|
||||||
|
public UserGroupId() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserGroupId(User user, Group group) {
|
||||||
|
this.user = user;
|
||||||
|
this.group = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Group getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if ( this == object ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( !( object instanceof UserGroupId ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserGroupId that = (UserGroupId) object;
|
||||||
|
|
||||||
|
return Objects.equals( user.getId(), that.user.getId() ) && Objects.equals(
|
||||||
|
group.getId(),
|
||||||
|
that.group.getId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash( user.getId(), group.getId() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "User")
|
||||||
|
@Table(name = "test_user")
|
||||||
|
public static class User {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(Long id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "GROUP")
|
||||||
|
@Table(name = "test_group")
|
||||||
|
public static class Group {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
|
private GroupType groupType;
|
||||||
|
|
||||||
|
public Group() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Group(Long id, String name, GroupType groupType) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.groupType = groupType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupType getGroupType() {
|
||||||
|
return groupType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "GroupType")
|
||||||
|
public static class GroupType {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public GroupType() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupType(Long id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue