diff --git a/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/HHH18782_JPAUnitTestCase.java b/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/HHH18782_JPAUnitTestCase.java new file mode 100644 index 00000000..236f2743 --- /dev/null +++ b/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/HHH18782_JPAUnitTestCase.java @@ -0,0 +1,94 @@ +package org.hibernate.bugs; + +import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.bugs.entities.Cart; +import org.hibernate.bugs.entities.Item; +import org.hibernate.bugs.entities.Wheel; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.testing.orm.junit.JiraKey; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Persistence; + +/** + * Reproducer of HHH-18782 + *

+ * “enable_lazy_load_no_trans=true” creates new instance of parent for each lazily loaded child relation + * + * @see HHH-18782 + */ +@JiraKey("HHH-18782") +class HHH18782_JPAUnitTestCase { + + private EntityManagerFactory entityManagerFactory; + + @BeforeEach + void init() { + Map properties = new HashMap<>(); + properties.put(AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, "true"); + entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" , properties); + } + + @AfterEach + void destroy() { + entityManagerFactory.close(); + } + + // Entities are auto-discovered, so just add them anywhere on class-path + // Add your tests, using standard JUnit. + @Test + void hhh18782Test_LazyLoadedChildrenHaveParentInstanceInInverseRelation() throws Exception { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + entityManager.getTransaction().begin(); + + Cart cart = new Cart(); + cart.setCartNo("1234"); + + cart.addItem(createItem("1")); + cart.addItem(createItem("2")); + cart.addWheel(createWheel("no1")); + + entityManager.persist(cart); + entityManager.flush(); + + Long offerId = cart.getId(); + entityManager.clear(); + + Cart cartFromEm = entityManager.find(Cart.class, offerId); + entityManager.getTransaction().commit(); + entityManager.close(); + + // lazy loading + + // Check that we load lazily - working + assertEquals(2, cartFromEm.getItems().size()); + assertEquals(1, cartFromEm.getWheels().size()); + + // Fails from here on >>> + assertSame("item 0 does not have the correct parent instance", cartFromEm, cartFromEm.getItem(0).getCart()); + assertSame("item 1 does not have the correct parent instance", cartFromEm, cartFromEm.getItem(1).getCart()); + assertSame("wheel 0 does not have the correct parent instance", cartFromEm, cartFromEm.getWheel(0).getCart()); + } + + private Item createItem(String itemNo) { + Item item = new Item(); + item.setItemNo(itemNo); + return item; + } + + private Wheel createWheel(String wheelNo) { + Wheel wheel = new Wheel(); + wheel.setWheelNo(wheelNo); + return wheel; + } + +} diff --git a/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Cart.java b/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Cart.java new file mode 100644 index 00000000..f0f33be8 --- /dev/null +++ b/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Cart.java @@ -0,0 +1,104 @@ +package org.hibernate.bugs.entities; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + +@Entity +@Table(name="CART") +public class Cart { + + @Id + @GeneratedValue(generator = "CartSeq", strategy = GenerationType.SEQUENCE) + @SequenceGenerator(name = "CartSeq", sequenceName = "CART_SEQ", allocationSize = 50) + private Long id; + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "cart", targetEntity = Item.class, orphanRemoval = true, cascade = CascadeType.ALL) + private List items = new ArrayList<>(); + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "cart", targetEntity = Wheel.class, orphanRemoval = true, cascade = CascadeType.ALL) + private List wheels = new ArrayList<>(); + + @Column(name = "CART_NO", length = 255) + private String cartNo; + + public List getItems() { + return items; + } + + public Item getItem(int index) { + return items.get(index); + } + + public void addItem(Item objectToAdd) { + if (objectToAdd == null) { + throw new NullPointerException("Can't add null to association Items of " + this); + } + if (items.contains(objectToAdd)) { + return; + } + objectToAdd.setCartInternal(this); + items.add(objectToAdd); + } + + public void removeItem(Item objectToRemove) { + if (objectToRemove == null) { + return; + } + if (items.remove(objectToRemove)) { + objectToRemove.setCartInternal(null); + } + } + + public void addWheel(Wheel objectToAdd) { + if (objectToAdd == null) { + throw new NullPointerException("Can't add null to association Wheels of " + this); + } + if (getWheels().contains(objectToAdd)) { + return; + } + objectToAdd.setCartInternal(this); + getWheels().add(objectToAdd); + } + + public void removeWheel(Wheel objectToRemove) { + if (objectToRemove == null) { + return; + } + if (getWheels().remove(objectToRemove)) { + objectToRemove.setCartInternal(null); + } + } + + public List getWheels() { + return wheels; + } + + public Wheel getWheel(int index) { + return wheels.get(index); + } + + public String getCartNo() { + return cartNo; + } + + public void setCartNo(String orderNo) { + this.cartNo = orderNo; + } + + public Long getId() { + return id; + } + +} diff --git a/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Item.java b/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Item.java new file mode 100644 index 00000000..608a6b0c --- /dev/null +++ b/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Item.java @@ -0,0 +1,54 @@ +package org.hibernate.bugs.entities; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + +@Entity +@Table(name="ITEM") +public class Item { + + @Id + @GeneratedValue(generator = "ItemSeq", strategy = GenerationType.SEQUENCE) + @SequenceGenerator(name = "ItemSeq", sequenceName = "ITEM_SEQ", allocationSize = 50) + private Long id; + + @ManyToOne(fetch = FetchType.EAGER, targetEntity = Cart.class) + @JoinColumn(name = "CART" + + "_ID") + private Cart cart; + + @Column(name = "ITEM_NO", length = 255) + private String itemNo; + + /** + * INTERNAL, managed via {@link Cart#addItem(OfferItem)} + */ + public void setCartInternal(Cart offer) { + this.cart = offer; + } + + public Cart getCart() { + return cart; + } + + public Long getId() { + return id; + } + + public String getItemNo() { + return itemNo; + } + + public void setItemNo(String itemNo) { + this.itemNo = itemNo; + } + +} diff --git a/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Wheel.java b/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Wheel.java new file mode 100644 index 00000000..3e767373 --- /dev/null +++ b/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/entities/Wheel.java @@ -0,0 +1,53 @@ +package org.hibernate.bugs.entities; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + +@Entity +@Table(name="WHEEL") +public class Wheel { + + @Id + @GeneratedValue(generator = "WheelSeq", strategy = GenerationType.SEQUENCE) + @SequenceGenerator(name = "WheelSeq", sequenceName = "WHEEL_SEQ", allocationSize = 50) + private Long id; + + @ManyToOne(fetch = FetchType.EAGER, targetEntity = Cart.class) + @JoinColumn(name = "CART_ID") + private Cart cart; + + @Column(name = "WHEEL_NO", length = 255) + private String wheelNo; + + /** + * INTERNAL, managed via {@link Cart#addItem(OfferItem)} + */ + public void setCartInternal(Cart offer) { + this.cart = offer; + } + + public Cart getCart() { + return cart; + } + + public Long getId() { + return id; + } + + public String getWheelNo() { + return wheelNo; + } + + public void setWheelNo(String wheelNo) { + this.wheelNo = wheelNo; + } + +}