From 085b0dfcea1f32655ebb0114e6147a3522b11362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Fri, 3 Jan 2025 11:24:46 +0100 Subject: [PATCH 1/3] HHH-18933 Test case adapted from reproducer mentioned in https://hibernate.atlassian.net/browse/HHH-18933 --- .../test/inheritance/HierarchyOrderTest.java | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/HierarchyOrderTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/HierarchyOrderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/HierarchyOrderTest.java new file mode 100644 index 000000000000..5913429d224a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/HierarchyOrderTest.java @@ -0,0 +1,195 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.inheritance; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.TypedQuery; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@DomainModel( + annotatedClasses = { + HierarchyOrderTest.DerOA.class, + HierarchyOrderTest.DerDA.class, + HierarchyOrderTest.DerDB.class, + HierarchyOrderTest.DerOB.class, + HierarchyOrderTest.BaseD.class, + HierarchyOrderTest.BaseO.class + } +) +@SessionFactory +class HierarchyOrderTest { + + private EntityManagerFactory emf; + private DerOA deroa; + private DerOB derob; + + @BeforeEach + void setUp() { + DerDB derba1 = new DerDB( 5 ); + DerDA derda1 = new DerDA( "1", "abase" ); + deroa = new DerOA( derda1 ); + derob = new DerOB( derba1 ); +// emf = buildEntityManagerFactory(); + } + + @Test + void testBaseProperty(SessionFactoryScope scope) { + scope.inSession( em -> { + em.getTransaction().begin(); + em.persist( deroa ); + em.persist( derob ); + em.getTransaction().commit(); + Integer ida = deroa.getId(); + Integer idb = derob.getId(); + em.clear(); + TypedQuery qa = em.createQuery( "select o from DerOA o where o.id =:id", DerOA.class ); + qa.setParameter( "id", ida ); + DerOA deroain = qa.getSingleResult(); + assertEquals( "abase", deroain.derda.baseprop ); + } ); + } + + @Test + void testDerivedProperty(SessionFactoryScope scope) { + scope.inSession( em -> { + em.getTransaction().begin(); + em.persist( deroa ); + em.persist( derob ); + em.getTransaction().commit(); + Integer idb = derob.getId(); + em.clear(); + + TypedQuery qb = em.createQuery( "select o from DerOB o where o.id =:id", DerOB.class ); + qb.setParameter( "id", idb ); + DerOB derobin = qb.getSingleResult(); + assertNotNull( derobin ); + assertEquals( 5, derobin.derdb().b ); + } ); + } + + /* + * Created on 03/12/2024 by Paul Harrison (paul.harrison@manchester.ac.uk). + */ + @Entity(name = "DerOA") + public static class DerOA extends BaseO { + public DerOA(DerDA derda) { + this.derda = derda; + } + + @Embedded + // @AttributeOverrides({ + // @AttributeOverride(name="a",column = @Column(name = "da")) + // }) + public BaseD derda; + + public DerOA() { + + } + } + + /* + * Created on 03/12/2024 by Paul Harrison (paul.harrison@manchester.ac.uk). + */ + @Embeddable + public static class DerDB extends BaseD { + public DerDB(int b) { + this.b = b; + } + + public int b; + + public DerDB() { + + } + } + + /* + * Created on 03/12/2024 by Paul Harrison (paul.harrison@manchester.ac.uk). + */ + @Embeddable + public static class DerDA extends BaseD { + public DerDA(String a, String bprop) { + super( bprop ); + this.a = a; + } + + public String a; + + public DerDA() { + + } + } + + /* + * Created on 03/12/2024 by Paul Harrison (paul.harrison@manchester.ac.uk). + */ + @Embeddable + public abstract static class BaseD { //TODO would really like this to be abstract + public String baseprop; + + public BaseD(String baseprop) { + this.baseprop = baseprop; + } + + public BaseD() { + + } + + public String getBaseprop() { + return baseprop; + } + + } + + @Entity(name = "BaseO") + @Inheritance(strategy = InheritanceType.JOINED) + public abstract static class BaseO { + @Id + @GeneratedValue + private Integer id; + + public Integer getId() { + return id; + } + } + + /* + * Created on 03/12/2024 by Paul Harrison (paul.harrison@manchester.ac.uk). + */ + @Entity(name = "DerOB") + public static class DerOB extends BaseO { + public DerOB(DerDB derdb) { + this.derdb = derdb; + } + + @Embedded + BaseD derdb; + + public DerOB() { + + } + + public DerDB derdb() { + return (DerDB) derdb; + } + } +} From 1f806cb274e8a761c2f1b06e40f966d973f57c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Sat, 11 Jan 2025 09:18:11 +0100 Subject: [PATCH 2/3] HHH-18933 Test case using classes from article https://in.relation.to/2024/07/12/embeddable-inheritance/ --- ...beddableInheritanceHierarchyOrderTest.java | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/EmbeddableInheritanceHierarchyOrderTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/EmbeddableInheritanceHierarchyOrderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/EmbeddableInheritanceHierarchyOrderTest.java new file mode 100644 index 000000000000..bb04a425bdbf --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/EmbeddableInheritanceHierarchyOrderTest.java @@ -0,0 +1,243 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.inheritance; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +@DomainModel( + annotatedClasses = { + EmbeddableInheritanceHierarchyOrderTest.Animal.class, + EmbeddableInheritanceHierarchyOrderTest.Cat.class, + EmbeddableInheritanceHierarchyOrderTest.Dog.class, + EmbeddableInheritanceHierarchyOrderTest.Fish.class, + EmbeddableInheritanceHierarchyOrderTest.Mammal.class, + // If Mammal is moved right under Animal (before Dog and Cat), test will pass + EmbeddableInheritanceHierarchyOrderTest.Owner.class + } +) +@SessionFactory +public class EmbeddableInheritanceHierarchyOrderTest { + + @AfterAll + static void clean(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from Owner" ).executeUpdate() ); + } + + @Test + public void test(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.persist( new Owner( 1L, new Animal( 2, "Agapius" ) ) ); + session.persist( new Owner( 2L, new Cat( 3, "Bercharius", "Blaesilla" ) ) ); + session.persist( new Owner( 3L, new Dog( 4, "Censurius", "Caesarea" ) ) ); + session.persist( new Owner( 4L, new Fish( 5, "Dionysius", 3 ) ) ); + session.persist( new Owner( 5L, new Mammal( 6, "Epagraphas", "Eanswida" ) ) ); + } ); + scope.inSession( session -> { + final Owner animalOwner = session.find( Owner.class, 1L ); + assertEquals( 2, animalOwner.getPet().getAge() ); + assertEquals( "Agapius", animalOwner.getPet().getName() ); + + final Owner fishOwner = session.find( Owner.class, 4L ); + if ( fishOwner.getPet() instanceof Fish ) { + final Fish fish = (Fish) fishOwner.getPet(); + assertEquals( 5, fish.getAge() ); + assertEquals( "Dionysius", fish.getName() ); + assertEquals( 3, fish.getFins() ); + } + else { + fail( "Not fish owner" ); + } + + final Owner mammalOwner = session.find( Owner.class, 5L ); + if ( mammalOwner.getPet() instanceof Mammal ) { + final Mammal mammal = (Mammal) mammalOwner.getPet(); + assertEquals( 6, mammal.getAge() ); + assertEquals( "Epagraphas", mammal.getName() ); + assertEquals( "Eanswida", mammal.getMother() ); + } + else { + fail( "Not mammal owner" ); + } + + final Owner catOwner = session.find( Owner.class, 2L ); + if ( catOwner.getPet() instanceof Cat ) { + final Cat cat = (Cat) catOwner.getPet(); + assertEquals( 3, cat.getAge() ); + assertEquals( "Bercharius", cat.getName() ); + assertEquals( "Blaesilla", cat.getMother() ); + } + else { + fail( "Not cat owner" ); + } + + final Owner dogOwner = session.find( Owner.class, 3L ); + if ( dogOwner.getPet() instanceof Dog ) { + final Dog dog = (Dog) dogOwner.getPet(); + assertEquals( 4, dog.getAge() ); + assertEquals( "Censurius", dog.getName() ); + assertEquals( "Caesarea", dog.getMother() ); + } + else { + fail( "Not dog owner" ); + } + } ); + } + + @Embeddable + @DiscriminatorColumn(name = "animal_type", length = 64) + static + class Animal { + private int age; + + private String name; + + public Animal() { + } + + public Animal(int age, String name) { + this.age = age; + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Embeddable + static + class Cat extends Mammal { + //private int mouse; + // [...] + + + public Cat() { + super(); + } + + public Cat(int age, String name, String mother) { + super( age, name, mother ); + } + } + + @Embeddable + static + class Dog extends Mammal { + //private int bone; + // [...] + + public Dog() { + } + + public Dog(int age, String name, String mother) { + super( age, name, mother ); + } + } + + @Embeddable + static + class Fish extends Animal { + private int fins; + + public Fish() { + } + + public Fish(int age, String name, int fins) { + super( age, name ); + this.fins = fins; + } + + public int getFins() { + return fins; + } + + public void setFins(int fins) { + this.fins = fins; + } + } + + @Embeddable + static + class Mammal extends Animal { + private String mother; + + public Mammal() { + } + + public Mammal(int age, String name, String mother) { + super( age, name ); + this.mother = mother; + } + + public String getMother() { + return mother; + } + + public void setMother(String mother) { + this.mother = mother; + } + } + + @Entity(name = "Owner") + static + class Owner { + @Id + private Long id; + + @Embedded + private Animal pet; + + public Owner() { + } + + public Owner(Long id, Animal pet) { + this.id = id; + this.pet = pet; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Animal getPet() { + return pet; + } + + public void setPet(Animal pet) { + this.pet = pet; + } + } +} From 05d3a3c46b42393cc96d4e7dab4611bbf2fd351c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Fri, 3 Jan 2025 13:29:49 +0100 Subject: [PATCH 3/3] HHH-18933 Hierarchically ordering classes before returning from method in equivalent way as in main (7.0) branch --- ...AnnotationMetadataSourceProcessorImpl.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java index fde786ffcbf3..6e6d592a98f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java @@ -304,7 +304,28 @@ private List orderAndFillHierarchy(List classes) { } } } - return new ArrayList<>( orderedClasses ); + + // order the hierarchy + ArrayList workingCopy = new ArrayList<>( orderedClasses ); + List newList = new ArrayList<>( orderedClasses.size() ); + while ( !workingCopy.isEmpty() ) { + XClass clazz = workingCopy.get( 0 ); + orderHierarchy( workingCopy, newList, orderedClasses, clazz ); + } + return newList; + } + + private void orderHierarchy(List copy, List newList, LinkedHashSet original, XClass clazz) { + if ( clazz != null && !Object.class.getName().equals( clazz.getName() ) ) { + //process superclass first + orderHierarchy( copy, newList, original, clazz.getSuperclass() ); + if ( original.contains( clazz ) ) { + if ( !newList.contains( clazz ) ) { + newList.add( clazz ); + } + copy.remove( clazz ); + } + } } @Override