diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Value.java b/hibernate-core/src/main/java/org/hibernate/mapping/Value.java index d2525cd73528..248c73175971 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Value.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Value.java @@ -86,9 +86,7 @@ default JdbcMapping getSelectableType(MappingContext mappingContext, int index) private JdbcMapping getType(MappingContext factory, Type elementType, int index) { if ( elementType instanceof CompositeType compositeType ) { - final Type[] subtypes = compositeType.getSubtypes(); - for ( int i = 0; i < subtypes.length; i++ ) { - final Type subtype = subtypes[i]; + for ( final var subtype : compositeType.getSubtypes() ) { final int columnSpan = subtype instanceof EntityType entityType ? getIdType( entityType ).getColumnSpan( factory ) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java index 0a1ac0b12589..d201ffe211e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java @@ -143,7 +143,7 @@ public TableGroupJoin createTableGroupJoin( null, creationState ); - return new TableGroupJoin( navigablePath, joinType, tableGroup, null ); + return new TableGroupJoin( navigablePath, joinType, tableGroup ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java index 766efc1c3dfe..55f568eca153 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java @@ -242,7 +242,7 @@ public TableGroupJoin createTableGroupJoin( creationState ); - return new TableGroupJoin( navigablePath, joinType, tableGroup, null ); + return new TableGroupJoin( navigablePath, joinType, tableGroup ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ManyToManyCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ManyToManyCollectionPart.java index 6de2d203f13d..44cc814d6485 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ManyToManyCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ManyToManyCollectionPart.java @@ -266,14 +266,9 @@ public TableGroupJoin createTableGroupJoin( creationState ); - final var join = new TableGroupJoin( - navigablePath, - joinType, - lazyTableGroup, - null - ); + final var join = new TableGroupJoin( navigablePath, joinType, lazyTableGroup ); - lazyTableGroup.setTableGroupInitializerCallback( (partTableGroup) -> { + lazyTableGroup.setTableGroupInitializerCallback( partTableGroup -> { // `partTableGroup` is the association table group join.applyPredicate( foreignKey.generateJoinPredicate( @@ -322,7 +317,9 @@ public LazyTableGroup createRootTableGroupJoin( this, explicitSourceAlias, sqlAliasBase, - creationState.getCreationContext().getSessionFactory(), + creationState.getCreationContext() + // TODO: FIXME + .getSessionFactory(), lhs ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/OneToManyCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/OneToManyCollectionPart.java index d0a196ecee15..855ab62424e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/OneToManyCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/OneToManyCollectionPart.java @@ -160,7 +160,8 @@ public TableGroupJoin createTableGroupJoin( if ( mapKeyPropertyName != null ) { final var elementPart = (EntityCollectionPart) - getCollectionDescriptor().getAttributeMapping().getElementDescriptor(); + getCollectionDescriptor().getAttributeMapping() + .getElementDescriptor(); if ( elementPart.getAssociatedEntityMappingType().findAttributeMapping( mapKeyPropertyName ) instanceof ToOneAttributeMapping toOne ) { final var mapKeyPropertyPath = navigablePath.append( mapKeyPropertyName ); @@ -180,7 +181,7 @@ public TableGroupJoin createTableGroupJoin( } } - return new TableGroupJoin( navigablePath, joinType, elementTableGroup, null ); + return new TableGroupJoin( navigablePath, joinType, elementTableGroup ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index 801284e877d2..e3cca8e43a3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -4,6 +4,7 @@ */ package org.hibernate.metamodel.mapping.internal; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.MappingException; import org.hibernate.cache.MutableCacheKeyBuilder; @@ -50,7 +51,6 @@ import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; -import org.hibernate.sql.ast.internal.TableGroupJoinHelper; import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasStemHelper; import org.hibernate.sql.ast.spi.SqlAstCreationState; @@ -82,6 +82,7 @@ import static java.util.Locale.ROOT; import static org.hibernate.boot.model.internal.SoftDeleteHelper.resolveSoftDeleteMapping; import static org.hibernate.internal.util.StringHelper.subStringNullIfEmpty; +import static org.hibernate.sql.ast.internal.TableGroupJoinHelper.determineJoinForPredicateApply; /** * @author Steve Ebersole @@ -184,7 +185,9 @@ public String getIndexPropertyName() { } }; - softDeleteMapping = resolveSoftDeleteMapping( this, bootDescriptor, getSeparateCollectionTable(), creationProcess ); + softDeleteMapping = + resolveSoftDeleteMapping( this, bootDescriptor, + getSeparateCollectionTable(), creationProcess ); injectAttributeMapping( elementDescriptor, indexDescriptor, collectionDescriptor, this ); @@ -206,36 +209,45 @@ private static void validateTargetEntity( String attributeName, PropertyAccess propertyAccess, MappingModelCreationProcess creationProcess) { - final var representationStrategy = getTypeRepresentationStrategy( declaringType ); - if ( representationStrategy == null + final var representationStrategy = typeRepresentationStrategy( declaringType ); + if ( representationStrategy != null // nothing to check against with dynamic models - || representationStrategy.getMode() != RepresentationMode.POJO ) { - return; + && representationStrategy.getMode() == RepresentationMode.POJO ) { + final var attributeMemberDetails = + getMemberDetails( attributeName, propertyAccess, + declaringClassDetails( declaringType, creationProcess ) ); + if ( attributeMemberDetails != null ) { + checkElementType( elementPart, declaringType, attributeName, attributeMemberDetails ); + } + // else usually indicates the case of embeddable + // inheritance mentioned in the @implNote } + } - var declaringClassDetails = - creationProcess.getCreationContext().getBootstrapContext() - .getModelsContext().getClassDetailsRegistry() - .resolveClassDetails( declaringType.getJavaType().getTypeName() ); - - final var attributeMemberDetails = - getMemberDetails( attributeName, propertyAccess, declaringClassDetails ); - if ( attributeMemberDetails == null ) { - // generally indicates the case of embeddable inheritance mentioned - // in the `implNote` - return; - } + private static ClassDetails declaringClassDetails( + ManagedMappingType declaringType, + MappingModelCreationProcess creationProcess) { + return creationProcess.getCreationContext().getBootstrapContext() + .getModelsContext().getClassDetailsRegistry() + .resolveClassDetails( declaringType.getJavaType().getTypeName() ); + } - var elementType = + private static void checkElementType( + EntityCollectionPart elementPart, + ManagedMappingType declaringType, + String attributeName, + MemberDetails attributeMemberDetails) { + final var elementType = attributeMemberDetails.getElementType() .determineRawClass().toJavaClass(); if ( !Object.class.equals( elementType ) ) { - var targetType = elementPart.getJavaType().getJavaTypeClass(); + final var targetType = elementPart.getJavaType().getJavaTypeClass(); if ( !elementType.isAssignableFrom( targetType ) ) { throw new MappingException( String.format( ROOT, - "Plural attribute [%s.%s] was mapped with targetEntity=`%s`, but the attribute is declared as `%s`", + "Plural attribute [%s.%s] was mapped with targetEntity=`%s`," + + " but the attribute is declared as `%s`", declaringType.getNavigableRole().getFullPath(), attributeName, targetType.getName(), @@ -261,7 +273,7 @@ else if ( member instanceof Method method ) { } } - private static @Nullable ManagedTypeRepresentationStrategy getTypeRepresentationStrategy(ManagedMappingType declaringType) { + private static @Nullable ManagedTypeRepresentationStrategy typeRepresentationStrategy(ManagedMappingType declaringType) { if ( declaringType instanceof EntityMappingType declaringEntityType ) { return declaringEntityType.getRepresentationStrategy(); } @@ -371,9 +383,9 @@ public boolean isBidirectionalAttributeName(NavigablePath fetchablePath, ToOneAt // and the FK-key refer to the same column then we say this is bidirectional, // given that this is only invoked for model parts of the collection elements ? modelPart.getSideNature() == ForeignKeyDescriptor.Nature.KEY - && collectionDescriptor.isOneToMany() - && fkDescriptor.getTargetPart() == modelPart.getForeignKeyDescriptor().getTargetPart() - && areEqual( fkDescriptor.getKeyPart(), modelPart.getForeignKeyDescriptor().getKeyPart() ) + && collectionDescriptor.isOneToMany() + && fkDescriptor.getTargetPart() == modelPart.getForeignKeyDescriptor().getTargetPart() + && areEqual( fkDescriptor.getKeyPart(), modelPart.getForeignKeyDescriptor().getKeyPart() ) : fetchablePath.getLocalName().equals( bidirectionalAttributeName ); } @@ -383,8 +395,8 @@ private boolean areEqual(ValuedModelPart part1, ValuedModelPart part2) { return false; } for ( int i = 0; i < typeCount; i++ ) { - final SelectableMapping selectable1 = part1.getSelectable( i ); - final SelectableMapping selectable2 = part2.getSelectable( i ); + final var selectable1 = part1.getSelectable( i ); + final var selectable2 = part2.getSelectable( i ); if ( selectable1.getJdbcMapping() != selectable2.getJdbcMapping() || !selectable1.getContainingTableExpression().equals( selectable2.getContainingTableExpression() ) || !selectable1.getSelectionExpression().equals( selectable2.getSelectionExpression() ) ) { @@ -686,19 +698,19 @@ public Fetch resolveCircularFetch( FetchParent fetchParent, FetchTiming fetchTiming, DomainResultCreationState creationState) { - if ( fetchTiming == FetchTiming.IMMEDIATE ) { - final boolean alreadyVisited = creationState.isAssociationKeyVisited( fkDescriptor.getAssociationKey() ); - if ( alreadyVisited ) { - return createSelectEagerCollectionFetch( - fetchParent, - fetchablePath, - creationState, - creationState.getSqlAstCreationState() - ); - } + if ( fetchTiming == FetchTiming.IMMEDIATE + // if it's already visited + && creationState.isAssociationKeyVisited( fkDescriptor.getAssociationKey() ) ) { + return createSelectEagerCollectionFetch( + fetchParent, + fetchablePath, + creationState, + creationState.getSqlAstCreationState() + ); + } + else { + return null; } - - return null; } private Fetch createSelectEagerCollectionFetch( @@ -706,20 +718,29 @@ private Fetch createSelectEagerCollectionFetch( NavigablePath fetchablePath, DomainResultCreationState creationState, SqlAstCreationState sqlAstCreationState) { - final DomainResult collectionKeyDomainResult; - if ( referencedPropertyName != null ) { - collectionKeyDomainResult = getKeyDescriptor().createTargetDomainResult( - fetchablePath, - sqlAstCreationState.getFromClauseAccess() - .getTableGroup( fetchParent.getNavigablePath() ), - fetchParent, - creationState - ); + return buildSelectEagerCollectionFetch( fetchablePath, this, + collectionKeyDomainResult( fetchParent, fetchablePath, creationState, sqlAstCreationState ), + fetchParent ); + } + + private @Nullable DomainResult collectionKeyDomainResult( + FetchParent fetchParent, + NavigablePath fetchablePath, + DomainResultCreationState creationState, + SqlAstCreationState sqlAstCreationState) { + if ( referencedPropertyName == null ) { + return null; } else { - collectionKeyDomainResult = null; + return getKeyDescriptor() + .createTargetDomainResult( + fetchablePath, + sqlAstCreationState.getFromClauseAccess() + .getTableGroup( fetchParent.getNavigablePath() ), + fetchParent, + creationState + ); } - return buildSelectEagerCollectionFetch( fetchablePath, this, collectionKeyDomainResult, fetchParent ); } private TableGroup resolveCollectionTableGroup( @@ -871,8 +892,8 @@ public TableGroupJoin createTableGroupJoin( collectionPredicateCollector.getPredicate() ); if ( predicateCollector != collectionPredicateCollector ) { - final var joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( tableGroupJoin ); - joinForPredicate.applyPredicate( predicateCollector.getPredicate() ); + determineJoinForPredicateApply( tableGroupJoin ) + .applyPredicate( predicateCollector.getPredicate() ); } return tableGroupJoin; } @@ -922,12 +943,9 @@ public SqlAstJoinType determineSqlJoinType(TableGroup lhs, @Nullable SqlAstJoinT return SqlAstJoinType.LEFT; } else if ( requestedJoinType == null ) { - if ( fetched ) { - return getDefaultSqlAstJoinType( lhs ); - } - else { - return SqlAstJoinType.INNER; - } + return fetched + ? getDefaultSqlAstJoinType( lhs ) + : SqlAstJoinType.INNER; } else { return requestedJoinType; @@ -967,43 +985,62 @@ private TableGroup createRootTableGroupJoin( boolean addsPredicate, Consumer predicateConsumer, SqlAstCreationState creationState) { - final CollectionPersister collectionDescriptor = getCollectionDescriptor(); - final SqlAstJoinType joinType = determineSqlJoinType( lhs, requestedJoinType, fetched ); - final SqlAliasBase sqlAliasBase = creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ); - - final TableGroup tableGroup; - if ( collectionDescriptor.isOneToMany() ) { - tableGroup = createOneToManyTableGroup( - lhs.canUseInnerJoins() && joinType == SqlAstJoinType.INNER, - joinType, - navigablePath, - fetched, - addsPredicate, - explicitSourceAlias, - sqlAliasBase, - creationState - ); - } - else { - tableGroup = createCollectionTableGroup( - lhs.canUseInnerJoins() && joinType == SqlAstJoinType.INNER, - joinType, - navigablePath, - fetched, - addsPredicate, - explicitSourceAlias, - sqlAliasBase, - creationState - ); - } + + final var tableGroup = + rootTableGroup( + navigablePath, + lhs, + explicitSourceAlias, + fetched, + addsPredicate, + creationState, + determineSqlJoinType( lhs, requestedJoinType, fetched ), + creationState.getSqlAliasBaseGenerator() + .createSqlAliasBase( getSqlAliasStem() ) + ); if ( predicateConsumer != null ) { - predicateConsumer.accept( getKeyDescriptor().generateJoinPredicate( lhs, tableGroup, creationState ) ); + predicateConsumer.accept( getKeyDescriptor() + .generateJoinPredicate( lhs, tableGroup, creationState ) ); } return tableGroup; } + private @NonNull TableGroup rootTableGroup( + NavigablePath navigablePath, + TableGroup lhs, + String explicitSourceAlias, + boolean fetched, + boolean addsPredicate, + SqlAstCreationState creationState, + SqlAstJoinType joinType, + SqlAliasBase sqlAliasBase) { + return getCollectionDescriptor().isOneToMany() + ? createOneToManyTableGroup( + lhs.canUseInnerJoins() + && joinType == SqlAstJoinType.INNER, + joinType, + navigablePath, + fetched, + addsPredicate, + explicitSourceAlias, + sqlAliasBase, + creationState + ) + : createCollectionTableGroup( + lhs.canUseInnerJoins() + && joinType == SqlAstJoinType.INNER, + joinType, + navigablePath, + fetched, + addsPredicate, + explicitSourceAlias, + sqlAliasBase, + creationState + ); + } + @Override public void setForeignKeyDescriptor(ForeignKeyDescriptor fkDescriptor) { @@ -1019,33 +1056,27 @@ private TableGroup createOneToManyTableGroup( String sourceAlias, SqlAliasBase explicitSqlAliasBase, SqlAstCreationState creationState) { + final var oneToManyCollectionPart = (OneToManyCollectionPart) elementDescriptor; final var sqlAliasBase = SqlAliasBase.from( explicitSqlAliasBase, sourceAlias, this, creationState.getSqlAliasBaseGenerator() ); - final var oneToManyCollectionPart = (OneToManyCollectionPart) elementDescriptor; - final var elementTableGroup = oneToManyCollectionPart.createAssociatedTableGroup( - canUseInnerJoins, - navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), - fetched, - sourceAlias, - sqlAliasBase, - creationState - ); final var tableGroup = new OneToManyTableGroup( this, - elementTableGroup, - creationState.getCreationContext().getSessionFactory() + oneToManyCollectionPart.createAssociatedTableGroup( + canUseInnerJoins, + navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), + fetched, + sourceAlias, + sqlAliasBase, + creationState + ), + creationState.getCreationContext() + // TODO: FIX ME + .getSessionFactory() ); - // For inner joins we never need join nesting - final boolean nestedJoin = joinType != SqlAstJoinType.INNER - // For outer joins we need nesting if there might be an on-condition that refers to the element table - && ( addsPredicate - || isAffectedByEnabledFilters( creationState.getLoadQueryInfluencers(), creationState.applyOnlyLoadByKeyFilters() ) - || collectionDescriptor.hasWhereRestrictions() ); - if ( indexDescriptor instanceof TableGroupJoinProducer tableGroupJoinProducer ) { final var tableGroupJoin = tableGroupJoinProducer.createTableGroupJoin( navigablePath.append( CollectionPart.Nature.INDEX.getName() ), @@ -1057,12 +1088,23 @@ private TableGroup createOneToManyTableGroup( false, creationState ); - tableGroup.registerIndexTableGroup( tableGroupJoin, nestedJoin ); + tableGroup.registerIndexTableGroup( tableGroupJoin, + isNestedJoin( joinType, addsPredicate, creationState ) ); } return tableGroup; } + private boolean isNestedJoin(SqlAstJoinType joinType, boolean addsPredicate, SqlAstCreationState creationState) { + // For inner joins we never need join nesting + return joinType != SqlAstJoinType.INNER + // For outer joins we need nesting if there might be an on-condition that refers to the element table + && ( addsPredicate + || collectionDescriptor.hasWhereRestrictions() + || isAffectedByEnabledFilters( creationState.getLoadQueryInfluencers(), + creationState.applyOnlyLoadByKeyFilters() ) ); + } + private TableGroup createCollectionTableGroup( boolean canUseInnerJoins, SqlAstJoinType joinType, @@ -1097,14 +1139,12 @@ private TableGroup createCollectionTableGroup( sqlAliasBase, s -> false, null, - creationState.getCreationContext().getSessionFactory() + creationState.getCreationContext() + // TODO: FIX ME + .getSessionFactory() ); - // For inner joins we never need join nesting - final boolean nestedJoin = joinType != SqlAstJoinType.INNER - // For outer joins we need nesting if there might be an on-condition that refers to the element table - && ( addsPredicate - || isAffectedByEnabledFilters( creationState.getLoadQueryInfluencers(), creationState.applyOnlyLoadByKeyFilters() ) - || collectionDescriptor.hasWhereRestrictions() ); + + final boolean nestedJoin = isNestedJoin( joinType, addsPredicate, creationState ); if ( elementDescriptor instanceof TableGroupJoinProducer tableGroupJoinProducer ) { final var tableGroupJoin = tableGroupJoinProducer.createTableGroupJoin( @@ -1142,7 +1182,8 @@ public TableGroup createRootTableGroup( boolean canUseInnerJoins, NavigablePath navigablePath, String explicitSourceAlias, - SqlAliasBase explicitSqlAliasBase, Supplier> additionalPredicateCollectorAccess, + SqlAliasBase explicitSqlAliasBase, + Supplier> additionalPredicateCollectorAccess, SqlAstCreationState creationState) { if ( getCollectionDescriptor().isOneToMany() ) { return createOneToManyTableGroup( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java index 05d6590c3f7b..703306b7896d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java @@ -108,6 +108,12 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.createInverseModelPart; +import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.getTableIdentifierExpression; +import static org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping.Cardinality.LOGICAL_ONE_TO_ONE; +import static org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping.Cardinality.MANY_TO_ONE; +import static org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping.Cardinality.ONE_TO_ONE; + /** * @author Steve Ebersole */ @@ -254,7 +260,7 @@ public ToOneAttributeMapping( // validate the type. var declaredType = propertyAccess.getGetter().getReturnTypeClass(); if ( !Object.class.equals( declaredType ) ) { - var targetType = entityMappingType.getMappedJavaType().getJavaTypeClass(); + final var targetType = entityMappingType.getMappedJavaType().getJavaTypeClass(); if ( !declaredType.isAssignableFrom( targetType ) ) { throw new MappingException( String.format( @@ -287,9 +293,10 @@ public ToOneAttributeMapping( ); if ( bootValue instanceof ManyToOne manyToOne ) { notFoundAction = manyToOne.getNotFoundAction(); - cardinality = manyToOne.isLogicalOneToOne() - ? Cardinality.LOGICAL_ONE_TO_ONE - : Cardinality.MANY_TO_ONE; + cardinality = + manyToOne.isLogicalOneToOne() + ? LOGICAL_ONE_TO_ONE + : MANY_TO_ONE; final var entityBinding = manyToOne.getMetadata() .getEntityBinding( manyToOne.getReferencedEntityName() ); @@ -299,7 +306,7 @@ public ToOneAttributeMapping( bootValue.getPropertyName() == null ? name : bootValue.getPropertyName(); - if ( cardinality == Cardinality.LOGICAL_ONE_TO_ONE ) { + if ( cardinality == LOGICAL_ONE_TO_ONE ) { boolean hasJoinTable = false; // Handle join table cases for ( var join : entityBinding.getJoinClosure() ) { @@ -327,12 +334,13 @@ && equal( join.getKey(), manyToOne ) ) { } else { this.hasJoinTable = false; - bidirectionalAttributeName = findBidirectionalOneToManyAttributeName( - propertyPath, - declaringType, - null, - entityBinding.getPropertyClosure() - ); + bidirectionalAttributeName = + findBidirectionalOneToManyAttributeName( + propertyPath, + declaringType, + null, + entityBinding.getPropertyClosure() + ); } bidirectionalAttributePath = bidirectionalAttributeName; } @@ -340,7 +348,7 @@ && equal( join.getKey(), manyToOne ) ) { // Only set the bidirectional attribute name if the referenced property can actually be circular i.e. an entity type final var property = entityBinding.getProperty( referencedPropertyName ); hasJoinTable = - cardinality == Cardinality.LOGICAL_ONE_TO_ONE + cardinality == LOGICAL_ONE_TO_ONE && property != null && property.getValue() instanceof ManyToOne manyToOneValue && manyToOneValue.isLogicalOneToOne(); @@ -354,7 +362,7 @@ && equal( join.getKey(), manyToOne ) ) { } else { final String targetTableName = - MappingModelCreationHelper.getTableIdentifierExpression( manyToOne.getTable(), + getTableIdentifierExpression( manyToOne.getTable(), declaringEntityPersister.getFactory() ); if ( CollectionPart.Nature.fromNameExact( navigableRole.getParent().getLocalName() ) != null ) { // * the to-one's parent is directly a collection element or index @@ -365,7 +373,9 @@ && equal( join.getKey(), manyToOne ) ) { final var pluralAttribute = (PluralAttributeMapping) declaringEntityPersister.findByPath( path ); assert pluralAttribute != null; - final var persister = (AbstractCollectionPersister) pluralAttribute.getCollectionDescriptor(); + final var persister = + (AbstractCollectionPersister) + pluralAttribute.getCollectionDescriptor(); isKeyTableNullable = !persister.getTableName().equals( targetTableName ); } else { @@ -381,7 +391,7 @@ && equal( join.getKey(), manyToOne ) ) { } else { assert bootValue instanceof OneToOne; - cardinality = Cardinality.ONE_TO_ONE; + cardinality = ONE_TO_ONE; hasJoinTable = false; /* @@ -439,16 +449,15 @@ the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does isInternalLoadNullable = isNullable(); } - if ( entityMappingType.getSoftDeleteMapping() != null ) { - // cannot be lazy - if ( getTiming() == FetchTiming.DELAYED ) { - throw new UnsupportedMappingException( String.format( - Locale.ROOT, - "To-one attribute (%s.%s) cannot be mapped as LAZY as its associated entity is defined with @SoftDelete", - declaringType.getPartName(), - getAttributeName() - ) ); - } + if ( entityMappingType.getSoftDeleteMapping() != null + // cannot be lazy + && getTiming() == FetchTiming.DELAYED ) { + throw new UnsupportedMappingException( String.format( + Locale.ROOT, + "To-one attribute (%s.%s) cannot be mapped as LAZY as its associated entity is defined with @SoftDelete", + declaringType.getPartName(), + getAttributeName() + ) ); } if ( referencedPropertyName == null ) { @@ -458,12 +467,13 @@ the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does bootValue.getBuildingContext().getMetadataCollector() .getEntityBinding( entityMappingType.getEntityName() ); final var identifierMapper = entityBinding.getIdentifierMapper(); - final Type propertyType = + final var propertyType = identifierMapper == null ? entityBinding.getIdentifier().getType() : identifierMapper.getType(); if ( entityBinding.getIdentifierProperty() == null ) { - if ( propertyType instanceof ComponentType compositeType && compositeType.isEmbedded() + if ( propertyType instanceof ComponentType compositeType + && compositeType.isEmbedded() && compositeType.getPropertyNames().length == 1 ) { targetKeyPropertyName = compositeType.getPropertyNames()[0]; addPrefixedPropertyPaths( @@ -525,7 +535,8 @@ the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does this.targetKeyPropertyNames = targetKeyPropertyNames; } else { - if ( propertyType instanceof ComponentType compositeType && compositeType.isEmbedded() + if ( propertyType instanceof ComponentType compositeType + && compositeType.isEmbedded() && compositeType.getPropertyNames().length == 1 ) { final Set targetKeyPropertyNames = new HashSet<>( 2 ); this.targetKeyPropertyName = compositeType.getPropertyNames()[0]; @@ -548,8 +559,10 @@ the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does this.targetKeyPropertyName = referencedPropertyName; final String mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ); - // If there is a "virtual property" for a non-PK join mapping, we try to see if the columns match the - // primary key columns and if so, we add the primary key property name as target key property + // If there is a "virtual property" for a non-PK join mapping, + // we try to see if the columns match the primary key columns + // and if so, we add the primary key property name as target + // key property if ( mapsIdAttributeName != null ) { addPrefixedPropertyPaths( targetKeyPropertyNames, @@ -584,14 +597,15 @@ private static SelectablePath findBidirectionalOneToManyAttributeName( for ( var property : properties ) { final var value = property.getValue(); if ( value instanceof Component component ) { - final var bidirectionalAttributeName = findBidirectionalOneToManyAttributeName( - propertyPath, - declaringType, - parentSelectablePath == null - ? SelectablePath.parse( property.getName() ) - : parentSelectablePath.append( property.getName() ), - component.getProperties() - ); + final var bidirectionalAttributeName = + findBidirectionalOneToManyAttributeName( + propertyPath, + declaringType, + parentSelectablePath == null + ? SelectablePath.parse( property.getName() ) + : parentSelectablePath.append( property.getName() ), + component.getProperties() + ); if ( bidirectionalAttributeName != null ) { return bidirectionalAttributeName; } @@ -634,7 +648,8 @@ else if ( value instanceof OneToOne oneToOne ) { if ( declaringTableGroupProducer.getNavigableRole().getLocalName() .equals( oneToOne.getReferencedEntityName() ) && propertyPath.equals( oneToOne.getMappedByProperty() ) - && oneToOne.getReferencedEntityName().equals( declaringType.getJavaType().getTypeName() ) ) { + && oneToOne.getReferencedEntityName() + .equals( declaringType.getJavaType().getTypeName() ) ) { return parentSelectablePath == null ? SelectablePath.parse( property.getName() ) : parentSelectablePath.append( property.getName() ); @@ -647,13 +662,16 @@ else if ( value instanceof OneToOne oneToOne ) { private static FetchTiming adjustFetchTiming( FetchTiming mappedFetchTiming, ToOne bootValue) { - return bootValue instanceof ManyToOne manyToOne && manyToOne.getNotFoundAction() != null + return bootValue instanceof ManyToOne manyToOne + && manyToOne.getNotFoundAction() != null ? FetchTiming.IMMEDIATE : mappedFetchTiming; } - private static TableGroupProducer resolveDeclaringTableGroupProducer(EntityPersister declaringEntityPersister, NavigableRole navigableRole) { - // Also handle cases where a collection contains an embeddable, that contains an association + private static TableGroupProducer resolveDeclaringTableGroupProducer( + EntityPersister declaringEntityPersister, NavigableRole navigableRole) { + // Also handle cases where a collection contains an embeddable, + // that contains an association NavigableRole parentRole = navigableRole.getParent(); String collectionRole = null; do { @@ -727,11 +745,10 @@ private static boolean equal(Value lhsValue, Value rhsValue) { static String findMapsIdPropertyName(EntityMappingType entityMappingType, String referencedPropertyName) { final var persister = entityMappingType.getEntityPersister(); - if ( Arrays.equals( persister.getIdentifierColumnNames(), - persister.getPropertyColumnNames( referencedPropertyName ) ) ) { - return persister.getIdentifierPropertyName(); - } - return null; + return Arrays.equals( persister.getIdentifierColumnNames(), + persister.getPropertyColumnNames( referencedPropertyName ) ) + ? persister.getIdentifierPropertyName() + : null; } public static void addPrefixedPropertyPaths( @@ -778,16 +795,7 @@ public static void addPrefixedPropertyNames( else if ( type instanceof EntityType entityType ) { final var identifierOrUniqueKeyType = entityType.getIdentifierOrUniqueKeyType( factory.getRuntimeMetamodels() ); - final String propertyName; - if ( entityType.isReferenceToPrimaryKey() ) { - propertyName = entityType.getAssociatedEntityPersister( factory ).getIdentifierPropertyName(); - } - else if ( identifierOrUniqueKeyType instanceof EmbeddedComponentType ) { - propertyName = null; - } - else { - propertyName = entityType.getRHSUniqueKeyPropertyName(); - } + final String propertyName = propertyName( factory, entityType, identifierOrUniqueKeyType ); final String newPrefix; final String newPkPrefix; final String newFkPrefix; @@ -826,6 +834,18 @@ else if ( propertyName == null ) { } } + private static @Nullable String propertyName(SessionFactoryImplementor factory, EntityType entityType, Type identifierOrUniqueKeyType) { + if ( entityType.isReferenceToPrimaryKey() ) { + return entityType.getAssociatedEntityPersister( factory ).getIdentifierPropertyName(); + } + else if ( identifierOrUniqueKeyType instanceof EmbeddedComponentType ) { + return null; + } + else { + return entityType.getRHSUniqueKeyPropertyName(); + } + } + public ToOneAttributeMapping copy(ManagedMappingType declaringType, TableGroupProducer declaringTableGroupProducer) { return new ToOneAttributeMapping( this, declaringType, declaringTableGroupProducer ); } @@ -834,7 +854,7 @@ public ToOneAttributeMapping copy(ManagedMappingType declaringType, TableGroupPr public void setForeignKeyDescriptor(ForeignKeyDescriptor foreignKeyDescriptor) { assert identifyingColumnsTableExpression != null; this.foreignKeyDescriptor = foreignKeyDescriptor; - if ( cardinality == Cardinality.ONE_TO_ONE && bidirectionalAttributePath != null ) { + if ( cardinality == ONE_TO_ONE && bidirectionalAttributePath != null ) { sideNature = ForeignKeyDescriptor.Nature.TARGET; } else { @@ -850,7 +870,7 @@ public void setForeignKeyDescriptor(ForeignKeyDescriptor foreignKeyDescriptor) { // Otherwise we need to join to the associated entity table(s) final boolean forceJoin = hasNotFoundAction() || entityMappingType.getSoftDeleteMapping() != null - || ( cardinality == Cardinality.ONE_TO_ONE && isNullable() ); + || cardinality == ONE_TO_ONE && isNullable(); canUseParentTableGroup = ! forceJoin && sideNature == ForeignKeyDescriptor.Nature.KEY && declaringTableGroupProducer.containsTableReference( identifyingColumnsTableExpression ); @@ -865,7 +885,7 @@ && getAssociatedEntityMappingType().getIdentifierMapping() // This is needed if the association entity nests the "inverse" toOne association in the embedded id, // because then, the key part of the foreign key is just a simple value instead of the expected embedded id // when doing delayed creation/querying of target entities. See HHH-19687 for details - circularFetchModelPart = MappingModelCreationHelper.createInverseModelPart( + circularFetchModelPart = createInverseModelPart( identifierMapping, getDeclaringType(), this, @@ -961,12 +981,13 @@ public ModelPart findSubPart(String name, EntityMappingType targetType) { sideNature == ForeignKeyDescriptor.Nature.KEY ? foreignKeyDescriptor.getKeyPart() : foreignKeyDescriptor.getTargetPart(); - if ( fkPart instanceof EmbeddableValuedModelPart && fkPart instanceof VirtualModelPart + if ( fkPart instanceof EmbeddableValuedModelPart modelPart + && fkPart instanceof VirtualModelPart && !EntityIdentifierMapping.ID_ROLE_NAME.equals( name ) && !ForeignKeyDescriptor.PART_NAME.equals( name ) && !ForeignKeyDescriptor.TARGET_PART_NAME.equals( name ) && !fkPart.getPartName().equals( name ) ) { - return ( (ModelPartContainer) fkPart ).findSubPart( name, targetType ); + return modelPart.findSubPart( name, targetType ); } return fkPart; } @@ -1015,8 +1036,9 @@ public class PrimaryKey { private Key key; } */ - if ( parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.PART_NAME ) - || parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.TARGET_PART_NAME ) ) { + final String localName = parentNavigablePath.getLocalName(); + if ( localName.equals( ForeignKeyDescriptor.PART_NAME ) + || localName.equals( ForeignKeyDescriptor.TARGET_PART_NAME ) ) { // todo (6.0): maybe it's better to have a flag in creation state that marks if we are building a circular fetch domain result already to skip this? return null; } @@ -1084,7 +1106,8 @@ private DomainResult determineCircularKeyResult( if ( circularFetchModelPart != null ) { return circularFetchModelPart.createDomainResult( fetchablePath, - createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ), + createTableGroupForDelayedFetch( fetchablePath, + parentTableGroup, null, creationState ), null, creationState ); @@ -1092,7 +1115,8 @@ private DomainResult determineCircularKeyResult( else if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) { return foreignKeyDescriptor.createKeyDomainResult( fetchablePath, - createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ), + createTableGroupForDelayedFetch( fetchablePath, + parentTableGroup, null, creationState ), fetchParent, creationState ); @@ -1189,7 +1213,7 @@ class EmbeddableB { */ return false; } - else if ( cardinality == Cardinality.MANY_TO_ONE ) { + else if ( cardinality == MANY_TO_ONE ) { /* class Child { @OneToOne(mappedBy = "biologicalChild") @@ -1212,19 +1236,19 @@ class Mother { final var parentPath = grandparentNavigablePath.getParent(); // This can be null for a collection loader if ( parentPath == null ) { - return grandparentNavigablePath.getFullPath().equals( - entityMappingType.findByPath( bidirectionalAttributePath ).getNavigableRole().getFullPath() - ); + return grandparentNavigablePath.getFullPath() + .equals( entityMappingType.findByPath( bidirectionalAttributePath ) + .getNavigableRole().getFullPath() ); } else { // If the parent is null, this is a simple collection fetch of a root, in which case the types must match if ( parentPath.getParent() == null ) { final String entityName = entityMappingType.getPartName(); - return parentPath.getFullPath().startsWith( entityName ) && ( - parentPath.getFullPath().length() == entityName.length() - // Ignore a possible alias - || parentPath.getFullPath().charAt( entityName.length() ) == '(' - ); + final String fullPath = parentPath.getFullPath(); + return fullPath.startsWith( entityName ) + && ( fullPath.length() == entityName.length() + // Ignore a possible alias + || fullPath.charAt( entityName.length() ) == '('); } // If we have a parent, we ensure that the parent is the same as the attribute name else { @@ -1384,7 +1408,8 @@ else if ( CollectionPart.Nature.fromNameExact( parentNavigablePath.getLocalName( ); } - if ( entityMappingType.isConcreteProxy() && sideNature == ForeignKeyDescriptor.Nature.TARGET ) { + if ( entityMappingType.isConcreteProxy() + && sideNature == ForeignKeyDescriptor.Nature.TARGET ) { createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ); } @@ -1467,7 +1492,9 @@ private NavigablePath getReferencedNavigablePath( DomainResultCreationState creationState, NavigablePath parentNavigablePath) { NavigablePath referencedNavigablePath = parentNavigablePath.getParent(); - MappingType partMappingType = creationState.resolveModelPart( referencedNavigablePath ).getPartMappingType(); + MappingType partMappingType = + creationState.resolveModelPart( referencedNavigablePath ) + .getPartMappingType(); /* class LineItem { @@ -1523,13 +1550,17 @@ class Level3 { */ while ( !( partMappingType instanceof EntityMappingType entityMapping ) || ( partMappingType != entityMappingType - && !entityMappingType.getEntityPersister().isSubclassEntityName( partMappingType.getMappedJavaType().getTypeName() ) - && !entityMapping.getEntityPersister().isSubclassEntityName( entityMappingType.getEntityName() ) ) ) { + && !entityMappingType.getEntityPersister() + .isSubclassEntityName( partMappingType.getMappedJavaType().getTypeName() ) + && !entityMapping.getEntityPersister() + .isSubclassEntityName( entityMappingType.getEntityName() ) ) ) { referencedNavigablePath = referencedNavigablePath.getParent(); if ( referencedNavigablePath == null ) { return null; } - partMappingType = creationState.resolveModelPart( referencedNavigablePath ).getPartMappingType(); + partMappingType = + creationState.resolveModelPart( referencedNavigablePath ) + .getPartMappingType(); } return referencedNavigablePath; } @@ -1546,12 +1577,13 @@ public EntityFetch generateFetch( final var sqlAstCreationState = creationState.getSqlAstCreationState(); final var fromClauseAccess = sqlAstCreationState.getFromClauseAccess(); - final var parentTableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath() ); + final var fetchParentNavigablePath = fetchParent.getNavigablePath(); + final var parentTableGroup = fromClauseAccess.getTableGroup( fetchParentNavigablePath ); final var parentNavigablePath = fetchablePath.getParent(); - assert parentNavigablePath.equals( fetchParent.getNavigablePath() ) - || fetchParent.getNavigablePath() instanceof TreatedNavigablePath - && parentNavigablePath.equals( fetchParent.getNavigablePath().getRealParent() ); + assert parentNavigablePath.equals( fetchParentNavigablePath ) + || fetchParentNavigablePath instanceof TreatedNavigablePath + && parentNavigablePath.equals( fetchParentNavigablePath.getRealParent() ); /* In case of selected we are going to add a fetch for the `fetchablePath` only if there is not already a `TableGroupJoin`. @@ -1578,19 +1610,10 @@ public static class EntityB { having the left join we don't want to add an extra implicit join that will be translated into an SQL inner join (see HHH-15342) */ - final var resolvingKeySideOfForeignKey = creationState.getCurrentlyResolvingForeignKeyPart(); - final ForeignKeyDescriptor.Nature side; - if ( resolvingKeySideOfForeignKey == ForeignKeyDescriptor.Nature.KEY - && this.sideNature == ForeignKeyDescriptor.Nature.TARGET ) { - // If we are currently resolving the key part of a foreign key we do not want to add joins. - // So if the lhs of this association is the target of the FK, we have to use the KEY part to avoid a join - side = ForeignKeyDescriptor.Nature.KEY; - } - else { - side = this.sideNature; - } + final var side = getSide( creationState ); if ( fetchTiming == FetchTiming.IMMEDIATE && selected - || !creationState.getSqlAstCreationState().isProcedureOrNativeQuery() && needsJoinFetch( side ) ) { + || !creationState.getSqlAstCreationState().isProcedureOrNativeQuery() + && needsJoinFetch( side ) ) { final var tableGroup = determineTableGroupForFetch( fetchablePath, fetchParent, @@ -1668,31 +1691,8 @@ else if ( hasNotFoundAction() */ - final DomainResult keyResult; - if ( side == ForeignKeyDescriptor.Nature.KEY ) { - final var tableGroup = - sideNature == ForeignKeyDescriptor.Nature.KEY - ? createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ) - : parentTableGroup; - keyResult = foreignKeyDescriptor.createKeyDomainResult( - fetchablePath, - tableGroup, - fetchParent, - creationState - ); - } - else { - final var tableGroup = - sideNature == ForeignKeyDescriptor.Nature.TARGET - ? parentTableGroup - : createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ); - keyResult = foreignKeyDescriptor.createTargetDomainResult( - fetchablePath, - tableGroup, - fetchParent, - creationState - ); - } + final var keyResult = + getKeyResult( fetchParent, fetchablePath, creationState, side, parentTableGroup ); final boolean selectByUniqueKey = isSelectByUniqueKey( side ); if ( needsImmediateFetch( fetchTiming ) ) { @@ -1721,19 +1721,68 @@ else if ( hasNotFoundAction() ); } + private DomainResult getKeyResult( + FetchParent fetchParent, + NavigablePath fetchablePath, + DomainResultCreationState creationState, + ForeignKeyDescriptor.Nature side, + TableGroup parentTableGroup) { + if ( side == ForeignKeyDescriptor.Nature.KEY ) { + final var tableGroup = + sideNature == ForeignKeyDescriptor.Nature.KEY + ? createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ) + : parentTableGroup; + return foreignKeyDescriptor.createKeyDomainResult( + fetchablePath, + tableGroup, + fetchParent, + creationState + ); + } + else { + final var tableGroup = + sideNature == ForeignKeyDescriptor.Nature.TARGET + ? parentTableGroup + : createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ); + return foreignKeyDescriptor.createTargetDomainResult( + fetchablePath, + tableGroup, + fetchParent, + creationState + ); + } + } + + private ForeignKeyDescriptor.Nature getSide(DomainResultCreationState creationState) { + final var resolvingKeySideOfForeignKey = creationState.getCurrentlyResolvingForeignKeyPart(); + if ( resolvingKeySideOfForeignKey == ForeignKeyDescriptor.Nature.KEY + && sideNature == ForeignKeyDescriptor.Nature.TARGET ) { + // If we are currently resolving the key part of a foreign key we do not want to add joins. + // So if the lhs of this association is the target of the FK, we have to use the KEY part to avoid a join + return ForeignKeyDescriptor.Nature.KEY; + } + else { + return sideNature; + } + } + private boolean needsJoinFetch(ForeignKeyDescriptor.Nature side) { if ( side == ForeignKeyDescriptor.Nature.TARGET ) { - // With composite identifier if the target model part doesn't correspond to the identifier of the target entity mapping - // we must eagerly fetch with a join (subselect would still cause problems). + // With composite identifier if the target model part doesn't correspond + // to the identifier of the target entity mapping, we must eagerly fetch + // with a join (subselect would still cause problems). final var identifier = entityMappingType.getIdentifierMapping(); if ( identifier instanceof BasicEntityIdentifierMappingImpl ) { return false; } - final var targetPart = foreignKeyDescriptor.getTargetPart(); - if ( identifier != targetPart ) { - // If the identifier and the target part of the same class, we can preserve laziness as deferred loading will still work - return identifier.getExpressibleJavaType().getJavaTypeClass() != targetPart.getExpressibleJavaType() - .getJavaTypeClass(); + else { + final var targetPart = foreignKeyDescriptor.getTargetPart(); + if ( identifier != targetPart ) { + // If the identifier and the target part of the same class, + // we can preserve laziness as deferred loading will still work + return identifier.getExpressibleJavaType().getJavaTypeClass() + != targetPart.getExpressibleJavaType().getJavaTypeClass(); + } } } @@ -1758,7 +1807,7 @@ else if ( !entityMappingType.isConcreteProxy() ) { return hasNotFoundAction() || entityMappingType.getSoftDeleteMapping() != null || ( !entityMappingType.getEntityPersister().isInstrumented() - && cardinality == Cardinality.ONE_TO_ONE && isOptional ); + && cardinality == ONE_TO_ONE && isOptional ); } else { return false; @@ -1865,17 +1914,19 @@ private boolean isSelectByUniqueKey(ForeignKeyDescriptor.Nature side) { if ( referencedPropertyName == null ) { return false; } + final var identifierMapping = entityMappingType.getIdentifierMapping(); if ( side == ForeignKeyDescriptor.Nature.KEY ) { // case 1.2 return !foreignKeyDescriptor.getNavigableRole() - .equals( entityMappingType.getIdentifierMapping().getNavigableRole() ); + .equals( identifierMapping.getNavigableRole() ); } else { // case 1.1 - // Make sure the entity identifier is not a target key property i.e. this really is a unique key mapping + // Make sure the entity identifier is not a target key property, + // i.e. this really is a unique key mapping return bidirectionalAttributePath != null - && ( !( entityMappingType.getIdentifierMapping() instanceof SingleAttributeIdentifierMapping ) - || !targetKeyPropertyNames.contains( entityMappingType.getIdentifierMapping().getAttributeName() ) ); + && !( identifierMapping instanceof SingleAttributeIdentifierMapping + && targetKeyPropertyNames.contains( identifierMapping.getAttributeName() ) ); } } @@ -1895,16 +1946,20 @@ public DomainResult createSnapshotDomainResult( ); } else { - return new NullDomainResult( foreignKeyDescriptor.getKeyPart().getJavaType() ); + final var javaType = foreignKeyDescriptor.getKeyPart().getJavaType(); + @SuppressWarnings("unchecked") + // Not safe, but that's the fault of the method signature + final var castJavaType = (JavaType) javaType; + return new NullDomainResult<>( castJavaType ); } } - public static class NullDomainResult implements DomainResult { - private final DomainResultAssembler resultAssembler; - private final JavaType resultJavaType; + public static class NullDomainResult implements DomainResult { + private final DomainResultAssembler resultAssembler; + private final JavaType resultJavaType; - public NullDomainResult(JavaType javaType) { - resultAssembler = new NullValueAssembler( javaType ); + public NullDomainResult(JavaType javaType) { + resultAssembler = new NullValueAssembler<>( javaType ); this.resultJavaType = javaType; } @@ -1914,8 +1969,8 @@ public String getResultVariable() { } @Override - public DomainResultAssembler createResultAssembler( - InitializerParent parent, + public DomainResultAssembler createResultAssembler( + InitializerParent parent, AssemblerCreationState creationState) { return resultAssembler; } @@ -1934,14 +1989,16 @@ public void collectValueIndexesToCache(BitSet valueIndexes) { private EntityFetch withRegisteredAssociationKeys( Supplier fetchCreator, DomainResultCreationState creationState) { - final boolean added = creationState.registerVisitedAssociationKey( foreignKeyDescriptor.getAssociationKey() ); + final boolean added = + creationState.registerVisitedAssociationKey( + foreignKeyDescriptor.getAssociationKey() ); AssociationKey additionalAssociationKey = null; - if ( cardinality == Cardinality.LOGICAL_ONE_TO_ONE && bidirectionalAttributePath != null ) { - final var bidirectionalModelPart = entityMappingType.findByPath( bidirectionalAttributePath ); + if ( cardinality == LOGICAL_ONE_TO_ONE && bidirectionalAttributePath != null ) { // Add the inverse association key side as well to be able to resolve to a CircularFetch - if ( bidirectionalModelPart instanceof ToOneAttributeMapping bidirectionalAttribute ) { - assert bidirectionalModelPart.getPartMappingType() == declaringTableGroupProducer; - final AssociationKey secondKey = bidirectionalAttribute.getForeignKeyDescriptor().getAssociationKey(); + if ( entityMappingType.findByPath( bidirectionalAttributePath ) + instanceof ToOneAttributeMapping bidirectionalAttribute ) { + assert bidirectionalAttribute.getPartMappingType() == declaringTableGroupProducer; + final var secondKey = bidirectionalAttribute.getForeignKeyDescriptor().getAssociationKey(); if ( creationState.registerVisitedAssociationKey( secondKey ) ) { additionalAssociationKey = secondKey; } @@ -1953,7 +2010,8 @@ private EntityFetch withRegisteredAssociationKeys( } finally { if ( added ) { - creationState.removeVisitedAssociationKey( foreignKeyDescriptor.getAssociationKey() ); + creationState.removeVisitedAssociationKey( + foreignKeyDescriptor.getAssociationKey() ); } if ( additionalAssociationKey != null ) { creationState.removeVisitedAssociationKey( additionalAssociationKey ); @@ -1987,7 +2045,8 @@ else if ( parentTableGroup.getModelPart() instanceof CollectionPart ) { public boolean isSimpleJoinPredicate(Predicate predicate) { // Since the table group is lazy, the initial predicate is null, // but if we get null here, we can safely assume this will be a simple join predicate - return predicate == null || foreignKeyDescriptor.isSimpleJoinPredicate( predicate ); + return predicate == null + || foreignKeyDescriptor.isSimpleJoinPredicate( predicate ); } @Override @@ -2024,7 +2083,8 @@ public TableGroupJoin createTableGroupJoin( // If a parent is a collection part, there is no custom predicate and the join is INNER or LEFT // we check if this attribute is the map key property to reuse the existing index table group - if ( !addsPredicate && ( joinType == SqlAstJoinType.INNER || joinType == SqlAstJoinType.LEFT ) ) { + if ( !addsPredicate + && ( joinType == SqlAstJoinType.INNER || joinType == SqlAstJoinType.LEFT ) ) { TableGroup parentTableGroup = lhs; ModelPartContainer parentContainer = lhs.getModelPart(); StringBuilder embeddablePathSb = null; @@ -2039,7 +2099,7 @@ public TableGroupJoin createTableGroupJoin( final var tableGroup = fromClauseAccess.findTableGroup( parentNavigablePath.getParent() ); if ( tableGroup == null ) { assert parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.PART_NAME ) - || parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.TARGET_PART_NAME ); + || parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.TARGET_PART_NAME ); // Might happen that we don't register a table group for the collection role if this is a // foreign key part and the collection is delayed. We can just break out in this case though, // since these checks here are only for reusing a map key property, which we won't have @@ -2054,8 +2114,11 @@ public TableGroupJoin createTableGroupJoin( } if ( CollectionPart.Nature.ELEMENT.getName().equals( parentTableGroup.getNavigablePath().getLocalName() ) ) { - final var parentParentPath = parentTableGroup.getNavigablePath().getParent(); - final var pluralTableGroup = (PluralTableGroup) fromClauseAccess.findTableGroup( parentParentPath ); + final var parentParentPath = + parentTableGroup.getNavigablePath().getParent(); + final var pluralTableGroup = + (PluralTableGroup) + fromClauseAccess.findTableGroup( parentParentPath ); if ( pluralTableGroup != null ) { final String indexPropertyName = pluralTableGroup.getModelPart().getIndexMetadata() @@ -2078,8 +2141,7 @@ public TableGroupJoin createTableGroupJoin( fetched, pluralTableGroup, this - ), - null + ) ); } } @@ -2099,13 +2161,14 @@ public TableGroupJoin createTableGroupJoin( final var join = new TableGroupJoin( navigablePath, - // Avoid checking for nested joins in here again, since this is already done in createRootTableGroupJoin - // and simply rely on the canUseInnerJoins flag instead for override the join type to LEFT + // Avoid checking for nested joins in here again, + // since this is already done in createRootTableGroupJoin + // and simply rely on the canUseInnerJoins flag instead + // for override the join type to LEFT requestedJoinType == null && !lazyTableGroup.canUseInnerJoins() ? SqlAstJoinType.LEFT : joinType, - lazyTableGroup, - null + lazyTableGroup ); final var lhsTableReference = lhs.resolveTableReference( navigablePath, @@ -2160,10 +2223,9 @@ public TableGroupJoin createTableGroupJoin( final var softDeleteMapping = associatedEntityMappingType.getSoftDeleteMapping(); if ( softDeleteMapping != null ) { // add the restriction - final TableReference tableReference = lazyTableGroup.resolveTableReference( - navigablePath, - associatedEntityMappingType.getSoftDeleteTableDetails().getTableName() - ); + final var tableReference = + lazyTableGroup.resolveTableReference( navigablePath, + associatedEntityMappingType.getSoftDeleteTableDetails().getTableName() ); join.applyPredicate( softDeleteMapping.createNonDeletedRestriction( tableReference, creationState.getSqlExpressionResolver() @@ -2313,7 +2375,8 @@ public boolean canUseParentTableGroup( } private void initializeIfNeeded(TableGroup lhs, SqlAstJoinType sqlAstJoinType, TableGroup tableGroup) { - if ( sqlAstJoinType == SqlAstJoinType.INNER && ( isNullable || !lhs.canUseInnerJoins() ) ) { + if ( sqlAstJoinType == SqlAstJoinType.INNER + && ( isNullable || !lhs.canUseInnerJoins() ) ) { if ( hasJoinTable ) { // Set the join type of the table reference join to INNER to retain cardinality expectation final var lhsTableReference = @@ -2446,7 +2509,7 @@ public int breakDownJdbcValues( Y y, JdbcValueBiConsumer valueConsumer, SharedSessionContractImplementor session) { - if ( cardinality == Cardinality.ONE_TO_ONE && sideNature == ForeignKeyDescriptor.Nature.TARGET ) { + if ( cardinality == ONE_TO_ONE && sideNature == ForeignKeyDescriptor.Nature.TARGET ) { return 0; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 0b7d8ed24318..ccedc7ee2b5a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -11,6 +11,7 @@ import org.hibernate.HibernateException; import org.hibernate.Internal; import org.hibernate.LockMode; +import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.function.TimestampaddFunction; import org.hibernate.dialect.function.TimestampdiffFunction; @@ -692,19 +693,24 @@ public TypeConfiguration getTypeConfiguration() { return creationContext.getTypeConfiguration(); } + private SessionFactoryOptions getSessionFactoryOptions() { + // TODO : FIX ME + return creationContext.getSessionFactory().getSessionFactoryOptions(); + } + @Override public int getPreferredSqlTypeCodeForBoolean() { - return creationContext.getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForBoolean(); + return getSessionFactoryOptions().getPreferredSqlTypeCodeForBoolean(); } @Override public int getPreferredSqlTypeCodeForDuration() { - return creationContext.getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForDuration(); + return getSessionFactoryOptions().getPreferredSqlTypeCodeForDuration(); } @Override public int getPreferredSqlTypeCodeForUuid() { - return creationContext.getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForUuid(); + return getSessionFactoryOptions().getPreferredSqlTypeCodeForUuid(); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -3590,8 +3596,7 @@ private TableGroup consumeEntityJoin(SqmEntityJoin sqmJoin, TableGroup lhsT final TableGroupJoin tableGroupJoin = new TableGroupJoin( sqmJoin.getNavigablePath(), correspondingSqlJoinType, - tableGroup, - null + tableGroup ); lhsTableGroup.addTableGroupJoin( tableGroupJoin ); @@ -3670,8 +3675,7 @@ private TableGroup consumeDerivedJoin(SqmDerivedJoin sqmJoin, TableGroup pare final TableGroupJoin tableGroupJoin = new TableGroupJoin( queryPartTableGroup.getNavigablePath(), sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(), - queryPartTableGroup, - null + queryPartTableGroup ); parentTableGroup.addTableGroupJoin( tableGroupJoin ); @@ -3705,8 +3709,7 @@ private TableGroup consumeFunctionJoin(SqmFunctionJoin sqmJoin, TableGroup pa final TableGroupJoin tableGroupJoin = new TableGroupJoin( tableGroup.getNavigablePath(), correspondingSqlJoinType, - tableGroup, - null + tableGroup ); parentTableGroup.addTableGroupJoin( tableGroupJoin ); @@ -3738,8 +3741,7 @@ private TableGroup consumeCteJoin(SqmCteJoin sqmJoin, TableGroup parentTableG final TableGroupJoin tableGroupJoin = new TableGroupJoin( tableGroup.getNavigablePath(), correspondingSqlJoinType, - tableGroup, - null + tableGroup ); parentTableGroup.addTableGroupJoin( tableGroupJoin ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleEntityValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleEntityValuedModelPart.java index 1cd2febb7912..ea70a04ab1eb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleEntityValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleEntityValuedModelPart.java @@ -262,8 +262,7 @@ public TableGroupJoin createTableGroupJoin( final TableGroupJoin tableGroupJoin = new TableGroupJoin( navigablePath, joinType, - lazyTableGroup, - null + lazyTableGroup ); lazyTableGroup.setTableGroupInitializerCallback( createTableGroupInitializerCallback( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java index ef6cbd3c3390..6bf007147ce6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java @@ -156,7 +156,7 @@ public List getTableReferenceJoins() { } @Override - public DomainResult createDomainResult( + public DomainResult createDomainResult( String resultVariable, DomainResultCreationState creationState) { return getTableGroup().createDomainResult( resultVariable, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java index 91a13bc92d63..f4dfc7e6fd64 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java @@ -152,7 +152,7 @@ public void visitNestedTableGroupJoins(Consumer consumer) { } @Override - public DomainResult createDomainResult(String resultVariable, DomainResultCreationState creationState) { + public DomainResult createDomainResult(String resultVariable, DomainResultCreationState creationState) { return elementTableGroup.createDomainResult( resultVariable, creationState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java index 0f773ea06052..22930453a14b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java @@ -100,7 +100,7 @@ public boolean isImplicit() { } @Override - public DomainResult createDomainResult( + public DomainResult createDomainResult( String resultVariable, DomainResultCreationState creationState) { return getJoinedGroup().createDomainResult( resultVariable, creationState );