diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/BasicMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/BasicMetadataGenerator.java index 06a8e72f00..b37c53d41f 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/BasicMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/BasicMetadataGenerator.java @@ -90,11 +90,12 @@ public final class BasicMetadataGenerator { } @SuppressWarnings({"unchecked"}) - boolean addKeyManyToOne(Element parent, PropertyAuditingData propertyAuditingData, Value value, - SimpleMapperBuilder mapper) { + boolean addManyToOne(Element parent, PropertyAuditingData propertyAuditingData, Value value, + SimpleMapperBuilder mapper) { Type type = value.getType(); - Element manyToOneElement = parent.addElement("key-many-to-one"); + // A null mapper occurs when adding to composite-id element + Element manyToOneElement = parent.addElement(mapper != null ? "many-to-one" : "key-many-to-one"); manyToOneElement.addAttribute("name", propertyAuditingData.getName()); manyToOneElement.addAttribute("class", type.getName()); MetadataTools.addColumns(manyToOneElement, value.getColumnIterator()); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java index f1c22a6b39..4e580db659 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java @@ -64,7 +64,7 @@ public final class IdMetadataGenerator { if (!"_identifierMapper".equals(property.getName())) { boolean added = false; if (propertyType instanceof ManyToOneType) { - added = mainGenerator.getBasicMetadataGenerator().addKeyManyToOne(parent, + added = mainGenerator.getBasicMetadataGenerator().addManyToOne(parent, getIdPersistentPropertyAuditingData(property), property.getValue(), mapper); } else { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java index 76ba6c0b76..a19ba5fcbf 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java @@ -256,7 +256,7 @@ public class MetadataTools { while (properties.hasNext()) { Element property = properties.next(); - if ("property".equals(property.getName())) { + if ("property".equals(property.getName()) || "many-to-one".equals(property.getName())) { Attribute nameAttr = property.attribute("name"); if (nameAttr != null) { nameAttr.setText(prefix + nameAttr.getText()); @@ -265,11 +265,13 @@ public class MetadataTools { changeNamesInColumnElement(property, columnNameIterator); if (changeToKey) { - property.setName("key-property"); + property.setName("key-" + property.getName()); } - Attribute insert = property.attribute("insert"); - insert.setText(Boolean.toString(insertable)); + if ("property".equals(property.getName())) { + Attribute insert = property.attribute("insert"); + insert.setText(Boolean.toString(insertable)); + } } } } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/Item.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/Item.java new file mode 100644 index 0000000000..6372a5d337 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/Item.java @@ -0,0 +1,68 @@ +package org.hibernate.envers.test.integration.ids.embeddedid; + +import org.hibernate.envers.Audited; + +import java.io.Serializable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Audited +public class Item implements Serializable { + @EmbeddedId + private ItemId id; + + private Double price; + + public Item() { + } + + public Item(ItemId id, Double price) { + this.id = id; + this.price = price; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Item)) return false; + + Item item = (Item) o; + + if (getId() != null ? !getId().equals(item.getId()) : item.getId() != null) return false; + if (getPrice() != null ? !getPrice().equals(item.getPrice()) : item.getPrice() != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (price != null ? price.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Item(id = " + id + ", price = + " + price + ")"; + } + + public ItemId getId() { + return id; + } + + public void setId(ItemId id) { + this.id = id; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/ItemId.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/ItemId.java new file mode 100644 index 0000000000..359e028fd6 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/ItemId.java @@ -0,0 +1,83 @@ +package org.hibernate.envers.test.integration.ids.embeddedid; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Embeddable +public class ItemId implements Serializable { + @Column(name = "model") + private String model; + + @Column(name = "version") + private Integer version; + + @ManyToOne + @JoinColumn(name = "producer") + private Producer producer; + + public ItemId() { + } + + public ItemId(String model, Integer version, Producer producer) { + this.model = model; + this.version = version; + this.producer = producer; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ItemId)) return false; + + ItemId itemId = (ItemId) o; + + if (getModel() != null ? !getModel().equals(itemId.getModel()) : itemId.getModel() != null) return false; + if (getProducer() != null ? !getProducer().equals(itemId.getProducer()) : itemId.getProducer() != null) return false; + if (getVersion() != null ? !getVersion().equals(itemId.getVersion()) : itemId.getVersion() != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = model != null ? model.hashCode() : 0; + result = 31 * result + (version != null ? version.hashCode() : 0); + result = 31 * result + (producer != null ? producer.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ItemId(model = " + model + ", version = " + version + ", producer = " + producer + ")"; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public Producer getProducer() { + return producer; + } + + public void setProducer(Producer producer) { + this.producer = producer; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/Producer.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/Producer.java new file mode 100644 index 0000000000..8df7a5d974 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/Producer.java @@ -0,0 +1,71 @@ +package org.hibernate.envers.test.integration.ids.embeddedid; + +import org.hibernate.envers.Audited; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Audited +public class Producer implements Serializable { + @Id + @Column(name = "id") + private Integer id; + + @Column(name = "name") + private String name; + + public Producer() { + } + + public Producer(Integer id, String name) { + this.id = id; + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Producer)) return false; + + Producer producer = (Producer) o; + + if (getId() != null ? !getId().equals(producer.getId()) : producer.getId() != null) return false; + if (getName() != null ? !getName().equals(producer.getName()) : producer.getName() != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (name != null ? name.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Producer(id = " + id + ", name = " + name + ")"; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/PurchaseOrder.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/PurchaseOrder.java new file mode 100644 index 0000000000..b322221442 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/PurchaseOrder.java @@ -0,0 +1,92 @@ +package org.hibernate.envers.test.integration.ids.embeddedid; + +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 PurchaseOrder implements Serializable { + @Id + @GeneratedValue + private Integer id; + + @ManyToOne + @JoinColumns({ + @JoinColumn(name = "model", referencedColumnName = "model", nullable = true), + @JoinColumn(name = "version", referencedColumnName = "version", nullable = true), + @JoinColumn(name = "producer", referencedColumnName = "producer", nullable = true)}) + private Item item; + + @Column(name = "NOTE") + private String comment; + + public PurchaseOrder() { + } + + public PurchaseOrder(Item item, String comment) { + this.item = item; + this.comment = comment; + } + + public PurchaseOrder(Integer id, Item item, String comment) { + this.id = id; + this.item = item; + this.comment = comment; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PurchaseOrder)) return false; + + PurchaseOrder that = (PurchaseOrder) o; + + if (getComment() != null ? !getComment().equals(that.getComment()) : that.getComment() != null) return false; + if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false; + if (getItem() != null ? !getItem().equals(that.getItem()) : that.getItem() != null) return false; + + return true; + } + + @Override + public String toString() { + return "PurchaseOrder(id = " + id + ", item = " + item + ", comment = " + comment + ")"; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (item != null ? item.hashCode() : 0); + result = 31 * result + (comment != null ? comment.hashCode() : 0); + return result; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Item getItem() { + return item; + } + + public void setItem(Item item) { + this.item = item; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/RelationInsideEmbeddableTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/RelationInsideEmbeddableTest.java new file mode 100644 index 0000000000..ff55496404 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/RelationInsideEmbeddableTest.java @@ -0,0 +1,83 @@ +package org.hibernate.envers.test.integration.ids.embeddedid; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.testing.TestForIssue; +import org.junit.Assert; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.util.Arrays; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@TestForIssue(jiraKey = "HHH-7690") +public class RelationInsideEmbeddableTest extends BaseEnversJPAFunctionalTestCase { + private Integer orderId = null; + private ItemId itemId = null; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{PurchaseOrder.class, Item.class, ItemId.class, Producer.class}; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + // Revision 1 + em.getTransaction().begin(); + Producer producer = new Producer(1, "Sony"); + ItemId sonyId = new ItemId("TV", 1, producer); + Item item = new Item(sonyId, 100.50); + PurchaseOrder order = new PurchaseOrder(item, null); + em.persist(producer); + em.persist(item); + em.persist(order); + em.getTransaction().commit(); + + // Revision 2 + em.getTransaction().begin(); + order = em.find(PurchaseOrder.class, order.getId()); + order.setComment("fragile"); + order = em.merge(order); + em.getTransaction().commit(); + + // Revision 3 + em.getTransaction().begin(); + item = em.find(Item.class, sonyId); + item.setPrice(110.00); + em.getTransaction().commit(); + + orderId = order.getId(); + itemId = sonyId; + + em.close(); + } + + @Test + public void testRevisionsCounts() throws Exception { + Assert.assertEquals(Arrays.asList(1, 2), getAuditReader().getRevisions(PurchaseOrder.class, orderId)); + Assert.assertEquals(Arrays.asList(1, 3), getAuditReader().getRevisions(Item.class, itemId)); + } + + @Test + public void testHistoryOfPurchaseOrder() { + PurchaseOrder ver1 = new PurchaseOrder(orderId, new Item(new ItemId("TV", 1, new Producer(1, "Sony")), 100.50), null); + PurchaseOrder ver2 = new PurchaseOrder(orderId, new Item(new ItemId("TV", 1, new Producer(1, "Sony")), 100.50), "fragile"); + + Assert.assertEquals(ver1, getAuditReader().find(PurchaseOrder.class, orderId, 1)); + Assert.assertEquals(ver2, getAuditReader().find(PurchaseOrder.class, orderId, 2)); + } + + @Test + public void testHistoryOfItem() { + Item ver1 = new Item(itemId, 100.50); + Item ver2 = new Item(itemId, 110.00); + + Assert.assertEquals(ver1, getAuditReader().find(Item.class, itemId, 1)); + Assert.assertEquals(ver2, getAuditReader().find(Item.class, itemId, 3)); + } +}