HHH-13945: catch PersistenceException during envers unproxy the field value, and fallback to using proxy entity

This commit is contained in:
Luke Chen 2020-11-17 14:48:24 +08:00 committed by Christian Beikov
parent 2228bd238e
commit 2404ff56ab
4 changed files with 178 additions and 0 deletions

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<Shipment> shipmentList;
ChildUser() {
}
public long getId() {
return id;
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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");
}
}

View File

@ -62,6 +62,11 @@ public class Shipment extends BaseDomainEntity {
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
private AddressVersion destination; 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() { Shipment() {
} }
@ -104,4 +109,8 @@ public class Shipment extends BaseDomainEntity {
public void setDestination(AddressVersion destination) { public void setDestination(AddressVersion destination) {
this.destination = destination; this.destination = destination;
} }
public void setUser(User user) {
this.user = user;
}
} }

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<Shipment> shipmentList;
User() {
}
public long getId() {
return id;
}
}