diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java index 28df8c6d76..32b3c6e680 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java @@ -6,13 +6,48 @@ */ package org.hibernate.metamodel.mapping.internal; +import java.io.Serializable; +import java.util.Iterator; +import java.util.Locale; +import java.util.function.Consumer; + +import org.hibernate.MappingException; +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.SharedSessionContract; import org.hibernate.bytecode.spi.ReflectionOptimizer; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.mapping.Any; +import org.hibernate.mapping.BasicValue; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Selectable; +import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.StateArrayContributorMetadata; +import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; +import org.hibernate.metamodel.model.domain.NavigableRole; +import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; import org.hibernate.property.access.spi.Getter; +import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.type.AnyType; +import org.hibernate.type.BasicType; +import org.hibernate.type.CollectionType; +import org.hibernate.type.CompositeType; +import org.hibernate.type.EntityType; +import org.hibernate.type.Type; +import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.MutabilityPlan; +import org.hibernate.type.spi.TypeConfiguration; /** * Base support for EmbeddableMappingType implementations @@ -71,4 +106,286 @@ public void setValues(Object component, Object[] values) { } ); } } + + @FunctionalInterface + protected interface ConcreteTableResolver { + String resolve(Column column, JdbcEnvironment jdbcEnvironment); + } + + @FunctionalInterface + protected interface SuccessfulCompletionCallback { + void success(); + } + + protected static class IllegalAttributeType extends RuntimeException { + public IllegalAttributeType(String message) { + super( message ); + } + } + + @FunctionalInterface + protected interface AttributeTypeValidator { + void check(String name, Type type) throws IllegalAttributeType; + } + + protected static boolean finishInitialization( + NavigableRole navigableRole, + Component bootDescriptor, + CompositeType compositeType, + String rootTableExpression, + String[] rootTableKeyColumnNames, + EmbeddableMappingType declarer, + EmbeddableRepresentationStrategy representationStrategy, + AttributeTypeValidator attributeTypeValidator, + ConcreteTableResolver concreteTableResolver, + Consumer<AttributeMapping> attributeConsumer, + SuccessfulCompletionCallback completionCallback, + MappingModelCreationProcess creationProcess) { + final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory(); + final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); + final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); + final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); + final Dialect dialect = jdbcEnvironment.getDialect(); + + final Type[] subtypes = compositeType.getSubtypes(); + + int attributeIndex = 0; + int columnPosition = 0; + + final Iterator<Property> propertyIterator = bootDescriptor.getPropertyIterator(); + while ( propertyIterator.hasNext() ) { + final Property bootPropertyDescriptor = propertyIterator.next(); + final Type subtype = subtypes[ attributeIndex ]; + + attributeTypeValidator.check( bootPropertyDescriptor.getName(), subtype ); + + final PropertyAccess propertyAccess = representationStrategy.resolvePropertyAccess( bootPropertyDescriptor ); + final AttributeMapping attributeMapping; + + if ( subtype instanceof BasicType ) { + final BasicValue basicValue = (BasicValue) bootPropertyDescriptor.getValue(); + final Selectable selectable = basicValue.getColumn(); + final String containingTableExpression; + final String columnExpression; + if ( rootTableKeyColumnNames == null ) { + if ( selectable.isFormula() ) { + columnExpression = selectable.getTemplate( dialect, creationProcess.getSqmFunctionRegistry() ); + } + else { + columnExpression = selectable.getText( dialect ); + } + + if ( selectable instanceof Column ) { + containingTableExpression = concreteTableResolver.resolve( (Column) selectable, jdbcEnvironment ); + } + else { + containingTableExpression = rootTableExpression; + } + } + else { + containingTableExpression = rootTableExpression; + columnExpression = rootTableKeyColumnNames[ columnPosition ]; + } + + attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping( + bootPropertyDescriptor.getName(), + navigableRole.append( bootPropertyDescriptor.getName() ), + attributeIndex, + bootPropertyDescriptor, + declarer, + (BasicType<?>) subtype, + containingTableExpression, + columnExpression, + selectable.isFormula(), + selectable.getCustomReadExpression(), + selectable.getCustomWriteExpression(), + propertyAccess, + compositeType.getCascadeStyle( attributeIndex ), + creationProcess + ); + + columnPosition++; + } + else if ( subtype instanceof AnyType ) { + final Any bootValueMapping = (Any) bootPropertyDescriptor.getValue(); + final AnyType anyType = (AnyType) subtype; + + final boolean nullable = bootValueMapping.isNullable(); + final boolean insertable = bootPropertyDescriptor.isInsertable(); + final boolean updateable = bootPropertyDescriptor.isUpdateable(); + final boolean includeInOptimisticLocking = bootPropertyDescriptor.isOptimisticLocked(); + final CascadeStyle cascadeStyle = compositeType.getCascadeStyle( attributeIndex ); + final MutabilityPlan<?> mutabilityPlan; + + if ( updateable ) { + mutabilityPlan = new MutabilityPlan<Object>() { + @Override + public boolean isMutable() { + return true; + } + + @Override + public Object deepCopy(Object value) { + if ( value == null ) { + return null; + } + + return anyType.deepCopy( value, creationProcess.getCreationContext().getSessionFactory() ); + } + + @Override + public Serializable disassemble(Object value, SharedSessionContract session) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public Object assemble(Serializable cached, SharedSessionContract session) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + }; + } + else { + mutabilityPlan = ImmutableMutabilityPlan.INSTANCE; + } + + final StateArrayContributorMetadataAccess attributeMetadataAccess = entityMappingType -> new StateArrayContributorMetadata() { + @Override + public PropertyAccess getPropertyAccess() { + return propertyAccess; + } + + @Override + public MutabilityPlan<?> getMutabilityPlan() { + return mutabilityPlan; + } + + @Override + public boolean isNullable() { + return nullable; + } + + @Override + public boolean isInsertable() { + return insertable; + } + + @Override + public boolean isUpdatable() { + return updateable; + } + + @Override + public boolean isIncludedInDirtyChecking() { + // todo (6.0) : do not believe this is correct + return updateable; + } + + @Override + public boolean isIncludedInOptimisticLocking() { + return includeInOptimisticLocking; + } + + @Override + public CascadeStyle getCascadeStyle() { + return cascadeStyle; + } + }; + + attributeMapping = new DiscriminatedAssociationAttributeMapping( + navigableRole.append( bootPropertyDescriptor.getName() ), + typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Object.class ), + declarer, + attributeIndex, + attributeMetadataAccess, + bootPropertyDescriptor.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE, + propertyAccess, + bootPropertyDescriptor, + anyType, + bootValueMapping, + creationProcess + ); + } + else if ( subtype instanceof CompositeType ) { + final CompositeType subCompositeType = (CompositeType) subtype; + final int columnSpan = subCompositeType.getColumnSpan( sessionFactory ); + final String subTableExpression; + final String[] subRootTableKeyColumnNames; + if ( rootTableKeyColumnNames == null ) { + subTableExpression = rootTableExpression; + subRootTableKeyColumnNames = null; + } + else { + subTableExpression = rootTableExpression; + subRootTableKeyColumnNames = new String[ columnSpan ]; + System.arraycopy( rootTableKeyColumnNames, columnPosition, subRootTableKeyColumnNames, 0, columnSpan ); + } + + attributeMapping = MappingModelCreationHelper.buildEmbeddedAttributeMapping( + bootPropertyDescriptor.getName(), + attributeIndex, + bootPropertyDescriptor, + declarer, + subCompositeType, + subTableExpression, + subRootTableKeyColumnNames, + propertyAccess, + compositeType.getCascadeStyle( attributeIndex ), + creationProcess + ); + + columnPosition += columnSpan; + } + else if ( subtype instanceof CollectionType ) { + final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner() + .getEntityName() ); + + attributeMapping = MappingModelCreationHelper.buildPluralAttributeMapping( + bootPropertyDescriptor.getName(), + attributeIndex, + bootPropertyDescriptor, + entityPersister, + propertyAccess, + compositeType.getCascadeStyle( attributeIndex ), + compositeType.getFetchMode( attributeIndex ), + creationProcess + ); + } + else if ( subtype instanceof EntityType ) { + final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner() + .getEntityName() ); + + attributeMapping = MappingModelCreationHelper.buildSingularAssociationAttributeMapping( + bootPropertyDescriptor.getName(), + navigableRole.append( bootPropertyDescriptor.getName() ), + attributeIndex, + bootPropertyDescriptor, + entityPersister, + entityPersister, + (EntityType) subtype, + representationStrategy.resolvePropertyAccess( bootPropertyDescriptor ), + compositeType.getCascadeStyle( attributeIndex ), + creationProcess + ); + columnPosition += bootPropertyDescriptor.getColumnSpan(); + } + else { + throw new MappingException( + String.format( + Locale.ROOT, + "Unable to determine attribute nature : %s#%s", + bootDescriptor.getOwner().getEntityName(), + bootPropertyDescriptor.getName() + ) + ); + } + + attributeConsumer.accept( attributeMapping ); + + attributeIndex++; + } + + completionCallback.success(); + + return true; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddableMappingTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddableMappingTypeImpl.java index a3b8077e54..3b597b4151 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddableMappingTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddableMappingTypeImpl.java @@ -275,6 +275,32 @@ private boolean finishInitialization( String rootTableExpression, String[] rootTableKeyColumnNames, MappingModelCreationProcess creationProcess) { +// for some reason I cannot get this to work, though only a single test fails - `CompositeElementTest` +// return finishInitialization( +// getNavigableRole(), +// bootDescriptor, +// compositeType, +// rootTableExpression, +// rootTableKeyColumnNames, +// this, +// representationStrategy, +// (name, type) -> {}, +// (column, jdbcEnvironment) -> getTableIdentifierExpression( +// column.getValue().getTable(), +// jdbcEnvironment +// ), +// this::addAttribute, +// () -> { +// // We need the attribute mapping types to finish initialization first before we can build the column mappings +// creationProcess.registerInitializationCallback( +// "EmbeddableMappingType(" + getEmbeddedValueMapping().getNavigableRole().getFullPath() + ")#initColumnMappings", +// this::initColumnMappings +// ); +// }, +// creationProcess +// ); +// todo (6.0) - get this ^^ to work, or drop the comment + final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory(); final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java index 95c67287e0..3cf151e702 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java @@ -6,33 +6,20 @@ */ package org.hibernate.metamodel.mapping.internal; -import java.io.Serializable; -import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.function.Consumer; -import org.hibernate.MappingException; import org.hibernate.NotYetImplementedFor6Exception; -import org.hibernate.SharedSessionContract; -import org.hibernate.dialect.Dialect; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.mapping.Any; -import org.hibernate.mapping.BasicValue; -import org.hibernate.mapping.Column; import org.hibernate.mapping.Component; import org.hibernate.mapping.IndexedConsumer; -import org.hibernate.mapping.Property; import org.hibernate.mapping.RootClass; -import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Table; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; @@ -49,7 +36,6 @@ import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMappings; import org.hibernate.metamodel.mapping.SingularAttributeMapping; -import org.hibernate.metamodel.mapping.StateArrayContributorMetadata; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; @@ -63,16 +49,10 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.AnyType; -import org.hibernate.type.BasicType; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; -import org.hibernate.type.EntityType; -import org.hibernate.type.Type; -import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; import org.hibernate.type.descriptor.java.JavaType; -import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.spi.CompositeTypeImplementor; -import org.hibernate.type.spi.TypeConfiguration; import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.getStateArrayContributorMetadataAccess; @@ -468,8 +448,6 @@ public EmbeddableMappingType createInverseMappingType( - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // init @@ -479,263 +457,38 @@ private boolean finishInitialization( String rootTableExpression, String[] rootTableKeyColumnNames, MappingModelCreationProcess creationProcess) { - final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory(); - final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); - final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); - final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); - final Dialect dialect = jdbcEnvironment.getDialect(); - - final Type[] subtypes = compositeType.getSubtypes(); - - int attributeIndex = 0; - int columnPosition = 0; - // Reset the attribute mappings that were added in previous attempts this.attributeMappings.clear(); - final Iterator<Property> propertyIterator = bootDescriptor.getPropertyIterator(); - while ( propertyIterator.hasNext() ) { - final Property bootPropertyDescriptor = propertyIterator.next(); - - final PropertyAccess propertyAccess = getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ); - - final AttributeMapping attributeMapping; - - final Type subtype = subtypes[ attributeIndex ]; - if ( subtype instanceof BasicType ) { - final BasicValue basicValue = (BasicValue) bootPropertyDescriptor.getValue(); - final Selectable selectable = basicValue.getColumn(); - final String containingTableExpression; - final String columnExpression; - if ( rootTableKeyColumnNames == null ) { - if ( selectable.isFormula() ) { - columnExpression = selectable.getTemplate( dialect, creationProcess.getSqmFunctionRegistry() ); + return finishInitialization( + navigableRole, + bootDescriptor, + compositeType, + rootTableExpression, + rootTableKeyColumnNames, + this, + representationStrategy, + (attributeName, attributeType) -> { + if ( attributeType instanceof CollectionType ) { + throw new IllegalAttributeType( "An IdClass cannot define collection attributes : " + attributeName ); } - else { - columnExpression = selectable.getText( dialect ); + if ( attributeType instanceof AnyType ) { + throw new IllegalAttributeType( "An IdClass cannot define <any/> attributes : " + attributeName ); } - if ( selectable instanceof Column ) { - containingTableExpression = getTableIdentifierExpression( - ( (Column) selectable ).getValue().getTable(), - jdbcEnvironment - ); - } - else { - containingTableExpression = rootTableExpression; - } - } - else { - containingTableExpression = rootTableExpression; - columnExpression = rootTableKeyColumnNames[ columnPosition ]; - } - - attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping( - bootPropertyDescriptor.getName(), - navigableRole.append( bootPropertyDescriptor.getName() ), - attributeIndex, - bootPropertyDescriptor, - this, - (BasicType<?>) subtype, - containingTableExpression, - columnExpression, - selectable.isFormula(), - selectable.getCustomReadExpression(), - selectable.getCustomWriteExpression(), - propertyAccess, - compositeType.getCascadeStyle( attributeIndex ), - creationProcess - ); - - columnPosition++; - } - else if ( subtype instanceof AnyType ) { - final Any bootValueMapping = (Any) bootPropertyDescriptor.getValue(); - final AnyType anyType = (AnyType) subtype; - - final boolean nullable = bootValueMapping.isNullable(); - final boolean insertable = bootPropertyDescriptor.isInsertable(); - final boolean updateable = bootPropertyDescriptor.isUpdateable(); - final boolean includeInOptimisticLocking = bootPropertyDescriptor.isOptimisticLocked(); - final CascadeStyle cascadeStyle = compositeType.getCascadeStyle( attributeIndex ); - final MutabilityPlan<?> mutabilityPlan; - - if ( updateable ) { - mutabilityPlan = new MutabilityPlan<Object>() { - @Override - public boolean isMutable() { - return true; - } - - @Override - public Object deepCopy(Object value) { - if ( value == null ) { - return null; - } - - return anyType.deepCopy( value, creationProcess.getCreationContext().getSessionFactory() ); - } - - @Override - public Serializable disassemble(Object value, SharedSessionContract session) { - throw new NotYetImplementedFor6Exception( getClass() ); - } - - @Override - public Object assemble(Serializable cached, SharedSessionContract session) { - throw new NotYetImplementedFor6Exception( getClass() ); - } - }; - } - else { - mutabilityPlan = ImmutableMutabilityPlan.INSTANCE; - } - - final StateArrayContributorMetadataAccess attributeMetadataAccess = entityMappingType -> new StateArrayContributorMetadata() { - @Override - public PropertyAccess getPropertyAccess() { - return propertyAccess; - } - - @Override - public MutabilityPlan<?> getMutabilityPlan() { - return mutabilityPlan; - } - - @Override - public boolean isNullable() { - return nullable; - } - - @Override - public boolean isInsertable() { - return insertable; - } - - @Override - public boolean isUpdatable() { - return updateable; - } - - @Override - public boolean isIncludedInDirtyChecking() { - // todo (6.0) : do not believe this is correct - return updateable; - } - - @Override - public boolean isIncludedInOptimisticLocking() { - return includeInOptimisticLocking; - } - - @Override - public CascadeStyle getCascadeStyle() { - return cascadeStyle; - } - }; - - attributeMapping = new DiscriminatedAssociationAttributeMapping( - navigableRole.append( bootPropertyDescriptor.getName() ), - typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Object.class ), - this, - attributeIndex, - attributeMetadataAccess, - bootPropertyDescriptor.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE, - propertyAccess, - bootPropertyDescriptor, - anyType, - bootValueMapping, - creationProcess - ); - } - else if ( subtype instanceof CompositeType ) { - final CompositeType subCompositeType = (CompositeType) subtype; - final int columnSpan = subCompositeType.getColumnSpan( sessionFactory ); - final String subTableExpression; - final String[] subRootTableKeyColumnNames; - if ( rootTableKeyColumnNames == null ) { - subTableExpression = rootTableExpression; - subRootTableKeyColumnNames = null; - } - else { - subTableExpression = rootTableExpression; - subRootTableKeyColumnNames = new String[ columnSpan ]; - System.arraycopy( rootTableKeyColumnNames, columnPosition, subRootTableKeyColumnNames, 0, columnSpan ); - } - - attributeMapping = MappingModelCreationHelper.buildEmbeddedAttributeMapping( - bootPropertyDescriptor.getName(), - attributeIndex, - bootPropertyDescriptor, - this, - subCompositeType, - subTableExpression, - subRootTableKeyColumnNames, - propertyAccess, - compositeType.getCascadeStyle( attributeIndex ), - creationProcess - ); - - columnPosition += columnSpan; - } - else if ( subtype instanceof CollectionType ) { - final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner() - .getEntityName() ); - - attributeMapping = MappingModelCreationHelper.buildPluralAttributeMapping( - bootPropertyDescriptor.getName(), - attributeIndex, - bootPropertyDescriptor, - entityPersister, - propertyAccess, - compositeType.getCascadeStyle( attributeIndex ), - compositeType.getFetchMode( attributeIndex ), - creationProcess - ); - } - else if ( subtype instanceof EntityType ) { - final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner() - .getEntityName() ); - - attributeMapping = MappingModelCreationHelper.buildSingularAssociationAttributeMapping( - bootPropertyDescriptor.getName(), - navigableRole.append( bootPropertyDescriptor.getName() ), - attributeIndex, - bootPropertyDescriptor, - entityPersister, - entityPersister, - (EntityType) subtype, - getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ), - compositeType.getCascadeStyle( attributeIndex ), - creationProcess - ); - columnPosition += bootPropertyDescriptor.getColumnSpan(); - } - else { - throw new MappingException( - String.format( - Locale.ROOT, - "Unable to determine attribute nature : %s#%s", - bootDescriptor.getOwner().getEntityName(), - bootPropertyDescriptor.getName() - ) - ); - } - - addAttribute( (SingularAttributeMapping) attributeMapping ); - - attributeIndex++; - } - - // We need the attribute mapping types to finish initialization first before we can build the column mappings - creationProcess.registerInitializationCallback( - "EmbeddableMappingType(" + navigableRole + ")#initColumnMappings", - this::initColumnMappings + }, + (column, jdbcEnvironment) -> getTableIdentifierExpression( column.getValue().getTable(), jdbcEnvironment ), + this::addAttribute, + () -> { + // We need the attribute mapping types to finish initialization first before we can build the column mappings + creationProcess.registerInitializationCallback( + "IdClassEmbeddable(" + getNavigableRole() + ")#initColumnMappings", + this::initColumnMappings + ); + }, + creationProcess ); - return true; } - - private static String getTableIdentifierExpression(Table table, JdbcEnvironment jdbcEnvironment) { return jdbcEnvironment .getQualifiedObjectNameFormatter().format( @@ -749,6 +502,10 @@ private boolean initColumnMappings() { return true; } + private void addAttribute(AttributeMapping attributeMapping) { + addAttribute( (SingularAttributeMapping) attributeMapping ); + } + private void addAttribute(SingularAttributeMapping attributeMapping) { // check if we've already seen this attribute... for ( int i = 0; i < attributeMappings.size(); i++ ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualIdEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualIdEmbeddable.java index 2186d36b4d..7af7957bb1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualIdEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualIdEmbeddable.java @@ -6,29 +6,14 @@ */ package org.hibernate.metamodel.mapping.internal; -import java.io.Serializable; -import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.function.Consumer; -import org.hibernate.MappingException; import org.hibernate.NotYetImplementedFor6Exception; -import org.hibernate.SharedSessionContract; -import org.hibernate.dialect.Dialect; -import org.hibernate.engine.FetchTiming; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.CascadeStyle; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.mapping.Any; -import org.hibernate.mapping.BasicValue; -import org.hibernate.mapping.Column; import org.hibernate.mapping.Component; import org.hibernate.mapping.IndexedConsumer; -import org.hibernate.mapping.Property; -import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Table; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; @@ -44,11 +29,8 @@ import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMappings; import org.hibernate.metamodel.mapping.SingularAttributeMapping; -import org.hibernate.metamodel.mapping.StateArrayContributorMetadata; -import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -56,15 +38,9 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.AnyType; -import org.hibernate.type.BasicType; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; -import org.hibernate.type.EntityType; -import org.hibernate.type.Type; -import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; -import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.spi.CompositeTypeImplementor; -import org.hibernate.type.spi.TypeConfiguration; import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; import static org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping.IdentifierValueMapper; @@ -375,259 +351,37 @@ private boolean finishInitialization( String rootTableExpression, String[] rootTableKeyColumnNames, MappingModelCreationProcess creationProcess) { - final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory(); - final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); - final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); - final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); - final Dialect dialect = jdbcEnvironment.getDialect(); - - final Type[] subtypes = compositeType.getSubtypes(); - - int attributeIndex = 0; - int columnPosition = 0; // Reset the attribute mappings that were added in previous attempts this.attributeMappings.clear(); - final Iterator<Property> propertyIterator = bootDescriptor.getPropertyIterator(); - while ( propertyIterator.hasNext() ) { - final Property bootPropertyDescriptor = propertyIterator.next(); - - final PropertyAccess propertyAccess = getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ); - - final AttributeMapping attributeMapping; - - final Type subtype = subtypes[ attributeIndex ]; - if ( subtype instanceof BasicType ) { - final BasicValue basicValue = (BasicValue) bootPropertyDescriptor.getValue(); - final Selectable selectable = basicValue.getColumn(); - final String containingTableExpression; - final String columnExpression; - if ( rootTableKeyColumnNames == null ) { - if ( selectable.isFormula() ) { - columnExpression = selectable.getTemplate( dialect, creationProcess.getSqmFunctionRegistry() ); + return finishInitialization( + navigableRole, + bootDescriptor, + compositeType, + rootTableExpression, + rootTableKeyColumnNames, + this, + representationStrategy, + (attributeName, attributeType) -> { + if ( attributeType instanceof CollectionType ) { + throw new IllegalAttributeType( "A \"virtual id\" cannot define collection attributes : " + attributeName ); } - else { - columnExpression = selectable.getText( dialect ); + if ( attributeType instanceof AnyType ) { + throw new IllegalAttributeType( "A \"virtual id\" cannot define <any/> attributes : " + attributeName ); } - if ( selectable instanceof Column ) { - containingTableExpression = getTableIdentifierExpression( - ( (Column) selectable ).getValue().getTable(), - jdbcEnvironment - ); - } - else { - containingTableExpression = rootTableExpression; - } - } - else { - containingTableExpression = rootTableExpression; - columnExpression = rootTableKeyColumnNames[ columnPosition ]; - } - - attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping( - bootPropertyDescriptor.getName(), - navigableRole.append( bootPropertyDescriptor.getName() ), - attributeIndex, - bootPropertyDescriptor, - this, - (BasicType<?>) subtype, - containingTableExpression, - columnExpression, - selectable.isFormula(), - selectable.getCustomReadExpression(), - selectable.getCustomWriteExpression(), - propertyAccess, - compositeType.getCascadeStyle( attributeIndex ), - creationProcess - ); - - columnPosition++; - } - else if ( subtype instanceof AnyType ) { - final Any bootValueMapping = (Any) bootPropertyDescriptor.getValue(); - final AnyType anyType = (AnyType) subtype; - - final boolean nullable = bootValueMapping.isNullable(); - final boolean insertable = bootPropertyDescriptor.isInsertable(); - final boolean updateable = bootPropertyDescriptor.isUpdateable(); - final boolean includeInOptimisticLocking = bootPropertyDescriptor.isOptimisticLocked(); - final CascadeStyle cascadeStyle = compositeType.getCascadeStyle( attributeIndex ); - final MutabilityPlan<?> mutabilityPlan; - - if ( updateable ) { - mutabilityPlan = new MutabilityPlan<Object>() { - @Override - public boolean isMutable() { - return true; - } - - @Override - public Object deepCopy(Object value) { - if ( value == null ) { - return null; - } - - return anyType.deepCopy( value, creationProcess.getCreationContext().getSessionFactory() ); - } - - @Override - public Serializable disassemble(Object value, SharedSessionContract session) { - throw new NotYetImplementedFor6Exception( getClass() ); - } - - @Override - public Object assemble(Serializable cached, SharedSessionContract session) { - throw new NotYetImplementedFor6Exception( getClass() ); - } - }; - } - else { - mutabilityPlan = ImmutableMutabilityPlan.INSTANCE; - } - - final StateArrayContributorMetadataAccess attributeMetadataAccess = entityMappingType -> new StateArrayContributorMetadata() { - @Override - public PropertyAccess getPropertyAccess() { - return propertyAccess; - } - - @Override - public MutabilityPlan<?> getMutabilityPlan() { - return mutabilityPlan; - } - - @Override - public boolean isNullable() { - return nullable; - } - - @Override - public boolean isInsertable() { - return insertable; - } - - @Override - public boolean isUpdatable() { - return updateable; - } - - @Override - public boolean isIncludedInDirtyChecking() { - // todo (6.0) : do not believe this is correct - return updateable; - } - - @Override - public boolean isIncludedInOptimisticLocking() { - return includeInOptimisticLocking; - } - - @Override - public CascadeStyle getCascadeStyle() { - return cascadeStyle; - } - }; - - attributeMapping = new DiscriminatedAssociationAttributeMapping( - navigableRole.append( bootPropertyDescriptor.getName() ), - typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Object.class ), - this, - attributeIndex, - attributeMetadataAccess, - bootPropertyDescriptor.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE, - propertyAccess, - bootPropertyDescriptor, - anyType, - bootValueMapping, - creationProcess - ); - } - else if ( subtype instanceof CompositeType ) { - final CompositeType subCompositeType = (CompositeType) subtype; - final int columnSpan = subCompositeType.getColumnSpan( sessionFactory ); - final String subTableExpression; - final String[] subRootTableKeyColumnNames; - if ( rootTableKeyColumnNames == null ) { - subTableExpression = rootTableExpression; - subRootTableKeyColumnNames = null; - } - else { - subTableExpression = rootTableExpression; - subRootTableKeyColumnNames = new String[ columnSpan ]; - System.arraycopy( rootTableKeyColumnNames, columnPosition, subRootTableKeyColumnNames, 0, columnSpan ); - } - - attributeMapping = MappingModelCreationHelper.buildEmbeddedAttributeMapping( - bootPropertyDescriptor.getName(), - attributeIndex, - bootPropertyDescriptor, - this, - subCompositeType, - subTableExpression, - subRootTableKeyColumnNames, - propertyAccess, - compositeType.getCascadeStyle( attributeIndex ), - creationProcess - ); - - columnPosition += columnSpan; - } - else if ( subtype instanceof CollectionType ) { - final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner() - .getEntityName() ); - - attributeMapping = MappingModelCreationHelper.buildPluralAttributeMapping( - bootPropertyDescriptor.getName(), - attributeIndex, - bootPropertyDescriptor, - entityPersister, - propertyAccess, - compositeType.getCascadeStyle( attributeIndex ), - compositeType.getFetchMode( attributeIndex ), - creationProcess - ); - } - else if ( subtype instanceof EntityType ) { - final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner() - .getEntityName() ); - - attributeMapping = MappingModelCreationHelper.buildSingularAssociationAttributeMapping( - bootPropertyDescriptor.getName(), - navigableRole.append( bootPropertyDescriptor.getName() ), - attributeIndex, - bootPropertyDescriptor, - entityPersister, - entityPersister, - (EntityType) subtype, - getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ), - compositeType.getCascadeStyle( attributeIndex ), - creationProcess - ); - columnPosition += bootPropertyDescriptor.getColumnSpan(); - } - else { - throw new MappingException( - String.format( - Locale.ROOT, - "Unable to determine attribute nature : %s#%s", - bootDescriptor.getOwner().getEntityName(), - bootPropertyDescriptor.getName() - ) - ); - } - - addAttribute( (SingularAttributeMapping) attributeMapping ); - - attributeIndex++; - } - - // We need the attribute mapping types to finish initialization first before we can build the column mappings - creationProcess.registerInitializationCallback( - "EmbeddableMappingType(" + navigableRole + ")#initColumnMappings", - this::initColumnMappings + }, + (column, jdbcEnvironment) -> getTableIdentifierExpression( column.getValue().getTable(), jdbcEnvironment ), + this::addAttribute, + () -> { + // We need the attribute mapping types to finish initialization first before we can build the column mappings + creationProcess.registerInitializationCallback( + "VirtualIdEmbeddable(" + navigableRole + ")#initColumnMappings", + this::initColumnMappings + ); + }, + creationProcess ); - return true; } @@ -645,6 +399,10 @@ private boolean initColumnMappings() { return true; } + private void addAttribute(AttributeMapping attributeMapping) { + addAttribute( (SingularAttributeMapping) attributeMapping ); + } + private void addAttribute(SingularAttributeMapping attributeMapping) { // check if we've already seen this attribute... for ( int i = 0; i < attributeMappings.size(); i++ ) {