From d1ac167ec642e04814e68e5e8068324f45fd02f2 Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Sun, 29 Jan 2012 17:09:40 +0100 Subject: [PATCH] HHH-4962 - Fix and test --- .../metadata/CollectionMetadataGenerator.java | 19 ++- .../reader/AuditedPropertiesReader.java | 5 + .../bidirectional/ImplicitMappedByTest.java | 110 ++++++++++++++++++ .../bidirectional/ManyToOneOwning.java | 87 ++++++++++++++ .../bidirectional/OneToManyOwned.java | 90 ++++++++++++++ 5 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/ImplicitMappedByTest.java create mode 100644 hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/ManyToOneOwning.java create mode 100644 hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/OneToManyOwned.java diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java index 8f114c8420..2395a6808b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java @@ -76,6 +76,7 @@ import org.hibernate.envers.tools.StringTools; import org.hibernate.envers.tools.Tools; import org.hibernate.mapping.Collection; import org.hibernate.mapping.IndexedCollection; +import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.OneToMany; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; @@ -148,12 +149,14 @@ public final class CollectionMetadataGenerator { void addCollection() { Type type = propertyValue.getType(); + Value value = propertyValue.getElement(); boolean oneToManyAttachedType = type instanceof BagType || type instanceof SetType || type instanceof MapType || type instanceof ListType; - boolean inverseOneToMany = (propertyValue.getElement() instanceof OneToMany) && (propertyValue.isInverse()); - boolean fakeOneToManyBidirectional = (propertyValue.getElement() instanceof OneToMany) && (propertyAuditingData.getAuditMappedBy() != null); + boolean inverseOneToMany = (value instanceof OneToMany) && (propertyValue.isInverse()); + boolean owningManyToOneWithJoinTableBidirectional = (value instanceof ManyToOne) && (propertyAuditingData.getAuditMappedBy() != null); + boolean fakeOneToManyBidirectional = (value instanceof OneToMany) && (propertyAuditingData.getAuditMappedBy() != null); - if (oneToManyAttachedType && (inverseOneToMany || fakeOneToManyBidirectional)) { + if (oneToManyAttachedType && (inverseOneToMany || fakeOneToManyBidirectional || owningManyToOneWithJoinTableBidirectional)) { // A one-to-many relation mapped using @ManyToOne and @OneToMany(mappedBy="...") addOneToManyAttached(fakeOneToManyBidirectional); } else { @@ -550,7 +553,15 @@ public final class CollectionMetadataGenerator { } private String getMappedBy(Collection collectionValue) { - PersistentClass referencedClass = ((OneToMany) collectionValue.getElement()).getAssociatedClass(); + PersistentClass referencedClass = null; + if (collectionValue.getElement() instanceof OneToMany) { + OneToMany oneToManyValue = (OneToMany) collectionValue.getElement(); + referencedClass = oneToManyValue.getAssociatedClass(); + } else if (collectionValue.getElement() instanceof ManyToOne) { + // Case for bi-directional relation with @JoinTable on the owning @ManyToOne side. + ManyToOne manyToOneValue = (ManyToOne) collectionValue.getElement(); + referencedClass = manyToOneValue.getMappings().getClass(manyToOneValue.getReferencedEntityName()); + } // If there's an @AuditMappedBy specified, returning it directly. String auditMappedBy = propertyAuditingData.getAuditMappedBy(); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java index a9554f7a12..e280663f6f 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.Set; import javax.persistence.JoinColumn; import javax.persistence.MapKey; +import javax.persistence.OneToMany; import javax.persistence.Version; import org.hibernate.MappingException; @@ -441,6 +442,10 @@ public class AuditedPropertiesReader { } private void setPropertyAuditMappedBy(XProperty property, PropertyAuditingData propertyData) { + OneToMany oneToMany = property.getAnnotation(OneToMany.class); + if (oneToMany != null && !"".equals(oneToMany.mappedBy())) { + propertyData.setAuditMappedBy(oneToMany.mappedBy()); + } AuditMappedBy auditMappedBy = property.getAnnotation(AuditMappedBy.class); if (auditMappedBy != null) { propertyData.setAuditMappedBy(auditMappedBy.mappedBy()); diff --git a/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/ImplicitMappedByTest.java b/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/ImplicitMappedByTest.java new file mode 100644 index 0000000000..950e4791f6 --- /dev/null +++ b/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/ImplicitMappedByTest.java @@ -0,0 +1,110 @@ +package org.hibernate.envers.test.integration.manytoone.bidirectional; + +import org.hibernate.ejb.Ejb3Configuration; +import org.hibernate.envers.test.AbstractEntityTest; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.tools.TestTools; +import org.hibernate.testing.TestForIssue; +import org.junit.Assert; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@TestForIssue(jiraKey = "HHH-4962") +public class ImplicitMappedByTest extends AbstractEntityTest { + private Long ownedId = null; + private Long owning1Id = null; + private Long owning2Id = null; + + public void configure(Ejb3Configuration cfg) { + cfg.addAnnotatedClass(OneToManyOwned.class); + cfg.addAnnotatedClass(ManyToOneOwning.class); + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + OneToManyOwned owned = new OneToManyOwned("data", null); + Set referencing = new HashSet(); + ManyToOneOwning owning1 = new ManyToOneOwning("data1", owned); + referencing.add(owning1); + ManyToOneOwning owning2 = new ManyToOneOwning("data2", owned); + referencing.add(owning2); + owned.setReferencing(referencing); + + // Revision 1 + em.getTransaction().begin(); + em.persist(owned); + em.persist(owning1); + em.persist(owning2); + em.getTransaction().commit(); + + ownedId = owned.getId(); + owning1Id = owning1.getId(); + owning2Id = owning2.getId(); + + // Revision 2 + em.getTransaction().begin(); + owning1 = em.find(ManyToOneOwning.class, owning1.getId()); + em.remove(owning1); + em.getTransaction().commit(); + + // Revision 3 + em.getTransaction().begin(); + owning2 = em.find(ManyToOneOwning.class, owning2.getId()); + owning2.setData("data2modified"); + em.merge(owning2); + em.getTransaction().commit(); + } + + @Test + public void testRevisionsCounts() { + Assert.assertEquals(Arrays.asList(1, 2), getAuditReader().getRevisions(OneToManyOwned.class, ownedId)); + Assert.assertEquals(Arrays.asList(1, 2), getAuditReader().getRevisions(ManyToOneOwning.class, owning1Id)); + Assert.assertEquals(Arrays.asList(1, 3), getAuditReader().getRevisions(ManyToOneOwning.class, owning2Id)); + } + + @Test + public void testHistoryOfOwned() { + OneToManyOwned owned = new OneToManyOwned("data", null, ownedId); + ManyToOneOwning owning1 = new ManyToOneOwning("data1", owned, owning1Id); + ManyToOneOwning owning2 = new ManyToOneOwning("data2", owned, owning2Id); + + OneToManyOwned ver1 = getAuditReader().find(OneToManyOwned.class, ownedId, 1); + Assert.assertEquals(owned, ver1); + Assert.assertEquals(TestTools.makeSet(owning1, owning2), ver1.getReferencing()); + + OneToManyOwned ver2 = getAuditReader().find(OneToManyOwned.class, ownedId, 2); + Assert.assertEquals(owned, ver2); + Assert.assertEquals(TestTools.makeSet(owning2), ver2.getReferencing()); + } + + @Test + public void testHistoryOfOwning1() { + ManyToOneOwning ver1 = new ManyToOneOwning("data1", null, owning1Id); + Assert.assertEquals(ver1, getAuditReader().find(ManyToOneOwning.class, owning1Id, 1)); + } + + @Test + public void testHistoryOfOwning2() { + OneToManyOwned owned = new OneToManyOwned("data", null, ownedId); + ManyToOneOwning owning1 = new ManyToOneOwning("data2", owned, owning2Id); + ManyToOneOwning owning3 = new ManyToOneOwning("data2modified", owned, owning2Id); + + ManyToOneOwning ver1 = getAuditReader().find(ManyToOneOwning.class, owning2Id, 1); + ManyToOneOwning ver3 = getAuditReader().find(ManyToOneOwning.class, owning2Id, 3); + + Assert.assertEquals(owning1, ver1); + Assert.assertEquals(owned.getId(), ver1.getReferences().getId()); + Assert.assertEquals(owning3, ver3); + Assert.assertEquals(owned.getId(), ver3.getReferences().getId()); + } +} \ No newline at end of file diff --git a/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/ManyToOneOwning.java b/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/ManyToOneOwning.java new file mode 100644 index 0000000000..a593e21406 --- /dev/null +++ b/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/ManyToOneOwning.java @@ -0,0 +1,87 @@ +package org.hibernate.envers.test.integration.manytoone.bidirectional; + +import org.hibernate.envers.Audited; + +import javax.persistence.*; +import java.io.Serializable; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Audited +public class ManyToOneOwning implements Serializable { + @Id + @GeneratedValue + private Long id; + + private String data; + + @ManyToOne + @JoinTable(name = "many_to_one_join_table", joinColumns = @JoinColumn(name = "owning_id"), + inverseJoinColumns = @JoinColumn(name = "owned_id")) + private OneToManyOwned references; + + public ManyToOneOwning() { + } + + public ManyToOneOwning(String data, OneToManyOwned references) { + this.data = data; + this.references = references; + } + + public ManyToOneOwning(String data, OneToManyOwned references, Long id) { + this.id = id; + this.data = data; + this.references = references; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ManyToOneOwning)) return false; + + ManyToOneOwning that = (ManyToOneOwning) o; + + if (data != null ? !data.equals(that.data) : that.data != null) return false; + if (id != null ? !id.equals(that.id) : that.id != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (data != null ? data.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ManyToOneOwning(id = " + id + ", data = " + data + ")"; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public OneToManyOwned getReferences() { + return references; + } + + public void setReferences(OneToManyOwned references) { + this.references = references; + } +} diff --git a/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/OneToManyOwned.java b/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/OneToManyOwned.java new file mode 100644 index 0000000000..0048e70568 --- /dev/null +++ b/hibernate-envers/src/matrix/java/org/hibernate/envers/test/integration/manytoone/bidirectional/OneToManyOwned.java @@ -0,0 +1,90 @@ +package org.hibernate.envers.test.integration.manytoone.bidirectional; + +import org.hibernate.envers.Audited; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Audited +public class OneToManyOwned implements Serializable { + @Id + @GeneratedValue + private Long id; + + private String data; + + @OneToMany(mappedBy="references") + private Set referencing = new HashSet(); + + public OneToManyOwned() { + } + + public OneToManyOwned(String data, Set referencing) { + this.data = data; + this.referencing = referencing; + } + + public OneToManyOwned(String data, Set referencing, Long id) { + this.id = id; + this.data = data; + this.referencing = referencing; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof OneToManyOwned)) return false; + + OneToManyOwned that = (OneToManyOwned) o; + + if (data != null ? !data.equals(that.data) : that.data != null) return false; + if (id != null ? !id.equals(that.id) : that.id != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (data != null ? data.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "OneToManyOwned(id = " + id + ", data = " + data + ")"; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public Set getReferencing() { + return referencing; + } + + public void setReferencing(Set referencing) { + this.referencing = referencing; + } +}