From 72b81eebfebdb7eb67e84fe8d01bafdfee73867b Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Tue, 1 Oct 2019 10:32:23 -0400 Subject: [PATCH] HHH-12965 Avoid creating foreign keys between audit and main tables * Added test case --- .../foreignkey/ForeignKeyExclusionTest.java | 62 ++++++++++++++++++ .../manytoone/foreignkey/LeafLayer.java | 49 +++++++++++++++ .../manytoone/foreignkey/MiddleLayer.java | 63 +++++++++++++++++++ .../manytoone/foreignkey/MiddleLayerPK.java | 53 ++++++++++++++++ .../manytoone/foreignkey/RootLayer.java | 47 ++++++++++++++ 5 files changed, 274 insertions(+) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/ForeignKeyExclusionTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/LeafLayer.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/MiddleLayer.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/MiddleLayerPK.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/RootLayer.java diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/ForeignKeyExclusionTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/ForeignKeyExclusionTest.java new file mode 100644 index 0000000000..7a2aca9781 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/ForeignKeyExclusionTest.java @@ -0,0 +1,62 @@ +/* + * 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.foreignkey; + +import java.time.LocalDate; +import java.util.ArrayList; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * Tests that no foreign key should be generated from audit schema to main schema. + * + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-12965") +public class ForeignKeyExclusionTest extends BaseEnversJPAFunctionalTestCase { + + private RootLayer rootLayer; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { RootLayer.class, MiddleLayer.class, LeafLayer.class }; + } + + @Test + public void testRemovingAuditedEntityWithIdClassAndManyToOneForeignKeyConstraint() { + // Revision 1 - Add Root/Middle/Leaf layers + this.rootLayer = doInJPA( this::entityManagerFactory, entityManager -> { + final RootLayer rootLayer = new RootLayer(); + rootLayer.setMiddleLayers( new ArrayList<>() ); + + MiddleLayer middleLayer = new MiddleLayer(); + rootLayer.getMiddleLayers().add( middleLayer ); + middleLayer.setRootLayer( rootLayer ); + middleLayer.setValidFrom( LocalDate.of( 2019, 3, 19 ) ); + middleLayer.setLeafLayers( new ArrayList<>() ); + + LeafLayer leafLayer = new LeafLayer(); + leafLayer.setMiddleLayer( middleLayer ); + middleLayer.getLeafLayers().add( leafLayer ); + + entityManager.persist( rootLayer ); + return rootLayer; + } ); + + // Revision 2 - Delete Root/Middle/Leaf layers + // This causes FK violation + doInJPA( this::entityManagerFactory, entityManager -> { + final RootLayer rootLayer = entityManager.find( RootLayer.class, this.rootLayer.getId() ); + entityManager.remove( rootLayer ); + } ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/LeafLayer.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/LeafLayer.java new file mode 100644 index 0000000000..0d8fff9f04 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/LeafLayer.java @@ -0,0 +1,49 @@ +/* + * 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.foreignkey; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.ManyToOne; + +import org.hibernate.envers.Audited; + +/** + * @author Chris Cranford + */ +@Entity(name = "LeafLayer") +@Audited +public class LeafLayer { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + @ManyToOne(optional = false) + @JoinColumns({ + @JoinColumn(name = "middle_layer_valid_from_fk", referencedColumnName = "valid_from"), + @JoinColumn(name = "middle_layer_root_layer_fk", referencedColumnName = "root_layer_fk") }) + private MiddleLayer middleLayer; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MiddleLayer getMiddleLayer() { + return middleLayer; + } + + public void setMiddleLayer(MiddleLayer middleLayer) { + this.middleLayer = middleLayer; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/MiddleLayer.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/MiddleLayer.java new file mode 100644 index 0000000000..0b506edbe0 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/MiddleLayer.java @@ -0,0 +1,63 @@ +/* + * 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.foreignkey; + +import java.time.LocalDate; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.hibernate.envers.Audited; + +/** + * @author Chris Cranford + */ +@Audited +@Entity +@IdClass(MiddleLayerPK.class) +public class MiddleLayer { + @Id + @Column(name = "valid_from", nullable = false) + private LocalDate validFrom; + @Id + @ManyToOne + @JoinColumn(name = "root_layer_fk") + private RootLayer rootLayer; + @OneToMany(mappedBy = "middleLayer", cascade = CascadeType.ALL, orphanRemoval = true) + private List leafLayers; + + public LocalDate getValidFrom() { + return validFrom; + } + + public void setValidFrom(LocalDate validFrom) { + this.validFrom = validFrom; + } + + public RootLayer getRootLayer() { + return rootLayer; + } + + public void setRootLayer(RootLayer rootLayer) { + this.rootLayer = rootLayer; + } + + public List getLeafLayers() { + return leafLayers; + } + + public void setLeafLayers(List leafLayers) { + this.leafLayers = leafLayers; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/MiddleLayerPK.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/MiddleLayerPK.java new file mode 100644 index 0000000000..21489248f4 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/MiddleLayerPK.java @@ -0,0 +1,53 @@ +/* + * 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.foreignkey; + +import java.io.Serializable; +import java.time.LocalDate; +import java.util.Objects; + +/** + * @author Chris Cranford + */ +public class MiddleLayerPK implements Serializable { + private Long rootLayer; + private LocalDate validFrom; + + public Long getRootLayer() { + return rootLayer; + } + + public void setRootLayer(Long rootLayer) { + this.rootLayer = rootLayer; + } + + public LocalDate getValidFrom() { + return validFrom; + } + + public void setValidFrom(LocalDate validFrom) { + this.validFrom = validFrom; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + MiddleLayerPK that = (MiddleLayerPK) o; + return Objects.equals( rootLayer, that.rootLayer ) && + Objects.equals( validFrom, that.validFrom ); + } + + @Override + public int hashCode() { + return Objects.hash( rootLayer, validFrom ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/RootLayer.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/RootLayer.java new file mode 100644 index 0000000000..9956292788 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytoone/foreignkey/RootLayer.java @@ -0,0 +1,47 @@ +/* + * 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.foreignkey; + +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +import org.hibernate.envers.Audited; + +/** + * @author Chris Cranford + */ +@Entity(name = "RootLayer") +@Audited +public class RootLayer { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + @OneToMany(mappedBy = "rootLayer", cascade = CascadeType.ALL, orphanRemoval = true) + private List middleLayers; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getMiddleLayers() { + return middleLayers; + } + + public void setMiddleLayers(List middleLayers) { + this.middleLayers = middleLayers; + } +}