From a8e881e6460fb45d26ce6715f71e8ebb30902dd6 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 13 Jan 2025 15:27:13 +0100 Subject: [PATCH 1/2] HHH-7135 Tests for derived identity with joined inheritance --- .../DerivedIdentityJoinedInheritanceTest.java | 125 +++++++++++++++++ .../MapsIdJoinedInheritanceTest.java | 130 ++++++++++++++++++ .../MapsIdUnownedOneToOneTest.java | 126 +++++++++++++++++ 3 files changed, 381 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/DerivedIdentityJoinedInheritanceTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/MapsIdJoinedInheritanceTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/MapsIdUnownedOneToOneTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/DerivedIdentityJoinedInheritanceTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/DerivedIdentityJoinedInheritanceTest.java new file mode 100644 index 000000000000..53790d06105b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/DerivedIdentityJoinedInheritanceTest.java @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.derivedidentities; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + + +@DomainModel( + annotatedClasses = { + DerivedIdentityJoinedInheritanceTest.ThingHolder.class, DerivedIdentityJoinedInheritanceTest.AThing.class, DerivedIdentityJoinedInheritanceTest.Thing1.class, DerivedIdentityJoinedInheritanceTest.Thing2.class + } +) +@SessionFactory +@Jira("https://hibernate.atlassian.net/browse/HHH-7135") +public class DerivedIdentityJoinedInheritanceTest { + + @Test + public void testInsertIntoMap(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + ThingHolder holder = new ThingHolder(); + Thing1 thing1 = new Thing1( holder, "test" ); + session.persist( holder ); + session.persist( thing1 ); + } + ); + + } + + @Entity(name = "AThing") + @Inheritance(strategy = InheritanceType.JOINED) + public abstract class AThing { + public AThing() { + } + + public AThing(ThingHolder holder) { + this.holder = holder; + } + + @Id + @OneToOne + @JoinColumn(name = "id") + private ThingHolder holder; + + public ThingHolder getHolder() { + return holder; + } + } + + @Entity(name = "ThingHolder") + public class ThingHolder { + public ThingHolder() { + } + + @Id + @GeneratedValue + private Integer id; + @OneToOne + private AThing thing; + + public Integer getId() { + return id; + } + + public AThing getThing() { + return thing; + } + + public void setThing(AThing thing) { + this.thing = thing; + } + } + + @Entity(name = "Thing1") + public class Thing1 extends AThing { + public Thing1() { + super(); + } + + public Thing1(ThingHolder holder, String string) { + super( holder ); + this.string = string; + } + + @Basic + private String string; + + public String getString() { + return string; + } + } + + @Entity(name = "Thing2") + public class Thing2 extends AThing { + public Thing2() { + super(); + } + + public Thing2(ThingHolder holder, Integer integer) { + super( holder ); + this.integer = integer; + } + + @Basic + private Integer integer; + + public Integer getInteger() { + return integer; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/MapsIdJoinedInheritanceTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/MapsIdJoinedInheritanceTest.java new file mode 100644 index 000000000000..6992683c3177 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/MapsIdJoinedInheritanceTest.java @@ -0,0 +1,130 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.derivedidentities; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + + +@DomainModel( + annotatedClasses = { + MapsIdJoinedInheritanceTest.ThingHolder.class, MapsIdJoinedInheritanceTest.AThing.class, MapsIdJoinedInheritanceTest.Thing1.class, MapsIdJoinedInheritanceTest.Thing2.class + } +) +@SessionFactory +@Jira("https://hibernate.atlassian.net/browse/HHH-7135") +public class MapsIdJoinedInheritanceTest { + + @Test + public void testInsertIntoMap(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + ThingHolder holder = new ThingHolder(); + Thing1 thing1 = new Thing1( holder, "test" ); + session.persist( holder ); + session.persist( thing1 ); + } + ); + + } + + @Entity(name = "AThing") + @Inheritance(strategy = InheritanceType.JOINED) + public abstract class AThing { + public AThing() { + } + + public AThing(ThingHolder holder) { + this.holder = holder; + } + + @Id + private Integer id; + @MapsId + @OneToOne + private ThingHolder holder; + + public Integer getId() { + return id; + } + + public ThingHolder getHolder() { + return holder; + } + } + + @Entity(name = "ThingHolder") + public class ThingHolder { + public ThingHolder() { + } + + @Id + @GeneratedValue + private Integer id; + @OneToOne + private AThing thing; + + public Integer getId() { + return id; + } + + public AThing getThing() { + return thing; + } + + public void setThing(AThing thing) { + this.thing = thing; + } + } + + @Entity(name = "Thing1") + public class Thing1 extends AThing { + public Thing1() { + super(); + } + + public Thing1(ThingHolder holder, String string) { + super( holder ); + this.string = string; + } + + @Basic + private String string; + + public String getString() { + return string; + } + } + + @Entity(name = "Thing2") + public class Thing2 extends AThing { + public Thing2() { + super(); + } + + public Thing2(ThingHolder holder, Integer integer) { + super( holder ); + this.integer = integer; + } + + @Basic + private Integer integer; + + public Integer getInteger() { + return integer; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/MapsIdUnownedOneToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/MapsIdUnownedOneToOneTest.java new file mode 100644 index 000000000000..25395bcae3bc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/MapsIdUnownedOneToOneTest.java @@ -0,0 +1,126 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.derivedidentities; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; +import org.hibernate.AnnotationException; +import org.hibernate.boot.MetadataSources; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.ServiceRegistryScope; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.fail; + +@ServiceRegistry +@Jira("https://hibernate.atlassian.net/browse/HHH-7135") +public class MapsIdUnownedOneToOneTest { + + @Test + public void testInvalidMapping(ServiceRegistryScope scope) { + MetadataSources metadataSources = new MetadataSources( scope.getRegistry() ) + .addAnnotatedClasses( MapsIdUnownedOneToOneTest.ThingHolder.class, MapsIdUnownedOneToOneTest.AThing.class, MapsIdUnownedOneToOneTest.Thing1.class, MapsIdUnownedOneToOneTest.Thing2.class ); + try { + metadataSources.buildMetadata(); + fail( "Was expecting failure" ); + } + catch (AnnotationException ignore) { + } + } + + @Entity(name = "AThing") + @Inheritance(strategy = InheritanceType.JOINED) + public abstract class AThing { + public AThing() { + } + + public AThing(ThingHolder holder) { + this.holder = holder; + } + + @Id + private Integer id; + @MapsId + @OneToOne(mappedBy = "thing") + private ThingHolder holder; + + public Integer getId() { + return id; + } + + public ThingHolder getHolder() { + return holder; + } + } + + @Entity(name = "ThingHolder") + public class ThingHolder { + public ThingHolder() { + } + + @Id + @GeneratedValue + private Integer id; + @OneToOne + private AThing thing; + + public Integer getId() { + return id; + } + + public AThing getThing() { + return thing; + } + + public void setThing(AThing thing) { + this.thing = thing; + } + } + + @Entity(name = "Thing1") + public class Thing1 extends AThing { + public Thing1() { + super(); + } + + public Thing1(ThingHolder holder, String string) { + super( holder ); + this.string = string; + } + + @Basic + private String string; + + public String getString() { + return string; + } + } + + @Entity(name = "Thing2") + public class Thing2 extends AThing { + public Thing2() { + super(); + } + + public Thing2(ThingHolder holder, Integer integer) { + super( holder ); + this.integer = integer; + } + + @Basic + private Integer integer; + + public Integer getInteger() { + return integer; + } + } +} From 4fac597229f3acafcc4cf504af742f9a3f120eae Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 13 Jan 2025 15:27:40 +0100 Subject: [PATCH 2/2] HHH-7135 Disallow @MapsId on unowned @OneToOne --- .../org/hibernate/boot/model/internal/ColumnsBuilder.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ColumnsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ColumnsBuilder.java index 1cb4a6e9ca75..7c87992f6077 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ColumnsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ColumnsBuilder.java @@ -289,6 +289,11 @@ private JoinColumn[] getJoinColumnAnnotations(MemberDetails property, PropertyDa */ AnnotatedColumns overrideColumnFromMapperOrMapsIdProperty(PropertyData override) { if ( override != null ) { + final OneToOne oneToOneAnn = override.getAttributeMember().getDirectAnnotationUsage( OneToOne.class ); + if ( oneToOneAnn != null && nullIfEmpty( oneToOneAnn.mappedBy() ) != null ) { + throw new AnnotationException( "Property '" + getPath( propertyHolder, override ) + + "' is annotated '@MapsId' but has no columns due to its '@OneToOne(mappedBy)' usage" ); + } final AnnotatedJoinColumns joinColumns = buildExplicitJoinColumns( override.getAttributeMember(), override ); return joinColumns == null ? buildDefaultJoinColumnsForToOne( override.getAttributeMember(), override )