From f207c61a1c9e51c17b30c15ceebb7baee1287076 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 25 Nov 2025 13:42:15 +0100 Subject: [PATCH] more refactoring to ModelBinder --- .../hbm/AbstractEntitySourceImpl.java | 15 +- .../internal/hbm/EntityNamingSourceImpl.java | 10 +- .../source/internal/hbm/ModelBinder.java | 897 ++++++++++-------- 3 files changed, 500 insertions(+), 422 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/AbstractEntitySourceImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/AbstractEntitySourceImpl.java index b8cc59f58235..2c2addd58c71 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/AbstractEntitySourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/AbstractEntitySourceImpl.java @@ -41,6 +41,8 @@ import org.hibernate.internal.util.collections.CollectionHelper; import static java.util.Collections.emptyMap; +import static org.hibernate.internal.util.StringHelper.isNotEmpty; +import static org.hibernate.internal.util.StringHelper.unqualify; /** * @author Steve Ebersole @@ -97,19 +99,20 @@ protected AbstractEntitySourceImpl(MappingDocument sourceMappingDocument, JaxbHb ); } - public static EntityNamingSourceImpl extractEntityNamingSource( + static EntityNamingSourceImpl extractEntityNamingSource( MappingDocument sourceMappingDocument, EntityInfo jaxbEntityMapping) { final String className = sourceMappingDocument.qualifyClassName( jaxbEntityMapping.getName() ); + final String mappingEntityName = jaxbEntityMapping.getEntityName(); final String entityName; final String jpaEntityName; - if ( StringHelper.isNotEmpty( jaxbEntityMapping.getEntityName() ) ) { - entityName = jaxbEntityMapping.getEntityName(); - jpaEntityName = jaxbEntityMapping.getEntityName(); + if ( isNotEmpty( mappingEntityName ) ) { + entityName = mappingEntityName; + jpaEntityName = mappingEntityName; } else { entityName = className; - jpaEntityName = StringHelper.unqualify( className ); + jpaEntityName = unqualify( className ); } return new EntityNamingSourceImpl( entityName, className, jpaEntityName ); } @@ -122,7 +125,7 @@ private FilterSource[] buildFilterSources() { return NO_FILTER_SOURCES; } - FilterSource[] results = new FilterSource[size]; + final var results = new FilterSource[size]; for ( int i = 0; i < size; i++ ) { JaxbHbmFilterType element = jaxbClassElement.getFilter().get( i ); results[i] = new FilterSourceImpl( sourceMappingDocument(), element ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/EntityNamingSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/EntityNamingSourceImpl.java index 68bc41743838..dd9cc77701be 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/EntityNamingSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/EntityNamingSourceImpl.java @@ -5,9 +5,10 @@ package org.hibernate.boot.model.source.internal.hbm; import org.hibernate.boot.model.source.spi.EntityNamingSource; -import org.hibernate.internal.util.StringHelper; import org.hibernate.mapping.PersistentClass; +import static org.hibernate.internal.util.StringHelper.isNotEmpty; + /** * Implementation of EntityNamingSource * @@ -24,12 +25,13 @@ public EntityNamingSourceImpl(String entityName, String className, String jpaEnt this.entityName = entityName; this.className = className; this.jpaEntityName = jpaEntityName; - - this.typeName = StringHelper.isNotEmpty( className ) ? className : entityName; + this.typeName = isNotEmpty( className ) ? className : entityName; } public EntityNamingSourceImpl(PersistentClass entityBinding) { - this( entityBinding.getEntityName(), entityBinding.getClassName(), entityBinding.getJpaEntityName() ); + this( entityBinding.getEntityName(), + entityBinding.getClassName(), + entityBinding.getJpaEntityName() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java index 4277507b3062..9cdaa0178438 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java @@ -45,6 +45,7 @@ import org.hibernate.boot.spi.NaturalIdUniqueKeyBinder; import org.hibernate.boot.spi.SecondPass; import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.dialect.Dialect; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.generator.internal.GeneratedGeneration; @@ -80,6 +81,7 @@ import org.hibernate.mapping.SingleTableSubclass; import org.hibernate.mapping.SortableValue; import org.hibernate.mapping.Table; +import org.hibernate.mapping.ToOne; import org.hibernate.mapping.UnionSubclass; import org.hibernate.mapping.UniqueKey; import org.hibernate.mapping.Value; @@ -470,7 +472,7 @@ private void bindJoinedSubclassEntity( keyBinding.makeNationalized(); } entityDescriptor.setKey( keyBinding ); - keyBinding.setOnDeleteAction( getOnDeleteAction( entitySource.isCascadeDeleteEnabled() ) ); + keyBinding.setOnDeleteAction( getOnDeleteAction( entitySource ) ); relationalObjectBinder.bindColumns( mappingDocument, entitySource.getPrimaryKeyColumnSources(), @@ -486,7 +488,8 @@ public Identifier determineImplicitName(LocalMetadataBuildingContext context) { } ); keyBinding.sortProperties(); - setForeignKeyName( keyBinding, entitySource.getExplicitForeignKeyName() ); + setForeignKeyName( keyBinding, + entitySource.getExplicitForeignKeyName() ); // model.getKey().setType( new Type( model.getIdentifier() ) ); entityDescriptor.createPrimaryKey(); entityDescriptor.createForeignKey(); @@ -1209,9 +1212,10 @@ private void bindCollectionMetadata(MappingDocument mappingDocument, PluralAttri binding.setTypeName( typeName ); binding.setTypeParameters( typeParameters ); - if ( source.getFetchCharacteristics().getFetchTiming() == FetchTiming.DELAYED ) { + final var fetchCharacteristics = source.getFetchCharacteristics(); + if ( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ) { binding.setLazy( true ); - binding.setExtraLazy( source.getFetchCharacteristics().isExtraLazy() ); + binding.setExtraLazy( fetchCharacteristics.isExtraLazy() ); } else { binding.setLazy( false ); @@ -1293,7 +1297,8 @@ private static void bindCustomSql(PluralAttributeSource source, Collection bindi } private static void setupFetching(PluralAttributeSource source, Collection binding) { - switch ( source.getFetchCharacteristics().getFetchStyle() ) { + final var fetchCharacteristics = source.getFetchCharacteristics(); + switch ( fetchCharacteristics.getFetchStyle() ) { case SELECT: binding.setFetchMode( FetchMode.SELECT ); break; @@ -1302,7 +1307,7 @@ private static void setupFetching(PluralAttributeSource source, Collection bindi break; case BATCH: binding.setFetchMode( FetchMode.SELECT ); - binding.setBatchSize( source.getFetchCharacteristics().getBatchSize() ); + binding.setBatchSize( fetchCharacteristics.getBatchSize() ); break; case SUBSELECT: binding.setFetchMode( FetchMode.SELECT ); @@ -1312,7 +1317,7 @@ private static void setupFetching(PluralAttributeSource source, Collection bindi break; default: throw new AssertionFailure( "Unexpected FetchStyle : " - + source.getFetchCharacteristics().getFetchStyle().name() ); + + fetchCharacteristics.getFetchStyle().name() ); } } @@ -1385,7 +1390,8 @@ private Identifier determineTable( for ( var relationalValueSource : relationalValueSources ) { // We need to get the containing table name for both columns and formulas, // particularly when a column/formula is for a property on a secondary table. - if ( !Objects.equals( tableName, relationalValueSource.getContainingTableName() ) ) { + final String containingTableName = relationalValueSource.getContainingTableName(); + if ( !Objects.equals( tableName, containingTableName ) ) { if ( tableName != null ) { throw new MappingException( String.format( @@ -1393,12 +1399,12 @@ private Identifier determineTable( "Attribute [%s] referenced columns from multiple tables: %s, %s", attributeName, tableName, - relationalValueSource.getContainingTableName() + containingTableName ), mappingDocument.getOrigin() ); } - tableName = relationalValueSource.getContainingTableName(); + tableName = containingTableName; } } return database.toIdentifier( tableName ); @@ -1429,8 +1435,7 @@ private void bindSecondaryTable( } secondaryTable.setComment( tableSource.getComment() ); } - else { - final var inLineViewSource = (InLineViewSource) tableSpecificationSource; + else if ( tableSpecificationSource instanceof InLineViewSource inLineViewSource ) { logicalTableName = toIdentifier( inLineViewSource.getLogicalName() ); secondaryTable = new Table( metadataBuildingContext.getCurrentContributorName(), @@ -1439,6 +1444,9 @@ private void bindSecondaryTable( false ); } + else { + throw new AssertionFailure( "Unexpected table specification soure type" ); + } secondaryTableJoin.setTable( secondaryTable ); entityTableXref.addSecondaryTable( mappingDocument, logicalTableName, secondaryTableJoin ); @@ -1456,13 +1464,14 @@ private void bindSecondaryTable( } final var keyBinding = - new DependantValue( mappingDocument, secondaryTable, persistentClass.getIdentifier() ); + new DependantValue( mappingDocument, secondaryTable, + persistentClass.getIdentifier() ); if ( mappingDocument.getBuildingOptions().useNationalizedCharacterData() ) { keyBinding.makeNationalized(); } secondaryTableJoin.setKey( keyBinding ); - keyBinding.setOnDeleteAction( getOnDeleteAction( secondaryTableSource.isCascadeDeleteEnabled() ) ); + keyBinding.setOnDeleteAction( getOnDeleteAction( secondaryTableSource ) ); // NOTE: no Type info to bind... @@ -1482,7 +1491,8 @@ public Identifier determineImplicitName(LocalMetadataBuildingContext context) { ); keyBinding.sortProperties(); - setForeignKeyName( keyBinding, secondaryTableSource.getExplicitForeignKeyName() ); + setForeignKeyName( keyBinding, + secondaryTableSource.getExplicitForeignKeyName() ); // skip creating primary and foreign keys for a subselect. if ( secondaryTable.getSubselect() == null ) { @@ -1633,15 +1643,8 @@ private Property createOneToOneAttribute( oneToOneSource.getAttributeRole() ); - final String propertyRef = oneToOneBinding.getReferencedPropertyName(); - if ( propertyRef != null ) { - handlePropertyReference( - sourceDocument, - oneToOneBinding.getReferencedEntityName(), - propertyRef, - "" - ); - } + handlePropertyRef( sourceDocument, oneToOneBinding, + "" ); // Defer the creation of the foreign key as we need the // associated entity persister to be initialized so that @@ -1656,25 +1659,36 @@ private Property createOneToOneAttribute( return property; } + private void handlePropertyRef( + MappingDocument sourceDocument, + ToOne binding, + String synopsis) { + final String propertyRef = binding.getReferencedPropertyName(); + if ( propertyRef != null ) { + handlePropertyReference( + sourceDocument, + binding.getReferencedEntityName(), + propertyRef, + synopsis + ); + } + } + private void handlePropertyReference( MappingDocument mappingDocument, String referencedEntityName, String referencedPropertyName, String sourceElementSynopsis) { - final var entityBinding = - mappingDocument.getMetadataCollector() - .getEntityBinding( referencedEntityName ); + final var collector = mappingDocument.getMetadataCollector(); + final var entityBinding = collector.getEntityBinding( referencedEntityName ); if ( entityBinding == null ) { // entity may just not have been processed yet - set up a delayed handler registerDelayedPropertyReferenceHandler( - new DelayedPropertyReferenceHandlerImpl( - referencedEntityName, - referencedPropertyName, - true, - sourceElementSynopsis, - mappingDocument.getOrigin() - ), - mappingDocument + mappingDocument, + referencedEntityName, + referencedPropertyName, + sourceElementSynopsis, + collector ); } else { @@ -1683,14 +1697,11 @@ private void handlePropertyReference( if ( propertyBinding == null ) { // attribute may just not have been processed yet - set up a delayed handler registerDelayedPropertyReferenceHandler( - new DelayedPropertyReferenceHandlerImpl( - referencedEntityName, - referencedPropertyName, - true, - sourceElementSynopsis, - mappingDocument.getOrigin() - ), - mappingDocument + mappingDocument, + referencedEntityName, + referencedPropertyName, + sourceElementSynopsis, + collector ); } else { @@ -1706,15 +1717,26 @@ private void handlePropertyReference( } private void registerDelayedPropertyReferenceHandler( - DelayedPropertyReferenceHandlerImpl handler, - MetadataBuildingContext buildingContext) { + MappingDocument mappingDocument, + String referencedEntityName, + String referencedPropertyName, + String sourceElementSynopsis, + InFlightMetadataCollector collector) { + final var handler = + new DelayedPropertyReferenceHandlerImpl( + referencedEntityName, + referencedPropertyName, + true, + sourceElementSynopsis, + mappingDocument.getOrigin() + ); BOOT_LOGGER.tracef( "Property [%s.%s] referenced by property-ref [%s] was not yet available - creating delayed handler", handler.referencedEntityName, handler.referencedPropertyName, handler.sourceElementSynopsis ); - buildingContext.getMetadataCollector().addDelayedPropertyReferenceHandler( handler ); + collector.addDelayedPropertyReferenceHandler( handler ); } public void bindOneToOne( @@ -1729,19 +1751,8 @@ public void bindOneToOne( oneToOneBinding ); - if ( oneToOneSource.isConstrained() ) { - final String cascadeStyleName = oneToOneSource.getCascadeStyleName(); - if ( cascadeStyleName != null && cascadeStyleName.contains( "delete-orphan" ) ) { - throw new MappingException( - String.format( - Locale.ENGLISH, - "one-to-one attribute [%s] cannot specify orphan delete cascading as it is constrained", - oneToOneSource.getAttributeRole().getFullPath() - ), - sourceDocument.getOrigin() - ); - } + checkConstrainedOneToOneOrphanDelete( sourceDocument, oneToOneSource ); oneToOneBinding.setConstrained( true ); oneToOneBinding.setForeignKeyType( ForeignKeyDirection.FROM_PARENT ); } @@ -1749,15 +1760,7 @@ public void bindOneToOne( oneToOneBinding.setForeignKeyType( ForeignKeyDirection.TO_PARENT ); } - final var fetchCharacteristics = oneToOneSource.getFetchCharacteristics(); - oneToOneBinding.setLazy( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ); - oneToOneBinding.setFetchMode( - fetchCharacteristics.getFetchStyle() == FetchStyle.SELECT - ? FetchMode.SELECT - : FetchMode.JOIN - ); - oneToOneBinding.setUnwrapProxy( fetchCharacteristics.isUnwrapProxies() ); - + handleFetchCharacteristics( oneToOneSource, oneToOneBinding ); final String referencedEntityAttributeName = oneToOneSource.getReferencedEntityAttributeName(); if ( isNotEmpty( referencedEntityAttributeName ) ) { @@ -1775,11 +1778,21 @@ public void bindOneToOne( DEPRECATION_LOGGER.logDeprecationOfEmbedXmlSupport(); } - if ( isNotEmpty( oneToOneSource.getExplicitForeignKeyName() ) ) { - setForeignKeyName( oneToOneBinding, oneToOneSource.getExplicitForeignKeyName() ); - } + setForeignKeyName( oneToOneBinding, + oneToOneSource.getExplicitForeignKeyName() ); - oneToOneBinding.setOnDeleteAction( getOnDeleteAction( oneToOneSource.isCascadeDeleteEnabled() ) ); + oneToOneBinding.setOnDeleteAction( getOnDeleteAction( oneToOneSource ) ); + } + + private static void handleFetchCharacteristics(SingularAttributeSourceToOne toOneSource, ToOne toOneBinding) { + final var fetchCharacteristics = toOneSource.getFetchCharacteristics(); + toOneBinding.setLazy( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ); + toOneBinding.setFetchMode( + fetchCharacteristics.getFetchStyle() == FetchStyle.SELECT + ? FetchMode.SELECT + : FetchMode.JOIN + ); + toOneBinding.setUnwrapProxy( fetchCharacteristics.isUnwrapProxies() ); } private Property createManyToOneAttribute( @@ -1787,18 +1800,42 @@ private Property createManyToOneAttribute( SingularAttributeSourceManyToOne manyToOneSource, ManyToOne manyToOneBinding, String containingClassName) { - final String attributeName = manyToOneSource.getName(); + final String referencedEntityName = + handleReferencedEntity( sourceDocument, manyToOneSource, manyToOneBinding, containingClassName ); + + if ( manyToOneSource.isUnique() ) { + manyToOneBinding.markAsLogicalOneToOne(); + } + bindManyToOneAttribute( sourceDocument, manyToOneSource, manyToOneBinding, referencedEntityName ); + + handlePropertyRef( sourceDocument, manyToOneBinding, + "" ); + + final var property = new Property(); + property.setValue( manyToOneBinding ); + bindProperty( sourceDocument, manyToOneSource, property ); + + checkManyToOneOrphanDelete( sourceDocument, manyToOneSource, manyToOneBinding ); + + return property; + } + + private String handleReferencedEntity( + MappingDocument sourceDocument, + SingularAttributeSourceManyToOne manyToOneSource, + ManyToOne manyToOneBinding, + String containingClassName) { final String explicitReferencedEntityName = manyToOneSource.getReferencedEntityName(); - final String referencedEntityName; if ( explicitReferencedEntityName != null ) { - referencedEntityName = explicitReferencedEntityName; + return explicitReferencedEntityName; } else { + final String attributeName = manyToOneSource.getName(); final var reflectedPropertyClass = reflectedPropertyClass( sourceDocument, containingClassName, attributeName ); if ( reflectedPropertyClass != null ) { - referencedEntityName = reflectedPropertyClass.getName(); + return reflectedPropertyClass.getName(); } else { prepareValueTypeViaReflection( @@ -1808,57 +1845,54 @@ private Property createManyToOneAttribute( attributeName, manyToOneSource.getAttributeRole() ); - referencedEntityName = manyToOneBinding.getTypeName(); + return manyToOneBinding.getTypeName(); } } + } - if ( manyToOneSource.isUnique() ) { - manyToOneBinding.markAsLogicalOneToOne(); - } - - bindManyToOneAttribute( sourceDocument, manyToOneSource, manyToOneBinding, referencedEntityName ); - - final String propertyRef = manyToOneBinding.getReferencedPropertyName(); - if ( propertyRef != null ) { - handlePropertyReference( - sourceDocument, - manyToOneBinding.getReferencedEntityName(), - propertyRef, - "" + private static void checkManyToOneOrphanDelete( + MappingDocument sourceDocument, + SingularAttributeSourceManyToOne manyToOneSource, + ManyToOne manyToOneBinding) { + // TODO: would be better to delay this until the end of binding (second pass, etc) + // in order to properly allow for a singular unique column for a many-to-one to + // to also trigger a "logical one-to-one". As is, this can occasionally lead to + // false exceptions if the many-to-one column binding is delayed and the + // uniqueness is indicated on the rather than on the + // + // Ideally, would love to see a SimpleValue#validate approach, rather than a + // SimpleValue#isValid that is then handled at a higher level (Property, etc). + // The reason being that the current approach misses the exact reason a + // "validation" fails since it loses "context" + final String cascadeStyleName = manyToOneSource.getCascadeStyleName(); + if ( cascadeStyleName != null && cascadeStyleName.contains( "delete-orphan" ) + && !manyToOneBinding.isLogicalOneToOne() ) { + throw new MappingException( + String.format( + Locale.ENGLISH, + "many-to-one attribute [%s] specified delete-orphan but is not specified as unique; " + + "remove delete-orphan cascading or specify unique=\"true\"", + manyToOneSource.getAttributeRole().getFullPath() + ), + sourceDocument.getOrigin() ); } + } - final var property = new Property(); - property.setValue( manyToOneBinding ); - bindProperty( sourceDocument, manyToOneSource, property ); - - if ( isNotEmpty( manyToOneSource.getCascadeStyleName() ) ) { - // todo : would be better to delay this the end of binding (second pass, etc) - // in order to properly allow for a singular unique column for a many-to-one to - // also trigger a "logical one-to-one". As-is, this can occasionally lead to - // false exceptions if the many-to-one column binding is delayed and the - // uniqueness is indicated on the rather than on the - // - // Ideally, would love to see a SimpleValue#validate approach, rather than a - // SimpleValue#isValid that is then handled at a higher level (Property, etc). - // The reason being that the current approach misses the exact reason - // a "validation" fails since it loses "context" - if ( manyToOneSource.getCascadeStyleName().contains( "delete-orphan" ) ) { - if ( !manyToOneBinding.isLogicalOneToOne() ) { - throw new MappingException( - String.format( - Locale.ENGLISH, - "many-to-one attribute [%s] specified delete-orphan but is not specified as unique; " + - "remove delete-orphan cascading or specify unique=\"true\"", - manyToOneSource.getAttributeRole().getFullPath() - ), - sourceDocument.getOrigin() - ); - } - } + private static void checkConstrainedOneToOneOrphanDelete( + MappingDocument sourceDocument, + SingularAttributeSourceOneToOne oneToOneSource) { + final String cascadeStyleName = oneToOneSource.getCascadeStyleName(); + if ( cascadeStyleName != null && cascadeStyleName.contains( "delete-orphan" ) ) { + throw new MappingException( + String.format( + Locale.ENGLISH, + "one-to-one attribute [%s] cannot specify orphan delete cascading as it is constrained", + oneToOneSource.getAttributeRole().getFullPath() + ), + sourceDocument.getOrigin() + ); } - - return property; } private void bindManyToOneAttribute( @@ -1868,24 +1902,10 @@ private void bindManyToOneAttribute( String referencedEntityName) { // NOTE: no type information to bind - manyToOneBinding.setReferencedEntityName( referencedEntityName ); - final String referencedEntityAttributeName = manyToOneSource.getReferencedEntityAttributeName(); - if ( isNotEmpty( referencedEntityAttributeName ) ) { - manyToOneBinding.setReferencedPropertyName( referencedEntityAttributeName ); - manyToOneBinding.setReferenceToPrimaryKey( false ); - } - else { - manyToOneBinding.setReferenceToPrimaryKey( true ); - } + handleReferencedEntity( manyToOneBinding, referencedEntityName, + manyToOneSource.getReferencedEntityAttributeName() ); - final var fetchCharacteristics = manyToOneSource.getFetchCharacteristics(); - manyToOneBinding.setLazy( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ); - manyToOneBinding.setUnwrapProxy( fetchCharacteristics.isUnwrapProxies() ); - manyToOneBinding.setFetchMode( - fetchCharacteristics.getFetchStyle() == FetchStyle.SELECT - ? FetchMode.SELECT - : FetchMode.JOIN - ); + handleFetchCharacteristics( manyToOneSource, manyToOneBinding ); if ( manyToOneSource.isEmbedXml() == Boolean.TRUE ) { DEPRECATION_LOGGER.logDeprecationOfEmbedXmlSupport(); @@ -1893,7 +1913,8 @@ private void bindManyToOneAttribute( manyToOneBinding.setIgnoreNotFound( manyToOneSource.isIgnoreNotFound() ); - setForeignKeyName( manyToOneBinding, manyToOneSource.getExplicitForeignKeyName() ); + setForeignKeyName( manyToOneBinding, + manyToOneSource.getExplicitForeignKeyName() ); final var columnBinder = new ManyToOneColumnBinder( sourceDocument, @@ -1918,7 +1939,6 @@ private void bindManyToOneAttribute( manyToOneBinding, referencedEntityName ); - if ( canBindColumnsImmediately && fkSecondPass.canProcessImmediately() ) { fkSecondPass.doSecondPass( null ); } @@ -1927,7 +1947,19 @@ private void bindManyToOneAttribute( } } - manyToOneBinding.setOnDeleteAction( getOnDeleteAction( manyToOneSource.isCascadeDeleteEnabled() ) ); + manyToOneBinding.setOnDeleteAction( getOnDeleteAction( manyToOneSource ) ); + } + + private static void handleReferencedEntity( + ManyToOne manyToOneBinding, String referencedEntityName, String referencedEntityAttributeName) { + manyToOneBinding.setReferencedEntityName( referencedEntityName ); + if ( isNotEmpty( referencedEntityAttributeName ) ) { + manyToOneBinding.setReferencedPropertyName( referencedEntityAttributeName ); + manyToOneBinding.setReferenceToPrimaryKey( false ); + } + else { + manyToOneBinding.setReferenceToPrimaryKey( true ); + } } private static void setForeignKeyName(SimpleValue manyToOneBinding, String foreignKeyName) { @@ -1946,13 +1978,9 @@ private Property createAnyAssociationAttribute( SingularAttributeSourceAny anyMapping, Any anyBinding, String entityName) { - final var attributeRole = anyMapping.getAttributeRole(); - bindAny( sourceDocument, anyMapping, anyBinding, attributeRole ); - prepareValueTypeViaReflection( sourceDocument, anyBinding, entityName, anyMapping.getName(), attributeRole ); - anyBinding.createForeignKey(); final var property = new Property(); @@ -1999,9 +2027,7 @@ private void bindAny( anyMapping.getKeySource().getRelationalValueSources(), anyBinding.getKeyMapping(), true, - context -> implicitNamingStrategy.determineAnyKeyColumnName( - anyMapping.getKeySource() - ) + context -> implicitNamingStrategy.determineAnyKeyColumnName( anyMapping.getKeySource() ) ); } @@ -2111,11 +2137,8 @@ private BasicType resolveExplicitlyNamedAnyDiscriminatorType( } throw new org.hibernate.MappingException( - String.format( - Locale.ROOT, - "Unable to resolve explicit any-discriminator type name - %s", - typeName - ) + "Unable to resolve explicit any-discriminator type name:" + + typeName ); } } @@ -2141,7 +2164,8 @@ private void prepareValueTypeViaReflection( throw new MappingException( String.format( Locale.ENGLISH, - "Attribute mapping must define a name attribute: containingClassName=[%s], propertyName=[%s], role=[%s]", + "Attribute mapping must define a name attribute:" + + " containingClassName=[%s], propertyName=[%s], role=[%s]", containingClassName, propertyName, attributeRole.getFullPath() @@ -2157,7 +2181,8 @@ private void prepareValueTypeViaReflection( throw new MappingException( String.format( Locale.ENGLISH, - "Error calling Value#setTypeUsingReflection: containingClassName=[%s], propertyName=[%s], role=[%s]", + "Error calling Value#setTypeUsingReflection:" + + " containingClassName=[%s], propertyName=[%s], role=[%s]", containingClassName, propertyName, attributeRole.getFullPath() @@ -2194,13 +2219,15 @@ private void bindProperty( property.setUpdatable( singularAttributeSource.isUpdatable() ); // isBytecodeLazy() refers to whether a property is lazy via bytecode enhancement (not proxies) property.setLazy( singularAttributeSource.isBytecodeLazy() ); - handleGenerationTiming( mappingDocument, propertySource, property, singularAttributeSource.getGenerationTiming() ); + handleGenerationTiming( mappingDocument, propertySource, property, + singularAttributeSource.getGenerationTiming() ); } property.setMetaAttributes( propertySource.getToolingHintContext().getMetaAttributeMap() ); if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.trace( "Mapped property: " + propertySource.getName() + " -> [" + columns( property.getValue() ) + "]" ); + BOOT_LOGGER.trace( "Mapped property: " + propertySource.getName() + + " -> [" + columns( property.getValue() ) + "]" ); } } @@ -2362,8 +2389,9 @@ private static void bindVirtual(String role, Component componentBinding) { // virtual (what used to be called embedded) is just a conceptual composition... // for example if ( componentBinding.getOwner().hasPojoRepresentation() ) { - BOOT_LOGGER.bindingVirtualComponentToOwner( role, componentBinding.getOwner().getClassName() ); - componentBinding.setComponentClassName( componentBinding.getOwner().getClassName() ); + final String ownerClassName = componentBinding.getOwner().getClassName(); + BOOT_LOGGER.bindingVirtualComponentToOwner( role, ownerClassName ); + componentBinding.setComponentClassName( ownerClassName ); } else { BOOT_LOGGER.bindingVirtualComponentAsDynamic( role ); @@ -2552,8 +2580,9 @@ private static TypeResolution resolveType( else { // the explicit name referred to a type-def typeName = typeDefinition.getTypeImplementorClass().getName(); - if ( typeDefinition.getParameters() != null ) { - typeParameters.putAll( typeDefinition.getParameters() ); + final var parameters = typeDefinition.getParameters(); + if ( parameters != null ) { + typeParameters.putAll( parameters ); } } @@ -2573,12 +2602,10 @@ private Table bindEntityTableSpecification( final EntitySource entitySource, PersistentClass entityDescriptor) { final String contributorName = mappingDocument.getCurrentContributorName(); - final boolean isTable = tableSpecSource instanceof TableSource; final boolean isAbstract = entityDescriptor.isAbstract() != null && entityDescriptor.isAbstract(); final Identifier logicalTableName; final Table table; - if ( isTable ) { - final var tableSource = (TableSource) tableSpecSource; + if ( tableSpecSource instanceof TableSource tableSource ) { logicalTableName = logicalTableName( mappingDocument, entitySource, tableSource ); table = tableForEntity( tableSource, @@ -2588,8 +2615,7 @@ private Table bindEntityTableSpecification( isAbstract ); } - else { - final var inlineViewSource = (InLineViewSource) tableSpecSource; + else if ( tableSpecSource instanceof InLineViewSource inlineViewSource ) { logicalTableName = database.toIdentifier( inlineViewSource.getLogicalName() ); table = tableForSubselect( inlineViewSource, @@ -2599,6 +2625,9 @@ private Table bindEntityTableSpecification( logicalTableName ); } + else { + throw new AssertionFailure( "Unexpected table specification source type" ); + } final var metadataCollector = mappingDocument.getMetadataCollector(); @@ -2609,8 +2638,7 @@ private Table bindEntityTableSpecification( superEntityTableXref( mappingDocument, entitySource, entityDescriptor, metadataCollector ) ); - if ( isTable ) { - final var tableSource = (TableSource) tableSpecSource; + if ( tableSpecSource instanceof TableSource tableSource ) { table.setRowId( tableSource.getRowId() ); final String checkConstraint = tableSource.getCheckConstraint(); if ( isNotEmpty( checkConstraint ) ) { @@ -2805,7 +2833,6 @@ private void registerSecondPass(SecondPass secondPass, MetadataBuildingContext c } - public static final class DelayedPropertyReferenceHandlerImpl implements InFlightMetadataCollector.DelayedPropertyReferenceHandler { public final String referencedEntityName; @@ -2947,9 +2974,7 @@ private void bindCollectionTable() { } else { final var tableSpecSource = pluralAttributeSource.getCollectionTableSpecificationSource(); - final Identifier logicalCatalogName = determineCatalogName( tableSpecSource ); - final Identifier logicalSchemaName = determineSchemaName( tableSpecSource ); - final var namespace = database.locateNamespace( logicalCatalogName, logicalSchemaName ); + final var namespace = locateTableNamespace( tableSpecSource ); final Table collectionTable; if ( tableSpecSource instanceof TableSource tableSource ) { @@ -2963,19 +2988,21 @@ private void bindCollectionTable() { ) ); } - else { + else if ( tableSpecSource instanceof InLineViewSource inlineViewSource ) { collectionTable = new Table( metadataBuildingContext.getCurrentContributorName(), namespace, - ( (InLineViewSource) tableSpecSource ).getSelectStatement(), + inlineViewSource.getSelectStatement(), false ); } + else { + throw new AssertionFailure( "Unexpected table specification source type" ); + } collectionBinding.setCollectionTable( collectionTable ); } - final var collectionTable = collectionBinding.getCollectionTable(); if ( BOOT_LOGGER.isTraceEnabled() ) { @@ -2984,36 +3011,33 @@ private void bindCollectionTable() { collectionTable.getName() ); } - if ( pluralAttributeSource.getCollectionTableComment() != null ) { - collectionTable.setComment( pluralAttributeSource.getCollectionTableComment() ); + final String tableComment = pluralAttributeSource.getCollectionTableComment(); + if ( tableComment != null ) { + collectionTable.setComment( tableComment ); } - if ( pluralAttributeSource.getCollectionTableCheck() != null ) { - collectionTable.addCheckConstraint( pluralAttributeSource.getCollectionTableCheck() ); + final String tableCheck = pluralAttributeSource.getCollectionTableCheck(); + if ( tableCheck != null ) { + collectionTable.addCheckConstraint( tableCheck ); } } private Identifier logicalName(TableSource tableSource) { - if ( isNotEmpty( tableSource.getExplicitTableName() ) ) { - return toIdentifier( tableSource.getExplicitTableName(), + final String explicitTableName = tableSource.getExplicitTableName(); + if ( isNotEmpty( explicitTableName ) ) { + return toIdentifier( explicitTableName, mappingDocument.getEffectiveDefaults().isDefaultQuoteIdentifiers() ); } else { - final var owner = collectionBinding.getOwner(); - final var ownerEntityNaming = new EntityNamingSourceImpl( - owner.getEntityName(), - owner.getClassName(), - owner.getJpaEntityName() - ); final var implicitNamingSource = new ImplicitCollectionTableNameSource() { @Override public Identifier getOwningPhysicalTableName() { - return owner.getTable().getNameIdentifier(); + return collectionBinding.getOwner().getTable().getNameIdentifier(); } @Override public EntityNaming getOwningEntityNaming() { - return ownerEntityNaming; + return new EntityNamingSourceImpl( collectionBinding.getOwner() ); } @Override @@ -3037,10 +3061,11 @@ protected void createBackReferences() { && !collectionBinding.getKey().isNullable() ) { // for non-inverse one-to-many, with a not-null fk, add a backref! final var oneToMany = (OneToMany) collectionBinding.getElement(); - final String entityName = oneToMany.getReferencedEntityName(); - final var referenced = getReferencedEntityBinding( entityName ); + final var referenced = + getReferencedEntityBinding( oneToMany.getReferencedEntityName() ); final var backref = new Backref(); - backref.setName( '_' + collectionBinding.getOwnerEntityName() + "." + pluralAttributeSource.getName() + "Backref" ); + backref.setName( '_' + collectionBinding.getOwnerEntityName() + + "." + pluralAttributeSource.getName() + "Backref" ); backref.setOptional( true ); backref.setUpdatable( false ); backref.setSelectable( false ); @@ -3075,8 +3100,9 @@ protected void bindCollectionKey() { collectionBinding.getCollectionTable(), keyVal ); - setForeignKeyName( key, keySource.getExplicitForeignKeyName() ); - key.setOnDeleteAction( getOnDeleteAction( pluralAttributeSource.getKeySource().isCascadeDeleteEnabled() ) ); + setForeignKeyName( key, + keySource.getExplicitForeignKeyName() ); + key.setOnDeleteAction( getOnDeleteAction( keySource ) ); // final ImplicitJoinColumnNameSource.Nature implicitNamingNature; // if ( getPluralAttributeSource().getElementSource() instanceof PluralAttributeElementSourceManyToMany @@ -3105,7 +3131,7 @@ protected void bindCollectionKey() { } protected void bindCollectionIdentifier() { - final CollectionIdSource idSource = getPluralAttributeSource().getCollectionIdSource(); + final var idSource = getPluralAttributeSource().getCollectionIdSource(); if ( idSource != null ) { final var idBagBinding = (IdentifierCollection) getCollectionBinding(); final var idBinding = new BasicValue( @@ -3142,158 +3168,106 @@ protected void bindCollectionIndex() { } protected void bindCollectionElement() { - final PluralAttributeElementSource pluralElementSource = getPluralAttributeSource().getElementSource(); + final var pluralElementSource = pluralAttributeSource.getElementSource(); if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.tracef( - "Binding [%s] element type for a [%s]", - pluralElementSource.getNature(), - getPluralAttributeSource().getNature() - ); + BOOT_LOGGER.tracef( "Binding [%s] element type for a [%s]", + pluralElementSource.getNature(), pluralAttributeSource.getNature() ); } - final var collectionBinding = getCollectionBinding(); - final var mappingDocument = getMappingDocument(); if ( pluralElementSource instanceof PluralAttributeElementSourceBasic elementSource ) { - final var elementBinding = - new BasicValue( mappingDocument, collectionBinding.getCollectionTable() ); - - bindSimpleValueType( - mappingDocument, - elementSource.getExplicitHibernateTypeSource(), - elementBinding - ); - - relationalObjectBinder.bindColumnsAndFormulas( - this.mappingDocument, - elementSource.getRelationalValueSources(), - elementBinding, - elementSource.areValuesNullableByDefault(), - context -> context.getMetadataCollector().getDatabase() - .toIdentifier( Collection.DEFAULT_ELEMENT_COLUMN_NAME ) - ); - - collectionBinding.setElement( elementBinding ); - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (the table containing the basic elements) - // This "where" clause comes from the collection mapping; e.g., - collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); + bindBasicElement( elementSource ); } else if ( pluralElementSource instanceof PluralAttributeElementSourceEmbedded elementSource ) { - final var elementBinding = new Component( mappingDocument, collectionBinding ); - - final var embeddableSource = elementSource.getEmbeddableSource(); - bindComponent( - this.mappingDocument, - embeddableSource, - elementBinding, - null, - embeddableSource.getAttributePathBase().getProperty(), - false - ); - - collectionBinding.setElement( elementBinding ); - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (the table containing the embeddable elements) - // This "where" clause comes from the collection mapping; e.g., - collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); + bindEmbeddedElement( elementSource ); } else if ( pluralElementSource instanceof PluralAttributeElementSourceOneToMany elementSource ) { - final var elementBinding = - new OneToMany( mappingDocument, collectionBinding.getOwner() ); - this.collectionBinding.setElement( elementBinding ); - - final var referencedEntityBinding = - getReferencedEntityBinding( elementSource.getReferencedEntityName() ); - - this.collectionBinding.setWhere( - getNonEmptyOrConjunctionIfBothNonEmpty( - referencedEntityBinding.getWhere(), - getPluralAttributeSource().getWhere() - ) - ); - - elementBinding.setReferencedEntityName( referencedEntityBinding.getEntityName() ); - elementBinding.setAssociatedClass( referencedEntityBinding ); - elementBinding.setIgnoreNotFound( elementSource.isIgnoreNotFound() ); + bindOneToManyElement( elementSource ); } else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToMany elementSource ) { - final var elementBinding = - new ManyToOne( mappingDocument, collectionBinding.getCollectionTable() ); - - relationalObjectBinder.bindColumnsAndFormulas( - mappingDocument, - elementSource.getRelationalValueSources(), - elementBinding, - false, - context -> context.getMetadataCollector().getDatabase() - .toIdentifier( Collection.DEFAULT_ELEMENT_COLUMN_NAME ) - ); + bindManyToManyElement( elementSource ); + } + else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToAny elementSource ) { + bindManyToAnyElement( elementSource ); + } + } - final var fetchCharacteristics = elementSource.getFetchCharacteristics(); - elementBinding.setLazy( fetchCharacteristics.getFetchTiming() != FetchTiming.IMMEDIATE ); - elementBinding.setFetchMode( - fetchCharacteristics.getFetchStyle() == FetchStyle.SELECT - ? FetchMode.SELECT - : FetchMode.JOIN - ); + private void bindManyToAnyElement(PluralAttributeElementSourceManyToAny elementSource) { + final var elementBinding = + new Any( mappingDocument, + collectionBinding.getCollectionTable() ); + bindAny( mappingDocument, elementSource, elementBinding, + pluralAttributeSource.getAttributeRole().append( "element" ) ); + collectionBinding.setElement( elementBinding ); + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (which is the join table for a many-to-any association). + // This "where" clause comes from the collection mapping; e.g., + collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); + } - setForeignKeyName( elementBinding, elementSource.getExplicitForeignKeyName() ); + private void bindManyToManyElement(PluralAttributeElementSourceManyToMany elementSource) { + final var elementBinding = + new ManyToOne( mappingDocument, + collectionBinding.getCollectionTable() ); + relationalObjectBinder.bindColumnsAndFormulas( + mappingDocument, + elementSource.getRelationalValueSources(), + elementBinding, + false, + context -> context.getMetadataCollector().getDatabase() + .toIdentifier( Collection.DEFAULT_ELEMENT_COLUMN_NAME ) + ); + handleFetchCharacteristics( elementSource, elementBinding ); + setForeignKeyName( elementBinding, + elementSource.getExplicitForeignKeyName() ); + final String referencedEntityName = elementSource.getReferencedEntityName(); + handleReferencedEntity( elementBinding, referencedEntityName, + elementSource.getReferencedEntityAttributeName() ); + collectionBinding.setElement( elementBinding ); + final var referencedEntityBinding = getReferencedEntityBinding( referencedEntityName ); + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (which is the join table for a many-to-many association). + // This "where" clause comes from the collection mapping; e.g., + collectionBinding.setWhere( pluralAttributeSource.getWhere() ); + collectionBinding.setManyToManyWhere( + getNonEmptyOrConjunctionIfBothNonEmpty( + referencedEntityBinding.getWhere(), + elementSource.getWhere() + ) + ); + collectionBinding.setManyToManyOrdering( elementSource.getOrder() ); + bindManyToManyFilters( elementSource, mappingDocument, collectionBinding, elementBinding ); + } - final String referencedEntityName = elementSource.getReferencedEntityName(); - elementBinding.setReferencedEntityName( referencedEntityName ); - final String referencedEntityAttributeName = elementSource.getReferencedEntityAttributeName(); - if ( isNotEmpty( referencedEntityAttributeName ) ) { - elementBinding.setReferencedPropertyName( referencedEntityAttributeName ); - elementBinding.setReferenceToPrimaryKey( false ); - } - else { - elementBinding.setReferenceToPrimaryKey( true ); + private void bindManyToManyFilters( + PluralAttributeElementSourceManyToMany elementSource, + MappingDocument mappingDocument, + Collection collectionBinding, + ManyToOne elementBinding) { + if ( !isEmpty( elementSource.getFilterSources() ) + || elementSource.getWhere() != null ) { + if ( collectionBinding.getFetchMode() == FetchMode.JOIN + && elementBinding.getFetchMode() != FetchMode.JOIN ) { + throw new MappingException( + String.format( + Locale.ENGLISH, + "many-to-many defining filter or where without join fetching is not " + + "valid within collection [%s] using join fetching", + getPluralAttributeSource().getAttributeRole().getFullPath() + ), + mappingDocument.getOrigin() + ); } + } - collectionBinding.setElement( elementBinding ); - - final PersistentClass referencedEntityBinding = getReferencedEntityBinding( referencedEntityName ); - - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (which is the join table for a many-to-many association). - // This "where" clause comes from the collection mapping; e.g., - collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); - - collectionBinding.setManyToManyWhere( - getNonEmptyOrConjunctionIfBothNonEmpty( - referencedEntityBinding.getWhere(), - elementSource.getWhere() - ) - ); - - collectionBinding.setManyToManyOrdering( elementSource.getOrder() ); - - if ( !isEmpty( elementSource.getFilterSources() ) - || elementSource.getWhere() != null ) { - if ( collectionBinding.getFetchMode() == FetchMode.JOIN - && elementBinding.getFetchMode() != FetchMode.JOIN ) { - throw new MappingException( - String.format( - Locale.ENGLISH, - "many-to-many defining filter or where without join fetching is not " + - "valid within collection [%s] using join fetching", - getPluralAttributeSource().getAttributeRole().getFullPath() - ), - mappingDocument.getOrigin() + for ( var filterSource : elementSource.getFilterSources() ) { + if ( filterSource.getName() == null ) { + if ( BOOT_LOGGER.isTraceEnabled() ) { + BOOT_LOGGER.tracef( + "Encountered filter with no name associated with many-to-many [%s]; skipping", + getPluralAttributeSource().getAttributeRole().getFullPath() ); } } - - for ( var filterSource : elementSource.getFilterSources() ) { - if ( filterSource.getName() == null ) { - if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.tracef( - "Encountered filter with no name associated with many-to-many [%s]; skipping", - getPluralAttributeSource().getAttributeRole().getFullPath() - ); - } - continue; - } - + else { if ( filterSource.getCondition() == null ) { throw new MappingException( String.format( @@ -3305,7 +3279,6 @@ else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToMany mappingDocument.getOrigin() ); } - if ( BOOT_LOGGER.isTraceEnabled() ) { BOOT_LOGGER.tracef( "Applying many-to-many filter [%s] as [%s] to collection [%s]", @@ -3314,7 +3287,6 @@ else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToMany getPluralAttributeSource().getAttributeRole().getFullPath() ); } - collectionBinding.addManyToManyFilter( filterSource.getName(), filterSource.getCondition(), @@ -3324,20 +3296,67 @@ else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToMany ); } } - else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToAny elementSource ) { - final var elementBinding = new Any( mappingDocument, collectionBinding.getCollectionTable() ); - bindAny( - this.mappingDocument, - elementSource, - elementBinding, - getPluralAttributeSource().getAttributeRole().append( "element" ) - ); - collectionBinding.setElement( elementBinding ); - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (which is the join table for a many-to-any association). - // This "where" clause comes from the collection mapping; e.g., - collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); - } + } + + private void bindOneToManyElement(PluralAttributeElementSourceOneToMany elementSource) { + final var elementBinding = + new OneToMany( mappingDocument, + collectionBinding.getOwner() ); + collectionBinding.setElement( elementBinding ); + final var referencedEntityBinding = + getReferencedEntityBinding( elementSource.getReferencedEntityName() ); + collectionBinding.setWhere( + getNonEmptyOrConjunctionIfBothNonEmpty( + referencedEntityBinding.getWhere(), + pluralAttributeSource.getWhere() + ) + ); + elementBinding.setReferencedEntityName( referencedEntityBinding.getEntityName() ); + elementBinding.setAssociatedClass( referencedEntityBinding ); + elementBinding.setIgnoreNotFound( elementSource.isIgnoreNotFound() ); + } + + private void bindEmbeddedElement(PluralAttributeElementSourceEmbedded elementSource) { + final var elementBinding = new Component( mappingDocument, collectionBinding ); + final var embeddableSource = elementSource.getEmbeddableSource(); + bindComponent( mappingDocument, embeddableSource, elementBinding, null, + embeddableSource.getAttributePathBase().getProperty(), false ); + collectionBinding.setElement( elementBinding ); + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (the table containing the embeddable elements) + // This "where" clause comes from the collection mapping; e.g., + collectionBinding.setWhere( pluralAttributeSource.getWhere() ); + } + + private void bindBasicElement(PluralAttributeElementSourceBasic elementSource) { + final var elementBinding = + new BasicValue( mappingDocument, + collectionBinding.getCollectionTable() ); + bindSimpleValueType( mappingDocument, elementSource.getExplicitHibernateTypeSource(), elementBinding ); + relationalObjectBinder.bindColumnsAndFormulas( + mappingDocument, + elementSource.getRelationalValueSources(), + elementBinding, + elementSource.areValuesNullableByDefault(), + context -> context.getMetadataCollector().getDatabase() + .toIdentifier( Collection.DEFAULT_ELEMENT_COLUMN_NAME ) + ); + collectionBinding.setElement( elementBinding ); + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (the table containing the basic elements) + // This "where" clause comes from the collection mapping; e.g., + collectionBinding.setWhere( pluralAttributeSource.getWhere() ); + } + + private static void handleFetchCharacteristics( + PluralAttributeElementSourceManyToMany elementSource, ManyToOne elementBinding) { + final var characteristics = elementSource.getFetchCharacteristics(); + elementBinding.setLazy( characteristics.getFetchTiming() != FetchTiming.IMMEDIATE ); + elementBinding.setFetchMode( + characteristics.getFetchStyle() == FetchStyle.SELECT + ? FetchMode.SELECT + : FetchMode.JOIN + ); } private PersistentClass getReferencedEntityBinding(String referencedEntityName) { @@ -3352,7 +3371,7 @@ private PersistentClass getReferencedEntityBinding(String referencedEntityName) getPluralAttributeSource().getAttributeRole().getFullPath(), referencedEntityName ), - getMappingDocument().getOrigin() + mappingDocument.getOrigin() ); } return entityBinding; @@ -3453,9 +3472,12 @@ protected void createBackReferences() { && !indexIsFormula ) { final var oneToMany = (OneToMany) collectionBinding.getElement(); final String entityName = oneToMany.getReferencedEntityName(); - final var referenced = getMappingDocument().getMetadataCollector().getEntityBinding( entityName ); + final var referenced = + getMappingDocument().getMetadataCollector() + .getEntityBinding( entityName ); final var backref = new IndexBackref(); - backref.setName( '_' + collectionBinding.getOwnerEntityName() + "." + getPluralAttributeSource().getName() + "IndexBackref" ); + backref.setName( '_' + collectionBinding.getOwnerEntityName() + + "." + getPluralAttributeSource().getName() + "IndexBackref" ); backref.setOptional( true ); backref.setUpdatable( false ); backref.setSelectable( false ); @@ -3532,9 +3554,12 @@ private void createIndexBackRef( && !collectionBinding.isInverse() ) { final var oneToMany = (OneToMany) collectionBinding.getElement(); final String entityName = oneToMany.getReferencedEntityName(); - final var referenced = mappingDocument.getMetadataCollector().getEntityBinding( entityName ); + final var referenced = + mappingDocument.getMetadataCollector() + .getEntityBinding( entityName ); final var backref = new IndexBackref(); - backref.setName( '_' + collectionBinding.getOwnerEntityName() + "." + pluralAttributeSource.getName() + "IndexBackref" ); + backref.setName( '_' + collectionBinding.getOwnerEntityName() + + "." + pluralAttributeSource.getName() + "IndexBackref" ); backref.setOptional( true ); backref.setUpdatable( false ); backref.setSelectable( false ); @@ -3575,7 +3600,6 @@ protected void bindCollectionIndex() { @Override protected void createBackReferences() { super.createBackReferences(); - createIndexBackRef( getMappingDocument(), getPluralAttributeSource(), @@ -3591,7 +3615,9 @@ public void bindListOrArrayIndex( final var indexSource = (PluralAttributeSequentialIndexSource) attributeSource.getIndexSource(); - final var indexBinding = new BasicValue( mappingDocument, collectionBinding.getCollectionTable() ); + final var indexBinding = + new BasicValue( mappingDocument, + collectionBinding.getCollectionTable() ); bindSimpleValueType( mappingDocument, indexSource.getTypeInformation(), indexBinding ); relationalObjectBinder.bindColumnsAndFormulas( @@ -3624,70 +3650,108 @@ private void bindMapKey( final org.hibernate.mapping.Map collectionBinding) { final var indexSource = pluralAttributeSource.getIndexSource(); if ( indexSource instanceof PluralAttributeMapKeySourceBasic mapKeySource ) { - final var value = new BasicValue( mappingDocument, collectionBinding.getCollectionTable() ); - bindSimpleValueType( mappingDocument, mapKeySource.getTypeInformation(), value ); - if ( !value.isTypeSpecified() ) { - throw new MappingException( - "map index element must specify a type: " - + pluralAttributeSource.getAttributeRole().getFullPath(), - mappingDocument.getOrigin() - ); - } - - relationalObjectBinder.bindColumnsAndFormulas( - mappingDocument, - mapKeySource.getRelationalValueSources(), - value, - true, - context -> database.toIdentifier( IndexedCollection.DEFAULT_INDEX_COLUMN_NAME ) - ); - - collectionBinding.setIndex( value ); + bindBasicMapKey( mappingDocument, pluralAttributeSource, collectionBinding, mapKeySource ); } else if ( indexSource instanceof PluralAttributeMapKeySourceEmbedded mapKeySource ) { - final var componentBinding = new Component( mappingDocument, collectionBinding ); - bindComponent( - mappingDocument, - mapKeySource.getEmbeddableSource(), - componentBinding, - null, - pluralAttributeSource.getName(), - false - ); - collectionBinding.setIndex( componentBinding ); + bindEmbeddedMapKey( mappingDocument, pluralAttributeSource, collectionBinding, mapKeySource ); } else if ( indexSource instanceof PluralAttributeMapKeyManyToManySource mapKeySource ) { - final var mapKeyBinding = new ManyToOne( mappingDocument, collectionBinding.getCollectionTable() ); + bindManyToManyMapKey( mappingDocument, pluralAttributeSource, collectionBinding, mapKeySource ); + } + else if ( indexSource instanceof PluralAttributeMapKeyManyToAnySource mapKeySource) { + bindManyToAnyMapKey( mappingDocument, pluralAttributeSource, collectionBinding, mapKeySource ); + } + } - mapKeyBinding.setReferencedEntityName( mapKeySource.getReferencedEntityName() ); + private void bindManyToAnyMapKey( + MappingDocument mappingDocument, + IndexedPluralAttributeSource pluralAttributeSource, + org.hibernate.mapping.Map collectionBinding, + PluralAttributeMapKeyManyToAnySource mapKeySource) { + final var mapKeyBinding = + new Any( mappingDocument, + collectionBinding.getCollectionTable() ); + bindAny( mappingDocument, mapKeySource, mapKeyBinding, + pluralAttributeSource.getAttributeRole().append( "key" ) ); + collectionBinding.setIndex( mapKeyBinding ); + } - relationalObjectBinder.bindColumnsAndFormulas( - mappingDocument, - mapKeySource.getRelationalValueSources(), - mapKeyBinding, - true, - context -> implicitNamingStrategy.determineMapKeyColumnName( - new ImplicitMapKeyColumnNameSource() { - @Override - public AttributePath getPluralAttributePath() { - return pluralAttributeSource.getAttributePath(); - } + private void bindManyToManyMapKey( + MappingDocument mappingDocument, + IndexedPluralAttributeSource pluralAttributeSource, + org.hibernate.mapping.Map collectionBinding, + PluralAttributeMapKeyManyToManySource mapKeySource) { + final var mapKeyBinding = + new ManyToOne( mappingDocument, + collectionBinding.getCollectionTable() ); - @Override - public MetadataBuildingContext getBuildingContext() { - return context; - } + mapKeyBinding.setReferencedEntityName( mapKeySource.getReferencedEntityName() ); + + relationalObjectBinder.bindColumnsAndFormulas( + mappingDocument, + mapKeySource.getRelationalValueSources(), + mapKeyBinding, + true, + context -> implicitNamingStrategy.determineMapKeyColumnName( + new ImplicitMapKeyColumnNameSource() { + @Override + public AttributePath getPluralAttributePath() { + return pluralAttributeSource.getAttributePath(); } - ) + + @Override + public MetadataBuildingContext getBuildingContext() { + return context; + } + } + ) + ); + collectionBinding.setIndex( mapKeyBinding ); + } + + private void bindEmbeddedMapKey( + MappingDocument mappingDocument, + IndexedPluralAttributeSource pluralAttributeSource, + org.hibernate.mapping.Map collectionBinding, + PluralAttributeMapKeySourceEmbedded mapKeySource) { + final var componentBinding = new Component( mappingDocument, collectionBinding ); + bindComponent( + mappingDocument, + mapKeySource.getEmbeddableSource(), + componentBinding, + null, + pluralAttributeSource.getName(), + false + ); + collectionBinding.setIndex( componentBinding ); + } + + private void bindBasicMapKey( + MappingDocument mappingDocument, + IndexedPluralAttributeSource pluralAttributeSource, + org.hibernate.mapping.Map collectionBinding, + PluralAttributeMapKeySourceBasic mapKeySource) { + final var value = + new BasicValue( mappingDocument, + collectionBinding.getCollectionTable() ); + bindSimpleValueType( mappingDocument, mapKeySource.getTypeInformation(), value ); + if ( !value.isTypeSpecified() ) { + throw new MappingException( + "map index element must specify a type: " + + pluralAttributeSource.getAttributeRole().getFullPath(), + mappingDocument.getOrigin() ); - collectionBinding.setIndex( mapKeyBinding ); - } - else if ( indexSource instanceof PluralAttributeMapKeyManyToAnySource mapKeySource) { - final var mapKeyBinding = new Any( mappingDocument, collectionBinding.getCollectionTable() ); - bindAny( mappingDocument, mapKeySource, mapKeyBinding, - pluralAttributeSource.getAttributeRole().append( "key" ) ); - collectionBinding.setIndex( mapKeyBinding ); } + + relationalObjectBinder.bindColumnsAndFormulas( + mappingDocument, + mapKeySource.getRelationalValueSources(), + value, + true, + context -> database.toIdentifier( IndexedCollection.DEFAULT_INDEX_COLUMN_NAME ) + ); + + collectionBinding.setIndex( value ); } private class ManyToOneColumnBinder implements ImplicitColumnNamingSecondPass { @@ -3727,7 +3791,8 @@ public boolean canProcessImmediately() { } final var referencedEntityBinding = - mappingDocument.getMetadataCollector().getEntityBinding( referencedEntityName ); + mappingDocument.getMetadataCollector() + .getEntityBinding( referencedEntityName ); if ( referencedEntityBinding == null ) { return false; } @@ -3759,7 +3824,8 @@ public void doSecondPass(Map persistentClasses) { // column making up the FK. final var referencedEntityBinding = - mappingDocument.getMetadataCollector().getEntityBinding( referencedEntityName ); + mappingDocument.getMetadataCollector() + .getEntityBinding( referencedEntityName ); if ( referencedEntityBinding == null ) { throw new AssertionFailure( @@ -3810,7 +3876,8 @@ private ManyToOneFkSecondPass( String referencedEntityName) { if ( referencedEntityName == null ) { throw new MappingException( - "entity name referenced by many-to-one required [" + manyToOneSource.getAttributeRole().getFullPath() + "]", + "entity name referenced by many-to-one required [" + + manyToOneSource.getAttributeRole().getFullPath() + "]", mappingDocument.getOrigin() ); } @@ -3841,7 +3908,9 @@ public void doSecondPass(Map persistentClasses) throws manyToOneBinding.createForeignKey(); } else { - manyToOneBinding.createPropertyRefConstraints( mappingDocument.getMetadataCollector().getEntityBindingMap() ); + manyToOneBinding.createPropertyRefConstraints( + mappingDocument.getMetadataCollector() + .getEntityBindingMap() ); } } @@ -3945,10 +4014,14 @@ public Identifier getUserProvidedIdentifier() { } } ); - uniqueKey.setName( uniqueKeyName.render( mappingDocument.getMetadataCollector().getDatabase().getDialect() ) ); + uniqueKey.setName( uniqueKeyName.render( getDialect() ) ); entityBinding.getTable().addUniqueKey( uniqueKey ); } + + private Dialect getDialect() { + return mappingDocument.getMetadataCollector().getDatabase().getDialect(); + } } private String columns(Value value) { @@ -3962,7 +4035,7 @@ private String columns(Value value) { return builder.toString(); } - private static OnDeleteAction getOnDeleteAction(boolean entitySource) { - return entitySource ? OnDeleteAction.CASCADE : OnDeleteAction.NO_ACTION; + private static OnDeleteAction getOnDeleteAction(ForeignKeyContributingSource entitySource) { + return entitySource.isCascadeDeleteEnabled() ? OnDeleteAction.CASCADE : OnDeleteAction.NO_ACTION; } }