diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java index 02276925acb5..e3a60f0a0e23 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java @@ -16,6 +16,7 @@ import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.Component; import org.hibernate.mapping.MappedSuperclass; @@ -350,11 +351,17 @@ else if ( MappedSuperclass.class.isAssignableFrom( mapping.getClass() ) ) { // applyNaturalIdAttribute( safeMapping, jpaType ); for ( Property property : safeMapping.getDeclaredProperties() ) { - if ( !safeMapping.isVersioned() - // skip the version property, it was already handled previously. - || property != safeMapping.getVersion() ) { - buildAttribute( property, jpaType ); + if ( isIdentifierProperty( property, safeMapping ) ) { + // property represents special handling for id-class mappings but we have already + // accounted for the embedded property mappings in #applyIdMetadata && + // #buildIdClassAttributes + continue; + } + if ( safeMapping.isVersioned() && property == safeMapping.getVersion() ) { + // skip the version property, it was already handled previously. + continue; } + buildAttribute( property, jpaType ); } ( (AttributeContainer) jpaType ).getInFlightAccess().finishUp(); @@ -406,6 +413,12 @@ else if ( MappedSuperclass.class.isAssignableFrom( mapping.getClass() ) ) { } } + private static boolean isIdentifierProperty(Property property, MappedSuperclass mappedSuperclass) { + final Component identifierMapper = mappedSuperclass.getIdentifierMapper(); + return identifierMapper != null ? + ArrayHelper.contains( identifierMapper.getPropertyNames(), property.getName() ) : false; + } + private void addAttribute(EmbeddableDomainType embeddable, Property property, Component component) { final PersistentAttribute attribute = attributeFactory.buildAttribute( embeddable, property); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/BaseSummary.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/BaseSummary.java new file mode 100644 index 000000000000..03f409ca011f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/BaseSummary.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.idclass; + +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.MappedSuperclass; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Objects; + +@IdClass(PKey.class) +@MappedSuperclass +public class BaseSummary implements Serializable { + + @Id + private Integer year; + @Id + private Integer month; + private BigDecimal value; + + public Integer getYear() { + return year; + } + + public void setYear(Integer year) { + this.year = year; + } + + public Integer getMonth() { + return month; + } + + public void setMonth(Integer month) { + this.month = month; + } + + public BigDecimal getValue() { + return value; + } + + public void setValue(BigDecimal value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + BaseSummary that = (BaseSummary) o; + return Objects.equals( year, that.year ) && Objects.equals( month, that.month ); + } + + @Override + public int hashCode() { + return Objects.hash( year, month ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/MappedSuperclassIdClassAttributesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/MappedSuperclassIdClassAttributesTest.java new file mode 100644 index 000000000000..d7d8738e2200 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/MappedSuperclassIdClassAttributesTest.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.idclass; + +import jakarta.persistence.metamodel.SingularAttribute; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel(annotatedClasses = {Summary.class, BaseSummary.class}) +@SessionFactory +@JiraKey( "HHH-18858" ) +public class MappedSuperclassIdClassAttributesTest { + + @Test + public void test(SessionFactoryScope scope) { + scope.inSession( entityManager -> { + final var yearAttribute = Summary_.year.getDeclaringType().getAttribute( "year" ); + assertThat( yearAttribute ).isEqualTo( Summary_.year ); + assertThat( ((SingularAttribute) yearAttribute).isId() ).isTrue(); + + final var monthAttribute = Summary_.month.getDeclaringType().getAttribute( "month" ); + assertThat( monthAttribute ).isEqualTo( Summary_.month ); + assertThat( ((SingularAttribute) monthAttribute).isId() ).isTrue(); + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/PKey.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/PKey.java new file mode 100644 index 000000000000..5b4a8ea3b500 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/PKey.java @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.idclass; + +import java.io.Serializable; +import java.util.Objects; + +public class PKey implements Serializable { + + private Integer year; + private Integer month; + + public Integer getYear() { + return year; + } + + public void setYear(Integer year) { + this.year = year; + } + + public Integer getMonth() { + return month; + } + + public void setMonth(Integer month) { + this.month = month; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + PKey pKey = (PKey) o; + return Objects.equals( year, pKey.year ) && Objects.equals( month, pKey.month ); + } + + @Override + public int hashCode() { + return Objects.hash( year, month ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/Summary.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/Summary.java new file mode 100644 index 000000000000..391717ad3835 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/Summary.java @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.idclass; + +import jakarta.persistence.Entity; + +@Entity +public class Summary extends BaseSummary { +}