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++ ) {