diff --git a/hibernate-core/src/test/java/org/hibernate/test/idclass/IdClassForNestedIdWithAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/test/idclass/IdClassForNestedIdWithAssociationTest.java
new file mode 100644
index 0000000000..73597437db
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/test/idclass/IdClassForNestedIdWithAssociationTest.java
@@ -0,0 +1,196 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate.test.idclass;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hibernate.test.util.SchemaUtil.getColumnNames;
+
+import java.io.Serializable;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.ManyToOne;
+
+import org.hibernate.testing.TestForIssue;
+import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
+import org.junit.Test;
+
+/**
+ * Test that bootstrap doesn't throw an exception
+ * when an entity has a composite ID containing an association to another entity,
+ * which itself has a composite ID containing an association to another entity.
+ *
+ * This test used to fail on bootstrap with the following error:
+ *
+ * org.hibernate.MappingException: identifier mapping has wrong number of columns: org.hibernate.test.idclass.IdClassForNestedIdWithAssociationTest$NestedIdClassEntity type: component[idClassEntity,key3]
+ * at org.hibernate.mapping.RootClass.validate(RootClass.java:273)
+ * at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:359)
+ * at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:307)
+ * at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:471)
+ * at org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase.buildResources(BaseNonConfigCoreFunctionalTestCase.java:165)
+ * at org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase.startUp(BaseNonConfigCoreFunctionalTestCase.java:141)
+ * at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+ * at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
+ * at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
+ * at java.base/java.lang.reflect.Method.invoke(Method.java:566)
+ * at org.hibernate.testing.junit4.TestClassMetadata.performCallbackInvocation(TestClassMetadata.java:205)
+ */
+@TestForIssue(jiraKey = "HHH-14918")
+public class IdClassForNestedIdWithAssociationTest extends BaseNonConfigCoreFunctionalTestCase {
+
+ @Override
+ protected Class>[] getAnnotatedClasses() {
+ return new Class[] { BasicEntity.class, IdClassEntity.class, NestedIdClassEntity.class };
+ }
+
+ @Test
+ public void metadataTest() {
+ assertThat( getColumnNames( "NestedIdClassEntity", metadata() ) )
+ // Just check we're using copied IDs; otherwise the test wouldn't be able to reproduce HHH-14918.
+ .containsExactlyInAnyOrder( "idClassEntity_basicEntity_key1", "idClassEntity_key2", "key3" );
+ }
+
+ // The main goal of the test is to check that bootstrap doesn't throw an exception,
+ // but it feels wrong to have a test class with just an empty test method,
+ // so just check that persisting/loading works correctly.
+ @Test
+ public void smokeTest() {
+ inTransaction( s -> {
+ BasicEntity basic = new BasicEntity( 1L );
+ s.persist( basic );
+ IdClassEntity idClass = new IdClassEntity( basic, 2L );
+ s.persist( idClass );
+ NestedIdClassEntity nestedIdClass = new NestedIdClassEntity( idClass, 3L );
+ s.persist( nestedIdClass );
+ } );
+
+ inTransaction( s -> {
+ NestedIdClassEntity nestedIdClass = s.get(
+ NestedIdClassEntity.class,
+ new NestedIdClassEntity.NestedIdClassEntityId( 1L, 2L, 3L )
+ );
+ assertThat( nestedIdClass )
+ .extracting( NestedIdClassEntity::getKey3 )
+ .isEqualTo( 3L );
+ IdClassEntity idClass = nestedIdClass.getIdClassEntity();
+ assertThat( idClass )
+ .extracting( IdClassEntity::getKey2 )
+ .isEqualTo( 2L );
+ BasicEntity basic = idClass.basicEntity;
+ assertThat( basic )
+ .extracting( BasicEntity::getKey1 )
+ .isEqualTo( 1L );
+ } );
+ }
+
+ @Entity(name = "BasicEntity")
+ public static class BasicEntity {
+ @Id
+ Long key1;
+
+ protected BasicEntity() {
+ }
+
+ public BasicEntity(long key1) {
+ this.key1 = key1;
+ }
+
+ public Long getKey1() {
+ return key1;
+ }
+ }
+
+ @Entity(name = "IdClassEntity")
+ @IdClass(IdClassEntity.IdClassEntityId.class)
+ public static class IdClassEntity {
+ @Id
+ @ManyToOne
+ BasicEntity basicEntity;
+ @Id
+ Long key2;
+
+ protected IdClassEntity() {
+ }
+
+ public IdClassEntity(BasicEntity basicEntity, long key2) {
+ this.basicEntity = basicEntity;
+ this.key2 = key2;
+ }
+
+ public BasicEntity getBasicEntity() {
+ return basicEntity;
+ }
+
+ public Long getKey2() {
+ return key2;
+ }
+
+ public static class IdClassEntityId implements Serializable {
+ long basicEntity;
+ long key2;
+
+ protected IdClassEntityId() {
+ }
+
+ public IdClassEntityId(long basicEntity, long key2) {
+ this.basicEntity = basicEntity;
+ this.key2 = key2;
+ }
+
+ public long getBasicEntity() {
+ return basicEntity;
+ }
+
+ public long getKey2() {
+ return key2;
+ }
+ }
+ }
+
+ @Entity(name = "NestedIdClassEntity")
+ @IdClass(NestedIdClassEntity.NestedIdClassEntityId.class)
+ public static class NestedIdClassEntity {
+ @Id
+ @ManyToOne
+ IdClassEntity idClassEntity;
+ @Id
+ Long key3;
+
+ protected NestedIdClassEntity() {
+ }
+
+ public NestedIdClassEntity(IdClassEntity idClassEntity, long key3) {
+ this.idClassEntity = idClassEntity;
+ this.key3 = key3;
+ }
+
+ public IdClassEntity getIdClassEntity() {
+ return idClassEntity;
+ }
+
+ public Long getKey3() {
+ return key3;
+ }
+
+ public static class NestedIdClassEntityId implements Serializable {
+ IdClassEntity.IdClassEntityId idClassEntity;
+ long key3;
+
+ protected NestedIdClassEntityId() {
+ }
+
+ public NestedIdClassEntityId(IdClassEntity.IdClassEntityId idClassEntity, long key3) {
+ this.idClassEntity = idClassEntity;
+ this.key3 = key3;
+ }
+
+ public NestedIdClassEntityId(long basicEntity, long key2, long key3) {
+ this( new IdClassEntity.IdClassEntityId( basicEntity, key2 ), key3 );
+ }
+ }
+ }
+}