From 9dca4b82ae88a0ef2712518b13f78cea09442396 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Fri, 13 May 2016 14:19:01 -0500 Subject: [PATCH] HHH-10246 - Fix Envers usage of JoinColumn with NaturalId. --- .../metadata/AuditMetadataGenerator.java | 14 +- .../test/integration/naturalid/Account.java | 91 ++++++++++++ .../test/integration/naturalid/Customer.java | 134 ++++++++++++++++++ .../test/integration/naturalid/Device.java | 88 ++++++++++++ .../naturalid/JoinColumnNaturalIdTest.java | 94 ++++++++++++ 5 files changed, 418 insertions(+), 3 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Account.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Customer.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Device.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/JoinColumnNaturalIdTest.java diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java index a6955d5ea9..32e442fa99 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java @@ -10,6 +10,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import org.dom4j.Element; import org.hibernate.MappingException; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.spi.MetadataImplementor; @@ -34,6 +35,7 @@ import org.hibernate.mapping.Join; import org.hibernate.mapping.OneToOne; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.mapping.SyntheticProperty; import org.hibernate.mapping.Table; import org.hibernate.mapping.Value; import org.hibernate.service.ServiceRegistry; @@ -43,11 +45,8 @@ import org.hibernate.type.ManyToOneType; import org.hibernate.type.OneToOneType; import org.hibernate.type.TimestampType; import org.hibernate.type.Type; - import org.jboss.logging.Logger; -import org.dom4j.Element; - /** * @author Adam Warski (adam at warski dot org) * @author Sebastian Komander @@ -56,6 +55,7 @@ import org.dom4j.Element; * @author Hernán Chanfreau * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) * @author Michal Skowronek (mskowr at o2 dot pl) + * @author Chris Cranford */ public final class AuditMetadataGenerator { private static final EnversMessageLogger LOG = Logger.getMessageLogger( @@ -368,6 +368,14 @@ public final class AuditMetadataGenerator { final String propertyName = property.getName(); final PropertyAuditingData propertyAuditingData = auditingData.getPropertyAuditingData( propertyName ); if ( propertyAuditingData != null ) { + // HHH-10246 + // Verifies if a mapping exists using a @JoinColumn against a @NaturalId field + // and if so, this eliminates the mapping property as it isn't needed. + if ( property instanceof SyntheticProperty ) { + if ( property.getValue().isAlternateUniqueKey() ) { + continue; + } + } addValue( parent, property.getValue(), diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Account.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Account.java new file mode 100644 index 0000000000..0b4fd9fce1 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Account.java @@ -0,0 +1,91 @@ +/* + * 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.naturalid; + +import java.io.Serializable; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +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 +@Audited +public class Account implements Serializable { + @Id + @GeneratedValue + private Integer id; + + @Audited + @ManyToOne + @JoinColumns({ + @JoinColumn(name = "customer_customernumber", referencedColumnName = "customerNumber"), + @JoinColumn(name = "customer_customername", referencedColumnName = "name") + }) + private Customer customer; + + Account() { + + } + + public Account(Integer id, Customer customer) { + this.id = id; + this.customer = customer; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + @Override + public int hashCode() { + int result; + result = ( id != null ? id.hashCode() : 0 ); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( obj == this ) { + return true; + } + if ( !(obj instanceof Account)) { + return false; + } + Account that = (Account) obj; + if ( id != null ? !id.equals( that.id ) : that.id != null ) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Account{" + + "id=" + id + + '}'; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Customer.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Customer.java new file mode 100644 index 0000000000..425a391ab3 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Customer.java @@ -0,0 +1,134 @@ +/* + * 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.naturalid; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +import org.hibernate.annotations.NaturalId; +import org.hibernate.envers.Audited; + +/** + * @author Chris Cranford + */ +@Entity +@Audited +public class Customer implements Serializable { + @Id + @GeneratedValue + private Integer id; + + @NaturalId + private String customerNumber; + + @NaturalId + private String name; + + @Audited + @OneToMany(mappedBy = "customer") + private Collection accounts = new ArrayList(); + + @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL) + private Set devices = new HashSet(); + + Customer() { + + } + + Customer(Integer id, String customerNumber, String name) { + this.id = id; + this.customerNumber = customerNumber; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getCustomerNumber() { + return customerNumber; + } + + public void setCustomerNumber(String customerNumber) { + this.customerNumber = customerNumber; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Collection getAccounts() { + return accounts; + } + + public void setAccounts(Collection accounts) { + this.accounts = accounts; + } + + public Set getDevices() { + return devices; + } + + public void setDevices(Set devices) { + this.devices = devices; + } + + @Override + public int hashCode() { + int result; + result = ( id != null ? id.hashCode() : 0 ); + result = 31 * result + ( customerNumber != null ? customerNumber.hashCode() : 0 ); + result = 31 * result + ( name != null ? name.hashCode() : 0 ); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( obj == this ) { + return true; + } + if ( !( obj instanceof Customer ) ) { + return false; + } + Customer that = (Customer) obj; + if ( id != null ? !id.equals( that.id ) : that.id != null ) { + return false; + } + if ( name != null ? !name.equals( that.name ) : that.name != null ) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Customer{" + + "id=" + id + + ", name='" + name + '\'' + + ", customerNumber='" + customerNumber + '\'' + + ", accounts=" + accounts + + ", devices=" + devices + + '}'; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Device.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Device.java new file mode 100644 index 0000000000..52045b1f6e --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/Device.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.naturalid; + +import java.io.Serializable; + +import javax.persistence.Entity; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +import org.hibernate.envers.Audited; + +/** + * @author Chris Cranford + */ +@Entity +@Audited +public class Device implements Serializable { + @Id + @GeneratedValue + private Integer id; + + @Audited + @ManyToOne + @JoinColumn(name = "customer_id", foreignKey = @ForeignKey(name = "fk_dev_cust_id")) + private Customer customer; + + Device() { + + } + + public Device(Integer id, Customer customer) { + this.id = id; + this.customer = customer; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + @Override + public int hashCode() { + int result; + result = ( id != null ? id.hashCode() : 0 ); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( obj == this ) { + return true; + } + if ( !( obj instanceof Device ) ) { + return false; + } + Device that = (Device) obj; + if ( id != null ? !id.equals( that.id ) : that.id != null ) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Device{" + + "id=" + id + + '}'; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/JoinColumnNaturalIdTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/JoinColumnNaturalIdTest.java new file mode 100644 index 0000000000..cccd0a0bce --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/naturalid/JoinColumnNaturalIdTest.java @@ -0,0 +1,94 @@ +/* + * 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.naturalid; + +import javax.persistence.EntityManager; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-10246") +public class JoinColumnNaturalIdTest extends BaseEnversJPAFunctionalTestCase { + private Integer customerId; + private Integer deviceId; + private Integer accountId; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Account.class, + Customer.class, + Device.class + }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getOrCreateEntityManager(); + try { + // Revision 1 + em.getTransaction().begin(); + Customer customer = new Customer(); + customer.setCustomerNumber( "1234567" ); + customer.setName( "ACME" ); + em.persist( customer ); + em.getTransaction().commit(); + customerId = customer.getId(); + // Revision 2 + em.getTransaction().begin(); + Device device = new Device(); + device.setCustomer( customer ); + Account account = new Account(); + account.setCustomer( customer ); + em.persist( account ); + em.persist( device ); + em.getTransaction().commit(); + accountId = account.getId(); + deviceId = device.getId(); + // Revision 3 + em.getTransaction().begin(); + em.remove( account ); + em.getTransaction().commit(); + } + finally { + em.close(); + } + } + + @Test + public void testRevisionCounts() { + assertEquals( 3, getAuditReader().getRevisions( Customer.class, customerId ).size() ); + assertEquals( 2, getAuditReader().getRevisions( Account.class, accountId ).size() ); + assertEquals( 1, getAuditReader().getRevisions( Device.class, deviceId ).size() ); + } + + @Test + public void testRevisionHistoryOfCustomer() { + final Customer customer = new Customer( customerId, "1234567", "ACME" ); + Customer rev1 = getAuditReader().find( Customer.class, customerId, 1 ); + assertEquals( customer, rev1 ); + + final Account account = new Account( accountId, customer ); + final Device device = new Device( deviceId, customer ); + customer.getAccounts().add( account ); + customer.getDevices().add( device ); + Customer rev2 = getAuditReader().find( Customer.class, customerId, 2 ); + assertEquals( customer, rev2 ); + + customer.getAccounts().clear(); + Customer rev3 = getAuditReader().find( Customer.class, customerId, 3 ); + assertEquals( customer, rev3 ); + } +}