From 40f28ff69c955b1b29a8f76d3d835fac4725474b Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Wed, 13 Apr 2011 19:12:44 +0200 Subject: [PATCH] HHH-4073 - Discriminator formula support in Envers --- .../metadata/AuditMetadataGenerator.java | 4 +- .../configuration/metadata/MetadataTools.java | 44 +++++- .../discriminatorformula/ChildEntity.java | 59 ++++++++ .../discriminatorformula/ClassTypeEntity.java | 59 ++++++++ .../DiscriminatorFormulaTest.java | 131 ++++++++++++++++++ .../discriminatorformula/ParentEntity.java | 91 ++++++++++++ 6 files changed, 384 insertions(+), 4 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ChildEntity.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ClassTypeEntity.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/DiscriminatorFormulaTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ParentEntity.java diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java index ec5ac6a269..c83f692afe 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java @@ -64,6 +64,7 @@ import org.jboss.logging.Logger; * @author Tomasz Bech * @author Stephanie Pau at Markit Group Plc * @author Hernán Chanfreau + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) */ public final class AuditMetadataGenerator { @@ -331,7 +332,8 @@ public final class AuditMetadataGenerator { // Checking if there is a discriminator column if (pc.getDiscriminator() != null) { Element discriminator_element = class_mapping.addElement("discriminator"); - MetadataTools.addColumns(discriminator_element, pc.getDiscriminator().getColumnIterator()); + // Database column or SQL formula allowed to distinguish entity types + MetadataTools.addColumnsOrFormulas(discriminator_element, pc.getDiscriminator().getColumnIterator()); discriminator_element.addAttribute("type", pc.getDiscriminator().getType().getName()); } 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 0eab6c8a41..a860bfbd1b 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 @@ -29,9 +29,11 @@ import org.dom4j.Document; import org.dom4j.Element; import org.hibernate.envers.tools.StringTools; import org.hibernate.mapping.Column; +import org.hibernate.mapping.Formula; /** * @author Adam Warski (adam at warski dot org) + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) */ public class MetadataTools { public static Element addNativelyGeneratedId(Element parent, String name, String type) { @@ -182,12 +184,22 @@ public class MetadataTools { public static void addColumns(Element any_mapping, Iterator columns) { while (columns.hasNext()) { - Column column = columns.next(); - addColumn(any_mapping, column.getName(), column.getLength(), column.getScale(), column.getPrecision(), - column.getSqlType(), column.getCustomRead(), column.getCustomWrite()); + addColumn(any_mapping, columns.next()); } } + /** + * Adds column element with the following attributes (unless empty): name, + * length, scale, precision, sql-type, read + * and write. + * @param any_mapping Parent element. + * @param column Column descriptor. + */ + public static void addColumn(Element any_mapping, Column column) { + addColumn(any_mapping, column.getName(), column.getLength(), column.getScale(), column.getPrecision(), + column.getSqlType(), column.getCustomRead(), column.getCustomWrite()); + } + @SuppressWarnings({"unchecked"}) private static void changeNamesInColumnElement(Element element, ColumnNameIterator columnNameIterator) { Iterator properties = element.elementIterator(); @@ -228,6 +240,32 @@ public class MetadataTools { } } + /** + * Adds formula element. + * @param element Parent element. + * @param formula Formula descriptor. + */ + public static void addFormula(Element element, Formula formula) { + element.addElement("formula").setText(formula.getText()); + } + + /** + * Adds all column or formula elements. + * @param element Parent element. + * @param columnIterator Iterator pointing at {@link org.hibernate.mapping.Column} and/or + * {@link org.hibernate.mapping.Formula} objects. + */ + public static void addColumnsOrFormulas(Element element, Iterator columnIterator) { + while (columnIterator.hasNext()) { + Object o = columnIterator.next(); + if (o instanceof Column) { + addColumn(element, (Column) o); + } else if (o instanceof Formula) { + addFormula(element, (Formula) o); + } + } + } + /** * An iterator over column names. */ diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ChildEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ChildEntity.java new file mode 100644 index 0000000000..ba735d644b --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ChildEntity.java @@ -0,0 +1,59 @@ +package org.hibernate.envers.test.integration.inheritance.single.discriminatorformula; + +import org.hibernate.envers.Audited; + +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@DiscriminatorValue(ClassTypeEntity.CHILD_TYPE) +@Audited +public class ChildEntity extends ParentEntity { + private String specificData; + + public ChildEntity() { + } + + public ChildEntity(Long typeId, String data, String specificData) { + super(typeId, data); + this.specificData = specificData; + } + + public ChildEntity(Long id, Long typeId, String data, String specificData) { + super(id, typeId, data); + this.specificData = specificData; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ChildEntity)) return false; + if (!super.equals(o)) return false; + + ChildEntity that = (ChildEntity) o; + + if (specificData != null ? !specificData.equals(that.specificData) : that.specificData != null) return false; + + return true; + } + + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (specificData != null ? specificData.hashCode() : 0); + return result; + } + + public String toString() { + return "ChildEntity(id = " + id + ", typeId = " + typeId + ", data = " + data + ", specificData = " + specificData + ")"; + } + + public String getSpecificData() { + return specificData; + } + + public void setSpecificData(String specificData) { + this.specificData = specificData; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ClassTypeEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ClassTypeEntity.java new file mode 100644 index 0000000000..e3ba0f5f9e --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ClassTypeEntity.java @@ -0,0 +1,59 @@ +package org.hibernate.envers.test.integration.inheritance.single.discriminatorformula; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +public class ClassTypeEntity { + public static final String PARENT_TYPE = "Parent"; + public static final String CHILD_TYPE = "Child"; + + @Id + @GeneratedValue + private Long id; + + private String type; + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ClassTypeEntity)) return false; + + ClassTypeEntity that = (ClassTypeEntity) o; + + if (id != null ? !id.equals(that.id) : that.id != null) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + + return true; + } + + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (id != null ? id.hashCode() : 0); + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } + + public String toString() { + return "ClassTypeEntity(id = " + id + ", type = " + type + ")"; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/DiscriminatorFormulaTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/DiscriminatorFormulaTest.java new file mode 100644 index 0000000000..ab7fe2eece --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/DiscriminatorFormulaTest.java @@ -0,0 +1,131 @@ +package org.hibernate.envers.test.integration.inheritance.single.discriminatorformula; + +import org.hibernate.ejb.Ejb3Configuration; +import org.hibernate.envers.test.AbstractEntityTest; +import org.hibernate.envers.test.Priority; +import org.hibernate.mapping.Formula; +import org.hibernate.mapping.PersistentClass; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +public class DiscriminatorFormulaTest extends AbstractEntityTest { + private PersistentClass parentAudit = null; + private ChildEntity childVer1 = null; + private ChildEntity childVer2 = null; + private ParentEntity parentVer1 = null; + private ParentEntity parentVer2 = null; + + @Override + public void configure(Ejb3Configuration cfg) { + cfg.addAnnotatedClass(ClassTypeEntity.class); + cfg.addAnnotatedClass(ParentEntity.class); + cfg.addAnnotatedClass(ChildEntity.class); + } + + @Test + @Priority(10) + public void initData() { + parentAudit = getCfg().getClassMapping("org.hibernate.envers.test.integration.inheritance.single.discriminatorformula.ParentEntity_AUD"); + + EntityManager em = getEntityManager(); + + // Child entity type + em.getTransaction().begin(); + ClassTypeEntity childType = new ClassTypeEntity(); + childType.setType(ClassTypeEntity.CHILD_TYPE); + em.persist(childType); + Long childTypeId = childType.getId(); + em.getTransaction().commit(); + + // Parent entity type + em.getTransaction().begin(); + ClassTypeEntity parentType = new ClassTypeEntity(); + parentType.setType(ClassTypeEntity.PARENT_TYPE); + em.persist(parentType); + Long parentTypeId = parentType.getId(); + em.getTransaction().commit(); + + // Child Rev 1 + em.getTransaction().begin(); + ChildEntity child = new ChildEntity(childTypeId, "Child data", "Child specific data"); + em.persist(child); + Long childId = child.getId(); + em.getTransaction().commit(); + + // Parent Rev 2 + em.getTransaction().begin(); + ParentEntity parent = new ParentEntity(parentTypeId, "Parent data"); + em.persist(parent); + Long parentId = parent.getId(); + em.getTransaction().commit(); + + // Child Rev 3 + em.getTransaction().begin(); + child = em.find(ChildEntity.class, childId); + child.setData("Child data modified"); + em.getTransaction().commit(); + + // Parent Rev 4 + em.getTransaction().begin(); + parent = em.find(ParentEntity.class, parentId); + parent.setData("Parent data modified"); + em.getTransaction().commit(); + + childVer1 = new ChildEntity(childId, childTypeId, "Child data", "Child specific data"); + childVer2 = new ChildEntity(childId, childTypeId, "Child data modified", "Child specific data"); + parentVer1 = new ParentEntity(parentId, parentTypeId, "Parent data"); + parentVer2 = new ParentEntity(parentId, parentTypeId, "Parent data modified"); + } + + @Test + public void testDiscriminatorFormulaInAuditTable() { + assert parentAudit.getDiscriminator().hasFormula(); + Iterator iterator = parentAudit.getDiscriminator().getColumnIterator(); + while (iterator.hasNext()) { + Object o = iterator.next(); + if (o instanceof Formula) { + Formula formula = (Formula) o; + assert formula.getText().equals(ParentEntity.DISCRIMINATOR_QUERY); + return; + } + } + assert false; + } + + @Test + public void testRevisionsCounts() { + assert Arrays.asList(1, 3).equals(getAuditReader().getRevisions(ChildEntity.class, childVer1.getId())); + assert Arrays.asList(2, 4).equals(getAuditReader().getRevisions(ParentEntity.class, parentVer1.getId())); + } + + @Test + public void testHistoryOfParent() { + assert getAuditReader().find(ParentEntity.class, parentVer1.getId(), 2).equals(parentVer1); + assert getAuditReader().find(ParentEntity.class, parentVer2.getId(), 4).equals(parentVer2); + } + + @Test + public void testHistoryOfChild() { + assert getAuditReader().find(ChildEntity.class, childVer1.getId(), 1).equals(childVer1); + assert getAuditReader().find(ChildEntity.class, childVer2.getId(), 3).equals(childVer2); + } + + @Test + public void testPolymorphicQuery() { + assert getAuditReader().createQuery().forEntitiesAtRevision(ChildEntity.class, 1).getSingleResult().equals(childVer1); + assert getAuditReader().createQuery().forEntitiesAtRevision(ParentEntity.class, 1).getSingleResult().equals(childVer1); + + List childEntityRevisions = getAuditReader().createQuery().forRevisionsOfEntity(ChildEntity.class, true, false).getResultList(); + assert Arrays.asList(childVer1, childVer2).equals(childEntityRevisions); + + List parentEntityRevisions = getAuditReader().createQuery().forRevisionsOfEntity(ParentEntity.class, true, false).getResultList(); + assert Arrays.asList(childVer1, parentVer1, childVer2, parentVer2).equals(parentEntityRevisions); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ParentEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ParentEntity.java new file mode 100644 index 0000000000..bf1216869f --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/single/discriminatorformula/ParentEntity.java @@ -0,0 +1,91 @@ +package org.hibernate.envers.test.integration.inheritance.single.discriminatorformula; + +import org.hibernate.annotations.DiscriminatorFormula; +import org.hibernate.envers.Audited; + +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@DiscriminatorFormula(ParentEntity.DISCRIMINATOR_QUERY) +@DiscriminatorValue(ClassTypeEntity.PARENT_TYPE) +@Audited +public class ParentEntity { + public static final String DISCRIMINATOR_QUERY = "(SELECT c.type FROM ClassTypeEntity c WHERE c.id = typeId)"; + + @Id + @GeneratedValue + protected Long id; + + protected Long typeId; + + protected String data; + + public ParentEntity() { + } + + public ParentEntity(Long typeId, String data) { + this.typeId = typeId; + this.data = data; + } + + public ParentEntity(Long id, Long typeId, String data) { + this.id = id; + this.typeId = typeId; + this.data = data; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ParentEntity)) return false; + + ParentEntity that = (ParentEntity) o; + + if (id != null ? !id.equals(that.id) : that.id != null) return false; + if (typeId != null ? !typeId.equals(that.id) : that.typeId != null) return false; + if (data != null ? !data.equals(that.data) : that.data != null) return false; + + return true; + } + + public int hashCode() { + int result; + result = (id != null ? id.hashCode() : 0); + result = 31 * result + (typeId != null ? typeId.hashCode() : 0); + result = 31 * result + (data != null ? data.hashCode() : 0); + return result; + } + + public String toString() { + return "ParentEntity(id = " + id + ", typeId = " + typeId + ", data = " + data + ")"; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getTypeId() { + return typeId; + } + + public void setTypeId(Long typeId) { + this.typeId = typeId; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } +}