HHH-4073 - Discriminator formula support in Envers
This commit is contained in:
parent
dfcdab4a33
commit
40f28ff69c
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Column> 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 <code>column</code> element with the following attributes (unless empty): <code>name</code>,
|
||||
* <code>length</code>, <code>scale</code>, <code>precision</code>, <code>sql-type</code>, <code>read</code>
|
||||
* and <code>write</code>.
|
||||
* @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<Element> properties = element.elementIterator();
|
||||
|
@ -228,6 +240,32 @@ public class MetadataTools {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds <code>formula</code> 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 <code>column</code> or <code>formula</code> 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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue