From 2404ff56abe4b85008b18d65e3097fb2d6e7de9d Mon Sep 17 00:00:00 2001 From: Luke Chen Date: Tue, 17 Nov 2020 14:48:24 +0800 Subject: [PATCH] HHH-13945: catch PersistenceException during envers unproxy the field value, and fallback to using proxy entity --- .../integration/manytoone/lazy/ChildUser.java | 38 ++++++++ .../lazy/ManyToOneLazyDeleteTest.java | 88 +++++++++++++++++++ .../integration/manytoone/lazy/Shipment.java | 9 ++ .../test/integration/manytoone/lazy/User.java | 43 +++++++++ 4 files changed, 178 insertions(+) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ChildUser.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyDeleteTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/User.java diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ChildUser.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ChildUser.java new file mode 100644 index 0000000000..0d97ec57f1 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ChildUser.java @@ -0,0 +1,38 @@ +/* + * 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.envers.test.integration.manytoone.lazy; + +import org.hibernate.envers.NotAudited; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import java.util.List; + +/** + * @author Luke Chen + */ +@Entity +@Table(name = "child_user") +public class ChildUser extends User { + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.REMOVE) + @NotAudited + private List shipmentList; + + ChildUser() { + } + + public long getId() { + return id; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyDeleteTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyDeleteTest.java new file mode 100644 index 0000000000..ac0ced5971 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/ManyToOneLazyDeleteTest.java @@ -0,0 +1,88 @@ +/* + * 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.envers.test.integration.manytoone.lazy; + +import org.hibernate.Hibernate; +import org.hibernate.envers.configuration.EnversSettings; +import org.hibernate.envers.test.BaseEnversFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Map; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; + +/** + * Tests that proxies can still be resolved correctly in ToOneIdMapper even the object is already deleted and can't + * find in cache. This can happen if the deleted object is an inherited object, and when the child object is deleted, + * we cannot find the object with the parent class name anymore. + * + * @author Luke Chen + */ +@TestForIssue(jiraKey = "HHH-13945") +public class ManyToOneLazyDeleteTest extends BaseEnversFunctionalTestCase { + private Long shipmentId; + private User user; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Shipment.class, Address.class, AddressVersion.class, User.class, ChildUser.class }; + } + + @Test + @Priority(10) + public void initData() { + + this.shipmentId = doInHibernate( this::sessionFactory, session -> { + final Shipment shipment = new Shipment( Instant.now(), "system", Instant.now().plus( Duration.ofDays( 3 ) ), "abcd123", null, null ); + session.persist( shipment ); + session.flush(); + + final Address origin = new Address( Instant.now(), "system", "Valencia#1" ); + final Address destination = new Address( Instant.now(), "system", "Madrid#3" ); + final AddressVersion originVersion0 = origin.addInitialVersion( "Poligono Manises" ); + final AddressVersion destinationVersion0 = destination.addInitialVersion( "Poligono Alcobendas" ); + user = new ChildUser(); + session.persist( origin ); + session.persist( destination ); + session.persist( user ); + + session.flush(); + shipment.setUser( user ); + shipment.setOrigin( originVersion0 ); + shipment.setDestination( destinationVersion0 ); + + session.merge( shipment ); + session.flush(); + + return shipment.getId(); + } ); + + doInHibernate( this::sessionFactory, session -> { + final Shipment shipment = session.get( Shipment.class, shipmentId ); + session.remove(shipment); + // Cast the User instance to the ChildUser, and delete the child one, so the cache for + // the User instance will not be there, and entityNotFound exception will be thrown while envers processing it + ChildUser childUser = session.get(ChildUser.class, user.getId()); + session.remove(childUser); + + session.flush(); + } ); + } + + @Override + protected void addSettings(Map settings) { + super.addSettings( settings ); + + settings.put(EnversSettings.STORE_DATA_AT_DELETE, "true"); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/Shipment.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/Shipment.java index 2dae856930..d21a4b7ca3 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/Shipment.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/Shipment.java @@ -62,6 +62,11 @@ public class Shipment extends BaseDomainEntity { @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) private AddressVersion destination; + @ManyToOne(optional = true, fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", referencedColumnName = "id", nullable = true) + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) + private User user; + Shipment() { } @@ -104,4 +109,8 @@ public class Shipment extends BaseDomainEntity { public void setDestination(AddressVersion destination) { this.destination = destination; } + + public void setUser(User user) { + this.user = user; + } } \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/User.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/User.java new file mode 100644 index 0000000000..a4847952df --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/lazy/User.java @@ -0,0 +1,43 @@ +/* + * 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.envers.test.integration.manytoone.lazy; + +import org.hibernate.envers.NotAudited; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import java.time.Instant; +import java.util.List; + +/** + * @author Luke Chen + */ +@Entity +@Table(name = "user") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + protected long id = 0; + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.REMOVE) + @NotAudited + private List shipmentList; + + User() { + } + + public long getId() { + return id; + } +} \ No newline at end of file