Cleanup support for virtual embeddable model parts and reuse existing compatible joins for implicit joins
This commit is contained in:
parent
d5d350e5e7
commit
39484b160d
|
@ -39,10 +39,10 @@ import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
|||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.Restrictable;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
|
@ -340,7 +340,7 @@ public class LoaderSelectBuilder {
|
|||
}
|
||||
|
||||
for ( ModelPart restrictedPart : restrictedParts ) {
|
||||
if ( restrictedPart instanceof ForeignKeyDescriptor || restrictedPart instanceof NonAggregatedIdentifierMappingImpl ) {
|
||||
if ( restrictedPart instanceof ForeignKeyDescriptor || restrictedPart instanceof NonAggregatedIdentifierMapping ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.metamodel.mapping;
|
||||
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||
|
||||
|
@ -26,4 +27,9 @@ public interface DiscriminatedAssociationModelPart extends Fetchable, FetchableC
|
|||
|
||||
EntityMappingType resolveDiscriminatorValue(Object discriminatorValue);
|
||||
Object resolveDiscriminatorForEntityType(EntityMappingType entityMappingType);
|
||||
|
||||
@Override
|
||||
default boolean isSimpleJoinPredicate(Predicate predicate) {
|
||||
return predicate == null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,6 +116,8 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValueMapping {
|
|||
SqlExpressionResolver sqlExpressionResolver,
|
||||
SqlAstCreationContext creationContext);
|
||||
|
||||
boolean isSimpleJoinPredicate(Predicate predicate);
|
||||
|
||||
@Override
|
||||
default String getPartName() {
|
||||
return PART_NAME;
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
|
|||
* @see jakarta.persistence.IdClass
|
||||
* @see jakarta.persistence.MapsId
|
||||
*/
|
||||
public interface NonAggregatedIdentifierMapping extends CompositeIdentifierMapping, EmbeddableValuedFetchable, FetchOptions {
|
||||
public interface NonAggregatedIdentifierMapping extends CompositeIdentifierMapping, EmbeddableValuedFetchable, FetchOptions, VirtualModelPart {
|
||||
/**
|
||||
* The virtual-id representation of this id mapping
|
||||
*/
|
||||
|
|
|
@ -89,7 +89,7 @@ public class EmbeddedAttributeMapping
|
|||
stateArrayPosition,
|
||||
tableExpression,
|
||||
attributeMetadataAccess,
|
||||
getPropertyAccess(parentInjectionAttributeName, embeddableMappingType),
|
||||
getPropertyAccess( parentInjectionAttributeName, embeddableMappingType ),
|
||||
mappedFetchTiming,
|
||||
mappedFetchStyle,
|
||||
embeddableMappingType,
|
||||
|
@ -132,7 +132,7 @@ public class EmbeddedAttributeMapping
|
|||
}
|
||||
|
||||
// Constructor is only used for creating the inverse attribute mapping
|
||||
private EmbeddedAttributeMapping(
|
||||
EmbeddedAttributeMapping(
|
||||
ManagedMappingType keyDeclaringType,
|
||||
TableGroupProducer declaringTableGroupProducer,
|
||||
SelectableMappings selectableMappings,
|
||||
|
@ -161,29 +161,6 @@ public class EmbeddedAttributeMapping
|
|||
this.parentInjectionAttributePropertyAccess = null;
|
||||
}
|
||||
|
||||
public static EmbeddableValuedModelPart createInverseModelPart(
|
||||
EmbeddableValuedModelPart modelPart,
|
||||
ManagedMappingType keyDeclaringType,
|
||||
TableGroupProducer declaringTableGroupProducer,
|
||||
SelectableMappings selectableMappings,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
final EmbeddableMappingType embeddableTypeDescriptor;
|
||||
if ( modelPart instanceof CompositeIdentifierMapping ) {
|
||||
embeddableTypeDescriptor = ( (CompositeIdentifierMapping) modelPart ).getMappedIdEmbeddableTypeDescriptor();
|
||||
}
|
||||
else {
|
||||
embeddableTypeDescriptor = modelPart.getEmbeddableTypeDescriptor();
|
||||
}
|
||||
return new EmbeddedAttributeMapping(
|
||||
keyDeclaringType,
|
||||
declaringTableGroupProducer,
|
||||
selectableMappings,
|
||||
modelPart,
|
||||
embeddableTypeDescriptor,
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getMappedType() {
|
||||
return getEmbeddableTypeDescriptor();
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.hibernate.sql.ast.SqlAstJoinType;
|
|||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
|
||||
|
@ -115,7 +116,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
this.targetSide = original.targetSide;
|
||||
this.keySide = new EmbeddedForeignKeyDescriptorSide(
|
||||
Nature.KEY,
|
||||
EmbeddedAttributeMapping.createInverseModelPart(
|
||||
MappingModelCreationHelper.createInverseModelPart(
|
||||
original.targetSide.getModelPart(),
|
||||
keyDeclaringType,
|
||||
keyDeclaringTableGroupProducer,
|
||||
|
@ -377,6 +378,84 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
return getPredicate( targetSideReference, keySideReference, creationContext, targetSelectableMappings, keySelectableMappings );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimpleJoinPredicate(Predicate predicate) {
|
||||
if ( !( predicate instanceof Junction ) ) {
|
||||
return false;
|
||||
}
|
||||
final Junction junction = (Junction) predicate;
|
||||
if ( junction.getNature() != Junction.Nature.CONJUNCTION ) {
|
||||
return false;
|
||||
}
|
||||
final List<Predicate> predicates = junction.getPredicates();
|
||||
if ( predicates.size() != keySelectableMappings.getJdbcTypeCount() ) {
|
||||
return false;
|
||||
}
|
||||
Boolean lhsIsKey = null;
|
||||
for ( int i = 0; i < predicates.size(); i++ ) {
|
||||
final Predicate p = predicates.get( i );
|
||||
if ( !( p instanceof ComparisonPredicate ) ) {
|
||||
return false;
|
||||
}
|
||||
final ComparisonPredicate comparisonPredicate = (ComparisonPredicate) p;
|
||||
if ( comparisonPredicate.getOperator() != ComparisonOperator.EQUAL ) {
|
||||
return false;
|
||||
}
|
||||
final Expression lhsExpr = comparisonPredicate.getLeftHandExpression();
|
||||
final Expression rhsExpr = comparisonPredicate.getRightHandExpression();
|
||||
if ( !( lhsExpr instanceof ColumnReference ) || !( rhsExpr instanceof ColumnReference ) ) {
|
||||
return false;
|
||||
}
|
||||
final ColumnReference lhs = (ColumnReference) lhsExpr;
|
||||
final ColumnReference rhs = (ColumnReference) rhsExpr;
|
||||
if ( lhsIsKey == null ) {
|
||||
final String keyExpression = keySelectableMappings.getSelectable( i ).getSelectionExpression();
|
||||
final String targetExpression = targetSelectableMappings.getSelectable( i ).getSelectionExpression();
|
||||
if ( keyExpression.equals( targetExpression ) ) {
|
||||
if ( !lhs.getColumnExpression().equals( keyExpression )
|
||||
|| !rhs.getColumnExpression().equals( keyExpression ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( keyExpression.equals( lhs.getColumnExpression() ) ) {
|
||||
if ( !targetExpression.equals( rhs.getColumnExpression() ) ) {
|
||||
return false;
|
||||
}
|
||||
lhsIsKey = true;
|
||||
}
|
||||
else if ( keyExpression.equals( rhs.getColumnExpression() ) ) {
|
||||
if ( !targetExpression.equals( lhs.getColumnExpression() ) ) {
|
||||
return false;
|
||||
}
|
||||
lhsIsKey = false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
final String lhsSelectionExpression;
|
||||
final String rhsSelectionExpression;
|
||||
if ( lhsIsKey ) {
|
||||
lhsSelectionExpression = keySelectableMappings.getSelectable( i ).getSelectionExpression();
|
||||
rhsSelectionExpression = targetSelectableMappings.getSelectable( i ).getSelectionExpression();
|
||||
}
|
||||
else {
|
||||
lhsSelectionExpression = targetSelectableMappings.getSelectable( i ).getSelectionExpression();
|
||||
rhsSelectionExpression = keySelectableMappings.getSelectable( i ).getSelectionExpression();
|
||||
}
|
||||
if ( !lhs.getColumnExpression().equals( lhsSelectionExpression )
|
||||
|| !rhs.getColumnExpression().equals( rhsSelectionExpression ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Predicate getPredicate(
|
||||
TableReference lhs,
|
||||
TableReference rhs,
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.hibernate.metamodel.mapping.ModelPart;
|
|||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -168,7 +169,14 @@ public class EntityCollectionPart
|
|||
final CompositeType compositeType;
|
||||
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
|
||||
&& compositeType.getPropertyNames().length == 1 ) {
|
||||
this.targetKeyPropertyNames = Collections.singleton( compositeType.getPropertyNames()[0] );
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
ToOneAttributeMapping.addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
compositeType.getPropertyNames()[0],
|
||||
compositeType.getSubtypes()[0],
|
||||
creationProcess.getCreationContext().getSessionFactory()
|
||||
);
|
||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else {
|
||||
final String mapsIdAttributeName;
|
||||
|
@ -327,6 +335,11 @@ public class EntityCollectionPart
|
|||
return SqlAstJoinType.INNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimpleJoinPredicate(Predicate predicate) {
|
||||
return fkDescriptor.isSimpleJoinPredicate( predicate );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Nature getNature() {
|
||||
return nature;
|
||||
|
@ -391,13 +404,14 @@ public class EntityCollectionPart
|
|||
// This is not possible for one-to-many associations because we need to create the target table group eagerly,
|
||||
// to preserve the cardinality. Also, the OneToManyTableGroup has no reference to the parent table group
|
||||
if ( !collectionDescriptor.isOneToMany() && targetKeyPropertyNames.contains( name ) ) {
|
||||
if ( fkDescriptor.getTargetPart() instanceof NonAggregatedIdentifierMappingImpl ) {
|
||||
return ( (ModelPartContainer) fkDescriptor.getKeyPart() ).findSubPart( name, targetType );
|
||||
}
|
||||
if ( fkTargetModelPart instanceof ToOneAttributeMapping ) {
|
||||
return fkTargetModelPart;
|
||||
}
|
||||
return fkDescriptor.getKeyPart();
|
||||
final ModelPart keyPart = fkDescriptor.getKeyPart();
|
||||
if ( keyPart instanceof EmbeddableValuedModelPart && keyPart instanceof VirtualModelPart ) {
|
||||
return ( (ModelPartContainer) keyPart ).findSubPart( name, targetType );
|
||||
}
|
||||
return keyPart;
|
||||
}
|
||||
return EntityValuedFetchable.super.findSubPart( name, targetType );
|
||||
}
|
||||
|
|
|
@ -61,7 +61,6 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
|||
import org.hibernate.metamodel.mapping.GeneratedValueResolver;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.MappingModelCreationLogger;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.metamodel.mapping.NonTransientException;
|
||||
|
@ -71,6 +70,7 @@ import org.hibernate.metamodel.mapping.SelectableMapping;
|
|||
import org.hibernate.metamodel.mapping.SelectableMappings;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
|
@ -341,20 +341,40 @@ public class MappingModelCreationHelper {
|
|||
attrType,
|
||||
tableExpression,
|
||||
rootTableKeyColumnNames,
|
||||
attributeMappingType -> new EmbeddedAttributeMapping(
|
||||
attrName,
|
||||
declaringType.getNavigableRole().append( attrName ),
|
||||
stateArrayPosition,
|
||||
tableExpression,
|
||||
attributeMetadataAccess,
|
||||
component.getParentProperty(),
|
||||
FetchTiming.IMMEDIATE,
|
||||
FetchStyle.JOIN,
|
||||
attributeMappingType,
|
||||
declaringType,
|
||||
propertyAccess,
|
||||
bootProperty.getValueGenerationStrategy()
|
||||
),
|
||||
attributeMappingType -> {
|
||||
if ( component.isEmbedded() ) {
|
||||
return new VirtualEmbeddedAttributeMapping(
|
||||
attrName,
|
||||
declaringType.getNavigableRole().append( attrName ),
|
||||
stateArrayPosition,
|
||||
tableExpression,
|
||||
attributeMetadataAccess,
|
||||
component.getParentProperty(),
|
||||
FetchTiming.IMMEDIATE,
|
||||
FetchStyle.JOIN,
|
||||
attributeMappingType,
|
||||
declaringType,
|
||||
propertyAccess,
|
||||
bootProperty.getValueGenerationStrategy()
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new EmbeddedAttributeMapping(
|
||||
attrName,
|
||||
declaringType.getNavigableRole().append( attrName ),
|
||||
stateArrayPosition,
|
||||
tableExpression,
|
||||
attributeMetadataAccess,
|
||||
component.getParentProperty(),
|
||||
FetchTiming.IMMEDIATE,
|
||||
FetchStyle.JOIN,
|
||||
attributeMappingType,
|
||||
declaringType,
|
||||
propertyAccess,
|
||||
bootProperty.getValueGenerationStrategy()
|
||||
);
|
||||
}
|
||||
},
|
||||
creationProcess
|
||||
);
|
||||
|
||||
|
@ -1172,7 +1192,7 @@ public class MappingModelCreationHelper {
|
|||
if ( inverse ) {
|
||||
return new EmbeddedForeignKeyDescriptor(
|
||||
embeddableValuedModelPart,
|
||||
EmbeddedAttributeMapping.createInverseModelPart(
|
||||
createInverseModelPart(
|
||||
embeddableValuedModelPart,
|
||||
keyDeclaringType,
|
||||
keyDeclaringTableGroupProducer,
|
||||
|
@ -1189,7 +1209,7 @@ public class MappingModelCreationHelper {
|
|||
}
|
||||
else {
|
||||
return new EmbeddedForeignKeyDescriptor(
|
||||
EmbeddedAttributeMapping.createInverseModelPart(
|
||||
createInverseModelPart(
|
||||
embeddableValuedModelPart,
|
||||
keyDeclaringType,
|
||||
keyDeclaringTableGroupProducer,
|
||||
|
@ -1494,6 +1514,41 @@ public class MappingModelCreationHelper {
|
|||
);
|
||||
}
|
||||
|
||||
public static EmbeddableValuedModelPart createInverseModelPart(
|
||||
EmbeddableValuedModelPart modelPart,
|
||||
ManagedMappingType keyDeclaringType,
|
||||
TableGroupProducer declaringTableGroupProducer,
|
||||
SelectableMappings selectableMappings,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
final EmbeddableMappingType embeddableTypeDescriptor;
|
||||
if ( modelPart instanceof CompositeIdentifierMapping ) {
|
||||
embeddableTypeDescriptor = ( (CompositeIdentifierMapping) modelPart ).getMappedIdEmbeddableTypeDescriptor();
|
||||
}
|
||||
else {
|
||||
embeddableTypeDescriptor = modelPart.getEmbeddableTypeDescriptor();
|
||||
}
|
||||
if ( modelPart instanceof VirtualModelPart ) {
|
||||
return new VirtualEmbeddedAttributeMapping(
|
||||
keyDeclaringType,
|
||||
declaringTableGroupProducer,
|
||||
selectableMappings,
|
||||
modelPart,
|
||||
embeddableTypeDescriptor,
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new EmbeddedAttributeMapping(
|
||||
keyDeclaringType,
|
||||
declaringTableGroupProducer,
|
||||
selectableMappings,
|
||||
modelPart,
|
||||
embeddableTypeDescriptor,
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static class CollectionMappingTypeImpl implements CollectionMappingType {
|
||||
private final JavaType collectionJtd;
|
||||
|
|
|
@ -517,6 +517,11 @@ public class PluralAttributeMappingImpl
|
|||
return SqlAstJoinType.LEFT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimpleJoinPredicate(Predicate predicate) {
|
||||
return fkDescriptor.isSimpleJoinPredicate( predicate );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroupJoin createTableGroupJoin(
|
||||
NavigablePath navigablePath,
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
|||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
|
@ -289,6 +290,28 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimpleJoinPredicate(Predicate predicate) {
|
||||
if ( !( predicate instanceof ComparisonPredicate ) ) {
|
||||
return false;
|
||||
}
|
||||
final ComparisonPredicate comparisonPredicate = (ComparisonPredicate) predicate;
|
||||
if ( comparisonPredicate.getOperator() != ComparisonOperator.EQUAL ) {
|
||||
return false;
|
||||
}
|
||||
final Expression lhsExpr = comparisonPredicate.getLeftHandExpression();
|
||||
final Expression rhsExpr = comparisonPredicate.getRightHandExpression();
|
||||
if ( !( lhsExpr instanceof ColumnReference ) || !( rhsExpr instanceof ColumnReference ) ) {
|
||||
return false;
|
||||
}
|
||||
final String lhs = ( (ColumnReference) lhsExpr ).getColumnExpression();
|
||||
final String rhs = ( (ColumnReference) rhsExpr ).getColumnExpression();
|
||||
final String keyExpression = keySide.getModelPart().getSelectionExpression();
|
||||
final String targetExpression = targetSide.getModelPart().getSelectionExpression();
|
||||
return ( lhs.equals( keyExpression ) && rhs.equals( targetExpression ) )
|
||||
|| ( lhs.equals( targetExpression ) && rhs.equals( keyExpression ) );
|
||||
}
|
||||
|
||||
protected TableReference getTableReference(TableGroup lhs, TableGroup tableGroup, String table) {
|
||||
final NavigablePath navigablePath = lhs.getNavigablePath().append( getTargetPart().getFetchableName() );
|
||||
if ( lhs.getPrimaryTableReference().getTableReference( navigablePath, table ) != null ) {
|
||||
|
|
|
@ -15,7 +15,6 @@ import java.util.function.BiConsumer;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -30,7 +29,6 @@ import org.hibernate.mapping.OneToOne;
|
|||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||
|
@ -47,6 +45,7 @@ import org.hibernate.metamodel.mapping.ModelPartContainer;
|
|||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||
|
@ -96,6 +95,7 @@ import org.hibernate.tuple.IdentifierProperty;
|
|||
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.CompositeType;
|
||||
import org.hibernate.type.EmbeddedComponentType;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
|
@ -440,8 +440,15 @@ public class ToOneAttributeMapping
|
|||
final CompositeType compositeType;
|
||||
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
|
||||
&& compositeType.getPropertyNames().length == 1 ) {
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
||||
this.targetKeyPropertyNames = Collections.singleton( targetKeyPropertyName );
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
targetKeyPropertyName,
|
||||
compositeType.getSubtypes()[0],
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else {
|
||||
this.targetKeyPropertyName = referencedPropertyName;
|
||||
|
@ -551,6 +558,9 @@ public class ToOneAttributeMapping
|
|||
if ( entityType.isReferenceToPrimaryKey() ) {
|
||||
propertyName = entityType.getAssociatedEntityPersister( factory ).getIdentifierPropertyName();
|
||||
}
|
||||
else if ( identifierOrUniqueKeyType instanceof EmbeddedComponentType ) {
|
||||
propertyName = null;
|
||||
}
|
||||
else {
|
||||
propertyName = entityType.getRHSUniqueKeyPropertyName();
|
||||
}
|
||||
|
@ -651,20 +661,17 @@ public class ToOneAttributeMapping
|
|||
// Prefer resolving the key part of the foreign key rather than the target part if possible
|
||||
// This way, we don't have to register table groups the target entity type
|
||||
if ( canUseParentTableGroup && targetKeyPropertyNames.contains( name ) ) {
|
||||
final ModelPart fkSideModelPart;
|
||||
final ModelPart fkTargetModelPart;
|
||||
final ModelPart fkPart;
|
||||
if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) {
|
||||
fkTargetModelPart = foreignKeyDescriptor.getTargetPart();
|
||||
fkSideModelPart = foreignKeyDescriptor.getKeyPart();
|
||||
fkPart = foreignKeyDescriptor.getKeyPart();
|
||||
}
|
||||
else {
|
||||
fkTargetModelPart = foreignKeyDescriptor.getKeyPart();
|
||||
fkSideModelPart = foreignKeyDescriptor.getTargetPart();
|
||||
fkPart = foreignKeyDescriptor.getTargetPart();
|
||||
}
|
||||
if ( fkTargetModelPart instanceof NonAggregatedIdentifierMappingImpl ) {
|
||||
return ( (ModelPartContainer) fkSideModelPart ).findSubPart( name, targetType );
|
||||
if ( fkPart instanceof EmbeddableValuedModelPart && fkPart instanceof VirtualModelPart ) {
|
||||
return ( (ModelPartContainer) fkPart ).findSubPart( name, targetType );
|
||||
}
|
||||
return fkSideModelPart;
|
||||
return fkPart;
|
||||
}
|
||||
return EntityValuedFetchable.super.findSubPart( name, targetType );
|
||||
}
|
||||
|
@ -1223,6 +1230,11 @@ public class ToOneAttributeMapping
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimpleJoinPredicate(Predicate predicate) {
|
||||
return foreignKeyDescriptor.isSimpleJoinPredicate( predicate );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfFetchables() {
|
||||
return getEntityMappingType().getNumberOfFetchables();
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.SelectableMappings;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
|
||||
import org.hibernate.tuple.ValueGeneration;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping implements VirtualModelPart {
|
||||
|
||||
public VirtualEmbeddedAttributeMapping(
|
||||
String name,
|
||||
NavigableRole navigableRole,
|
||||
int stateArrayPosition,
|
||||
String tableExpression,
|
||||
StateArrayContributorMetadataAccess attributeMetadataAccess,
|
||||
String parentInjectionAttributeName,
|
||||
FetchTiming mappedFetchTiming,
|
||||
FetchStyle mappedFetchStyle,
|
||||
EmbeddableMappingType embeddableMappingType,
|
||||
ManagedMappingType declaringType,
|
||||
PropertyAccess propertyAccess, ValueGeneration valueGeneration) {
|
||||
super(
|
||||
name,
|
||||
navigableRole,
|
||||
stateArrayPosition,
|
||||
tableExpression,
|
||||
attributeMetadataAccess,
|
||||
parentInjectionAttributeName,
|
||||
mappedFetchTiming,
|
||||
mappedFetchStyle,
|
||||
embeddableMappingType,
|
||||
declaringType,
|
||||
propertyAccess,
|
||||
valueGeneration
|
||||
);
|
||||
}
|
||||
|
||||
public VirtualEmbeddedAttributeMapping(
|
||||
String name,
|
||||
NavigableRole navigableRole,
|
||||
int stateArrayPosition,
|
||||
String tableExpression,
|
||||
StateArrayContributorMetadataAccess attributeMetadataAccess,
|
||||
PropertyAccess parentInjectionAttributePropertyAccess,
|
||||
FetchTiming mappedFetchTiming,
|
||||
FetchStyle mappedFetchStyle,
|
||||
EmbeddableMappingType embeddableMappingType,
|
||||
ManagedMappingType declaringType,
|
||||
PropertyAccess propertyAccess, ValueGeneration valueGeneration) {
|
||||
super(
|
||||
name,
|
||||
navigableRole,
|
||||
stateArrayPosition,
|
||||
tableExpression,
|
||||
attributeMetadataAccess,
|
||||
parentInjectionAttributePropertyAccess,
|
||||
mappedFetchTiming,
|
||||
mappedFetchStyle,
|
||||
embeddableMappingType,
|
||||
declaringType,
|
||||
propertyAccess,
|
||||
valueGeneration
|
||||
);
|
||||
}
|
||||
|
||||
// Constructor is only used for creating the inverse attribute mapping
|
||||
VirtualEmbeddedAttributeMapping(
|
||||
ManagedMappingType keyDeclaringType,
|
||||
TableGroupProducer declaringTableGroupProducer,
|
||||
SelectableMappings selectableMappings,
|
||||
EmbeddableValuedModelPart inverseModelPart,
|
||||
EmbeddableMappingType embeddableTypeDescriptor,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
super(
|
||||
keyDeclaringType,
|
||||
declaringTableGroupProducer,
|
||||
selectableMappings,
|
||||
inverseModelPart,
|
||||
embeddableTypeDescriptor,
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeMapping copy(ManagedMappingType declaringType) {
|
||||
return new VirtualEmbeddedAttributeMapping(
|
||||
getAttributeName(),
|
||||
getNavigableRole(),
|
||||
getStateArrayPosition(),
|
||||
getContainingTableExpression(),
|
||||
getAttributeMetadataAccess(),
|
||||
getParentInjectionAttributePropertyAccess(),
|
||||
getTiming(),
|
||||
getStyle(),
|
||||
getEmbeddableTypeDescriptor(),
|
||||
declaringType,
|
||||
getPropertyAccess(),
|
||||
getValueGeneration()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -167,6 +167,7 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
|
|||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.Queryable;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
|
@ -184,7 +185,6 @@ import org.hibernate.metamodel.mapping.internal.GeneratedValuesProcessor;
|
|||
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleNaturalIdMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.EntityInstantiator;
|
||||
|
@ -5023,7 +5023,7 @@ public abstract class AbstractEntityPersister
|
|||
baseValueType = (ManagedMappingType) attributeMapping.getMappedType();
|
||||
}
|
||||
}
|
||||
else if ( identifierMapping instanceof NonAggregatedIdentifierMappingImpl ) {
|
||||
else if ( identifierMapping instanceof NonAggregatedIdentifierMapping ) {
|
||||
final EmbeddedAttributeMapping embeddedAttributeMapping = (EmbeddedAttributeMapping) findAttributeMapping( NavigableRole.IDENTIFIER_MAPPER_PROPERTY );
|
||||
final AttributeMapping mapping = embeddedAttributeMapping.getMappedType()
|
||||
.findAttributeMapping( basePropertyName );
|
||||
|
@ -6386,8 +6386,8 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
|
||||
private ModelPart getIdentifierModelPart(String name, EntityMappingType treatTargetType) {
|
||||
if ( identifierMapping instanceof NonAggregatedIdentifierMappingImpl ) {
|
||||
final ModelPart subPart = ( (NonAggregatedIdentifierMappingImpl) identifierMapping ).findSubPart(
|
||||
if ( identifierMapping instanceof NonAggregatedIdentifierMapping ) {
|
||||
final ModelPart subPart = ( (NonAggregatedIdentifierMapping) identifierMapping ).findSubPart(
|
||||
name,
|
||||
treatTargetType
|
||||
);
|
||||
|
|
|
@ -28,8 +28,8 @@ import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
|||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
|
||||
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
|
||||
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
|
@ -428,7 +428,7 @@ public class DomainResultCreationStateImpl
|
|||
if ( fetchableContainer instanceof EntityValuedModelPart ) {
|
||||
final EntityValuedModelPart entityValuedFetchable = (EntityValuedModelPart) fetchableContainer;
|
||||
final EntityIdentifierMapping identifierMapping = entityValuedFetchable.getEntityMappingType().getIdentifierMapping();
|
||||
final boolean idClass = identifierMapping instanceof NonAggregatedIdentifierMappingImpl;
|
||||
final boolean idClass = identifierMapping instanceof NonAggregatedIdentifierMapping;
|
||||
final String identifierAttributeName = attributeName( identifierMapping );
|
||||
if ( idClass ) {
|
||||
final Map.Entry<String, NavigablePath> oldEntry = relativePathStack.getCurrent();
|
||||
|
|
|
@ -2842,28 +2842,39 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
querySpec.getFromClause().addRoot( tableGroup );
|
||||
}
|
||||
else {
|
||||
final TableGroupJoin tableGroupJoin = joinProducer.createTableGroupJoin(
|
||||
joinedPath.getNavigablePath(),
|
||||
// Check if we can reuse a table group join of the parent
|
||||
final TableGroup compatibleTableGroup = findCompatibleJoinedGroup(
|
||||
actualParentTableGroup,
|
||||
null,
|
||||
defaultSqlAstJoinType,
|
||||
false,
|
||||
false,
|
||||
this
|
||||
joinProducer,
|
||||
defaultSqlAstJoinType
|
||||
);
|
||||
// Implicit joins in the ON clause of attribute joins need to be added as nested table group joins
|
||||
// We don't have to do that for entity joins etc. as these do not have an inherent dependency on the lhs.
|
||||
// We can just add the implicit join before the currently processing join
|
||||
// See consumeEntityJoin for details
|
||||
final boolean nested = currentClauseStack.getCurrent() == Clause.FROM
|
||||
&& currentlyProcessingJoin instanceof SqmAttributeJoin<?, ?>;
|
||||
if ( nested ) {
|
||||
actualParentTableGroup.addNestedTableGroupJoin( tableGroupJoin );
|
||||
if ( compatibleTableGroup == null ) {
|
||||
final TableGroupJoin tableGroupJoin = joinProducer.createTableGroupJoin(
|
||||
joinedPath.getNavigablePath(),
|
||||
actualParentTableGroup,
|
||||
null,
|
||||
defaultSqlAstJoinType,
|
||||
false,
|
||||
false,
|
||||
this
|
||||
);
|
||||
// Implicit joins in the ON clause of attribute joins need to be added as nested table group joins
|
||||
// We don't have to do that for entity joins etc. as these do not have an inherent dependency on the lhs.
|
||||
// We can just add the implicit join before the currently processing join
|
||||
// See consumeEntityJoin for details
|
||||
final boolean nested = currentClauseStack.getCurrent() == Clause.FROM
|
||||
&& currentlyProcessingJoin instanceof SqmAttributeJoin<?, ?>;
|
||||
if ( nested ) {
|
||||
actualParentTableGroup.addNestedTableGroupJoin( tableGroupJoin );
|
||||
}
|
||||
else {
|
||||
actualParentTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
}
|
||||
tableGroup = tableGroupJoin.getJoinedGroup();
|
||||
}
|
||||
else {
|
||||
actualParentTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
tableGroup = compatibleTableGroup;
|
||||
}
|
||||
tableGroup = tableGroupJoin.getJoinedGroup();
|
||||
}
|
||||
|
||||
fromClauseIndex.register( joinedPath, tableGroup );
|
||||
|
@ -2875,6 +2886,31 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
return tableGroup;
|
||||
}
|
||||
|
||||
private TableGroup findCompatibleJoinedGroup(
|
||||
TableGroup parentTableGroup,
|
||||
TableGroupJoinProducer joinProducer,
|
||||
SqlAstJoinType requestedJoinType) {
|
||||
// We don't look into nested table group joins as that wouldn't be "compatible"
|
||||
for ( TableGroupJoin join : parentTableGroup.getTableGroupJoins() ) {
|
||||
// Compatibility obviously requires the same model part but also join type compatibility
|
||||
// Note that if the requested join type is left, we can also use an existing inner join
|
||||
// The other case, when the requested join type is inner and there is an existing left join,
|
||||
// is not compatible though because the cardinality is different.
|
||||
// We could reuse the join though if we alter the join type to INNER, but that's an optimization for later
|
||||
final SqlAstJoinType joinType = join.getJoinType();
|
||||
if ( join.getJoinedGroup().getModelPart() == joinProducer
|
||||
&& ( requestedJoinType == joinType || requestedJoinType == SqlAstJoinType.LEFT && joinType == SqlAstJoinType.INNER ) ) {
|
||||
// If there is an existing inner join, we can always use that as a new join can never produce results
|
||||
// regardless of the join type or predicate since the LHS is the same table group
|
||||
// If this is a left join though, we have to check if the predicate is simply the association predicate
|
||||
if ( joinType == SqlAstJoinType.INNER || joinProducer.isSimpleJoinPredicate( join.getPredicate() ) ) {
|
||||
return join.getJoinedGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerPluralTableGroupParts(TableGroup tableGroup) {
|
||||
if ( tableGroup instanceof PluralTableGroup ) {
|
||||
final PluralTableGroup pluralTableGroup = (PluralTableGroup) tableGroup;
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
package org.hibernate.query.sqm.sql.internal;
|
||||
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||
import org.hibernate.query.sqm.tree.domain.NonAggregatedCompositeSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||
|
@ -31,7 +30,7 @@ public class NonAggregatedCompositeValuedPathInterpretation<T>
|
|||
final TableGroup tableGroup = sqlAstCreationState
|
||||
.getFromClauseAccess()
|
||||
.findTableGroup( sqmPath.getLhs().getNavigablePath() );
|
||||
final NonAggregatedIdentifierMappingImpl mapping = (NonAggregatedIdentifierMappingImpl) tableGroup.getModelPart()
|
||||
final NonAggregatedIdentifierMapping mapping = (NonAggregatedIdentifierMapping) tableGroup.getModelPart()
|
||||
.findSubPart( sqmPath.getReferencedPathSource().getPathName(), null );
|
||||
|
||||
return new NonAggregatedCompositeValuedPathInterpretation<>(
|
||||
|
|
|
@ -3912,6 +3912,16 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
tableGroupJoinCollector
|
||||
);
|
||||
}
|
||||
// A lazy table group, even if uninitialized, might contain table group joins
|
||||
else if ( joinedGroup instanceof LazyTableGroup ) {
|
||||
processNestedTableGroupJoins( joinedGroup, tableGroupJoinCollector );
|
||||
if ( tableGroupJoinCollector != null ) {
|
||||
tableGroupJoinCollector.addAll( joinedGroup.getTableGroupJoins() );
|
||||
}
|
||||
else {
|
||||
processTableGroupJoins( joinedGroup );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.ast.tree.from;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
|
@ -36,6 +37,8 @@ public class LazyTableGroup extends DelegatingTableGroup {
|
|||
private final Supplier<TableGroup> tableGroupSupplier;
|
||||
private final TableGroup parentTableGroup;
|
||||
private final BiPredicate<NavigablePath, String> navigablePathChecker;
|
||||
private List<TableGroupJoin> tableGroupJoins;
|
||||
private List<TableGroupJoin> nestedTableGroupJoins;
|
||||
private Consumer<TableGroup> tableGroupConsumer;
|
||||
private TableGroup tableGroup;
|
||||
|
||||
|
@ -72,6 +75,18 @@ public class LazyTableGroup extends DelegatingTableGroup {
|
|||
}
|
||||
|
||||
tableGroup = tableGroupSupplier.get();
|
||||
if ( tableGroupJoins != null ) {
|
||||
for ( TableGroupJoin tableGroupJoin : tableGroupJoins ) {
|
||||
tableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
}
|
||||
tableGroupJoins = null;
|
||||
}
|
||||
if ( nestedTableGroupJoins != null ) {
|
||||
for ( TableGroupJoin tableGroupJoin : nestedTableGroupJoins ) {
|
||||
tableGroup.addNestedTableGroupJoin( tableGroupJoin );
|
||||
}
|
||||
nestedTableGroupJoins = null;
|
||||
}
|
||||
if ( tableGroupConsumer != null ) {
|
||||
tableGroupConsumer.accept( tableGroup );
|
||||
tableGroupConsumer = null;
|
||||
|
@ -102,24 +117,70 @@ public class LazyTableGroup extends DelegatingTableGroup {
|
|||
|
||||
@Override
|
||||
public List<TableGroupJoin> getTableGroupJoins() {
|
||||
return tableGroup == null ? Collections.emptyList() : tableGroup.getTableGroupJoins();
|
||||
if ( tableGroup == null ) {
|
||||
return nestedTableGroupJoins == null ? Collections.emptyList() : nestedTableGroupJoins;
|
||||
}
|
||||
else {
|
||||
return tableGroup.getTableGroupJoins();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TableGroupJoin> getNestedTableGroupJoins() {
|
||||
return tableGroup == null ? Collections.emptyList() : tableGroup.getNestedTableGroupJoins();
|
||||
if ( tableGroup == null ) {
|
||||
return tableGroupJoins == null ? Collections.emptyList() : tableGroupJoins;
|
||||
}
|
||||
else {
|
||||
return tableGroup.getNestedTableGroupJoins();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTableGroupJoin(TableGroupJoin join) {
|
||||
if ( tableGroup == null ) {
|
||||
if ( tableGroupJoins == null ) {
|
||||
tableGroupJoins = new ArrayList<>();
|
||||
}
|
||||
tableGroupJoins.add( join );
|
||||
}
|
||||
else {
|
||||
getTableGroup().addTableGroupJoin( join );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNestedTableGroupJoin(TableGroupJoin join) {
|
||||
if ( tableGroup == null ) {
|
||||
if ( nestedTableGroupJoins == null ) {
|
||||
nestedTableGroupJoins = new ArrayList<>();
|
||||
}
|
||||
nestedTableGroupJoins.add( join );
|
||||
}
|
||||
else {
|
||||
getTableGroup().addNestedTableGroupJoin( join );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
|
||||
if ( tableGroup != null ) {
|
||||
if ( tableGroup == null ) {
|
||||
if ( tableGroupJoins != null ) {
|
||||
tableGroupJoins.forEach( consumer );
|
||||
}
|
||||
}
|
||||
else {
|
||||
tableGroup.visitTableGroupJoins( consumer );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
|
||||
if ( tableGroup != null ) {
|
||||
if ( tableGroup == null ) {
|
||||
if ( nestedTableGroupJoins != null ) {
|
||||
nestedTableGroupJoins.forEach( consumer );
|
||||
}
|
||||
}
|
||||
else {
|
||||
tableGroup.visitNestedTableGroupJoins( consumer );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
|
|||
|
||||
SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup);
|
||||
|
||||
boolean isSimpleJoinPredicate(Predicate predicate);
|
||||
|
||||
/**
|
||||
* Create a TableGroupJoin as defined for this producer
|
||||
*/
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
|||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.metamodel.EmbeddableRepresentationStrategy;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
|
@ -34,8 +35,6 @@ import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
|||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.internal.NullValueAssembler;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.EntityJavaTypeDescriptor;
|
||||
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
@ -231,17 +230,12 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
return;
|
||||
}
|
||||
|
||||
// Special handling for non-aggregated attributes which use the actual entity instance as container,
|
||||
// which we access through the fetch parent access.
|
||||
// If this model part is an identifier, we must construct the instance as this is called during resolveKey
|
||||
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
|
||||
final JavaType<?> embeddableJtd = embeddableTypeDescriptor.getMappedJavaTypeDescriptor();
|
||||
|
||||
if ( fetchParentAccess != null &&
|
||||
embeddableJtd.getJavaTypeClass().isAssignableFrom( fetchParentAccess.getInitializedPart().getJavaTypeDescriptor().getJavaTypeClass() )
|
||||
&& embeddableJtd instanceof EntityJavaTypeDescriptor<?>
|
||||
&& !( embedded instanceof CompositeIdentifierMapping )
|
||||
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ) ) {
|
||||
// Virtual model parts use the owning entity as container which the fetch parent access provides.
|
||||
// For an identifier or foreign key this is called during the resolveKey phase of the fetch parent,
|
||||
// so we can't use the fetch parent access in that case.
|
||||
if ( fetchParentAccess != null && embedded instanceof VirtualModelPart
|
||||
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() )
|
||||
&& !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getUnaliasedLocalName() ) ) {
|
||||
fetchParentAccess.resolveInstance( processingState );
|
||||
compositeInstance = fetchParentAccess.getInitializedInstance();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.embeddable;
|
|||
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
|
||||
|
@ -19,4 +20,9 @@ public interface EmbeddableValuedFetchable extends EmbeddableValuedModelPart, Fe
|
|||
default SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) {
|
||||
return SqlAstJoinType.LEFT;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isSimpleJoinPredicate(Predicate predicate) {
|
||||
return predicate == null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,8 +112,11 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab
|
|||
public NavigablePath resolveNavigablePath(Fetchable fetchable) {
|
||||
if ( fetchable instanceof TableGroupProducer ) {
|
||||
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
||||
if ( tableGroupJoin.getJoinedGroup().isFetched() && tableGroupJoin.getJoinedGroup().getModelPart() == fetchable ) {
|
||||
return tableGroupJoin.getNavigablePath();
|
||||
final NavigablePath navigablePath = tableGroupJoin.getNavigablePath();
|
||||
if ( tableGroupJoin.getJoinedGroup().isFetched()
|
||||
&& fetchable.getFetchableName().equals( navigablePath.getUnaliasedLocalName() )
|
||||
&& tableGroupJoin.getJoinedGroup().getModelPart() == fetchable ) {
|
||||
return navigablePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,8 +59,11 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E
|
|||
if ( fetchable instanceof TableGroupProducer &&
|
||||
!getNavigablePath().getUnaliasedLocalName().equals( getNavigablePath().getLocalName() ) ) {
|
||||
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
||||
if ( tableGroupJoin.getJoinedGroup().isFetched() && tableGroupJoin.getJoinedGroup().getModelPart() == fetchable ) {
|
||||
return tableGroupJoin.getNavigablePath();
|
||||
final NavigablePath navigablePath = tableGroupJoin.getNavigablePath();
|
||||
if ( tableGroupJoin.getJoinedGroup().isFetched()
|
||||
&& fetchable.getFetchableName().equals( navigablePath.getUnaliasedLocalName() )
|
||||
&& tableGroupJoin.getJoinedGroup().getModelPart() == fetchable ) {
|
||||
return navigablePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
package org.hibernate.test.hql;
|
||||
package org.hibernate.orm.test.hql;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.query.Query;
|
||||
|
@ -15,6 +15,7 @@ import org.hibernate.testing.jdbc.SQLStatementInspector;
|
|||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -66,9 +67,21 @@ public class NaturalIdDereferenceTest {
|
|||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void deleteData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from BookRefRef" ).executeUpdate();
|
||||
session.createQuery( "delete from BookRef" ).executeUpdate();
|
||||
session.createQuery( "delete from Book" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void naturalIdDereferenceTest(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r.normalBook.isbn FROM BookRef r" );
|
||||
|
@ -83,6 +96,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void normalIdDereferenceFromAlias(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r.normalBook.id FROM BookRef r" );
|
||||
|
@ -97,6 +111,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void naturalIdDereferenceFromAlias(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r.naturalBook.isbn FROM BookRef r" );
|
||||
|
@ -111,6 +126,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void normalIdDereferenceFromImplicitJoin(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r2.normalBookRef.normalBook.id FROM BookRefRef r2" );
|
||||
|
@ -124,6 +140,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void naturalIdDereferenceFromImplicitJoin(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r2.normalBookRef.naturalBook.isbn FROM BookRefRef r2" );
|
||||
|
@ -142,6 +159,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void nestedNaturalIdDereferenceFromImplicitJoin(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r2.naturalBookRef.naturalBook.isbn FROM BookRefRef r2" );
|
||||
|
@ -159,6 +177,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void nestedNaturalIdDereferenceFromImplicitJoin2(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r2.naturalBookRef.naturalBook.id FROM BookRefRef r2" );
|
||||
|
@ -172,6 +191,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void doNotDereferenceNaturalIdIfIsReferenceToPrimaryKey(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r2.normalBookRef.normalBook.isbn FROM BookRefRef r2" );
|
||||
|
@ -185,6 +205,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void selectedEntityIsNotDereferencedForPrimaryKey(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r2.normalBookRef.normalBook FROM BookRefRef r2" );
|
||||
|
@ -209,6 +230,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void selectedEntityIsNotDereferencedForNaturalId(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT r2.naturalBookRef.naturalBook FROM BookRefRef r2" );
|
||||
|
@ -228,6 +250,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void dereferenceNaturalIdInJoin(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery(
|
||||
|
@ -251,6 +274,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void dereferenceNaturalIdInJoin2(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery( "SELECT b.normalBook FROM BookRefRef a " +
|
||||
|
@ -271,6 +295,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void dereferenceNaturalIdInJoin3(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery(
|
||||
|
@ -292,6 +317,7 @@ public class NaturalIdDereferenceTest {
|
|||
@Test
|
||||
public void dereferenceNaturalIdInJoin4(SessionFactoryScope scope) {
|
||||
SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createQuery(
|
Loading…
Reference in New Issue