mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-13 06:34:50 +00:00
HHH-4073 - Discriminator formula support in Envers
This commit is contained in:
parent
f289c7e4bd
commit
63a099fed4
@ -66,6 +66,7 @@
|
|||||||
* @author Tomasz Bech
|
* @author Tomasz Bech
|
||||||
* @author Stephanie Pau at Markit Group Plc
|
* @author Stephanie Pau at Markit Group Plc
|
||||||
* @author Hernán Chanfreau
|
* @author Hernán Chanfreau
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
*/
|
*/
|
||||||
public final class AuditMetadataGenerator {
|
public final class AuditMetadataGenerator {
|
||||||
private static final Logger log = LoggerFactory.getLogger(AuditMetadataGenerator.class);
|
private static final Logger log = LoggerFactory.getLogger(AuditMetadataGenerator.class);
|
||||||
@ -332,7 +333,8 @@ private Triple<Element, ExtendedPropertyMapper, String> generateMappingData(
|
|||||||
// Checking if there is a discriminator column
|
// Checking if there is a discriminator column
|
||||||
if (pc.getDiscriminator() != null) {
|
if (pc.getDiscriminator() != null) {
|
||||||
Element discriminator_element = class_mapping.addElement("discriminator");
|
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());
|
discriminator_element.addAttribute("type", pc.getDiscriminator().getType().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,9 +32,11 @@
|
|||||||
import org.hibernate.envers.tools.StringTools;
|
import org.hibernate.envers.tools.StringTools;
|
||||||
|
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
|
import org.hibernate.mapping.Formula;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adam Warski (adam at warski dot org)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
*/
|
*/
|
||||||
public class MetadataTools {
|
public class MetadataTools {
|
||||||
public static Element addNativelyGeneratedId(Element parent, String name, String type) {
|
public static Element addNativelyGeneratedId(Element parent, String name, String type) {
|
||||||
@ -185,11 +187,21 @@ public static Element createJoin(Element parent, String tableName,
|
|||||||
|
|
||||||
public static void addColumns(Element any_mapping, Iterator<Column> columns) {
|
public static void addColumns(Element any_mapping, Iterator<Column> columns) {
|
||||||
while (columns.hasNext()) {
|
while (columns.hasNext()) {
|
||||||
Column column = columns.next();
|
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(),
|
addColumn(any_mapping, column.getName(), column.getLength(), column.getScale(), column.getPrecision(),
|
||||||
column.getSqlType(), column.getCustomRead(), column.getCustomWrite());
|
column.getSqlType(), column.getCustomRead(), column.getCustomWrite());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked"})
|
@SuppressWarnings({"unchecked"})
|
||||||
private static void changeNamesInColumnElement(Element element, ColumnNameIterator columnNameIterator) {
|
private static void changeNamesInColumnElement(Element element, ColumnNameIterator columnNameIterator) {
|
||||||
@ -231,6 +243,32 @@ public static void prefixNamesInPropertyElement(Element element, String prefix,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* 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…
x
Reference in New Issue
Block a user