Merge pull request #204 from lukasz-antoniak/HHH-6753
HHH-6753 - Envers WorkUnits: Problems with merge
This commit is contained in:
commit
199f3e300c
|
@ -29,20 +29,24 @@ import java.util.Map;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.envers.RevisionType;
|
import org.hibernate.envers.RevisionType;
|
||||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||||
|
import org.hibernate.envers.tools.Tools;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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 AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit {
|
public class AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit {
|
||||||
|
private final Object[] state;
|
||||||
private final Map<String, Object> data;
|
private final Map<String, Object> data;
|
||||||
|
|
||||||
public AddWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
|
public AddWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
|
||||||
Serializable id, EntityPersister entityPersister, Object[] state) {
|
Serializable id, EntityPersister entityPersister, Object[] state) {
|
||||||
super(sessionImplementor, entityName, verCfg, id, RevisionType.ADD);
|
super(sessionImplementor, entityName, verCfg, id, RevisionType.ADD);
|
||||||
|
|
||||||
data = new HashMap<String, Object>();
|
this.data = new HashMap<String, Object>();
|
||||||
verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(sessionImplementor, data,
|
this.state = state;
|
||||||
|
this.verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(sessionImplementor, data,
|
||||||
entityPersister.getPropertyNames(), state, null);
|
entityPersister.getPropertyNames(), state, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +55,8 @@ public class AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
|
||||||
super(sessionImplementor, entityName, verCfg, id, RevisionType.ADD);
|
super(sessionImplementor, entityName, verCfg, id, RevisionType.ADD);
|
||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
final String[] propertyNames = sessionImplementor.getFactory().getEntityPersister(getEntityName()).getPropertyNames();
|
||||||
|
this.state = Tools.mapToArray(data, propertyNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsWork() {
|
public boolean containsWork() {
|
||||||
|
@ -62,6 +68,10 @@ public class AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object[] getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
public AuditWorkUnit merge(AddWorkUnit second) {
|
public AuditWorkUnit merge(AddWorkUnit second) {
|
||||||
return second;
|
return second;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,16 @@ import java.util.Map;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.envers.RevisionType;
|
import org.hibernate.envers.RevisionType;
|
||||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||||
|
import org.hibernate.envers.tools.Tools;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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 DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit {
|
public class DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit {
|
||||||
private final Object[] state;
|
private final Object[] state;
|
||||||
|
private final EntityPersister entityPersister;
|
||||||
private final String[] propertyNames;
|
private final String[] propertyNames;
|
||||||
|
|
||||||
public DelWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
|
public DelWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
|
||||||
|
@ -43,6 +46,7 @@ public class DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
|
||||||
super(sessionImplementor, entityName, verCfg, id, RevisionType.DEL);
|
super(sessionImplementor, entityName, verCfg, id, RevisionType.DEL);
|
||||||
|
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
this.entityPersister = entityPersister;
|
||||||
this.propertyNames = entityPersister.getPropertyNames();
|
this.propertyNames = entityPersister.getPropertyNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +67,10 @@ public class DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuditWorkUnit merge(AddWorkUnit second) {
|
public AuditWorkUnit merge(AddWorkUnit second) {
|
||||||
return null;
|
if (Tools.arraysEqual(second.getState(), state)) {
|
||||||
|
return null; // Return null if object's state has not changed.
|
||||||
|
}
|
||||||
|
return new ModWorkUnit(sessionImplementor, entityName, verCfg, id, entityPersister, second.getState(), state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuditWorkUnit merge(ModWorkUnit second) {
|
public AuditWorkUnit merge(ModWorkUnit second) {
|
||||||
|
|
|
@ -125,6 +125,17 @@ public class Tools {
|
||||||
return obj1.equals(obj2);
|
return obj1.equals(obj2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean arraysEqual(Object[] array1, Object[] array2) {
|
||||||
|
if (array1 == null) return array2 == null;
|
||||||
|
if (array2 == null || array1.length != array2.length) return false;
|
||||||
|
for (int i = 0; i < array1.length; ++i) {
|
||||||
|
if (array1[i] != null ? !array1[i].equals(array2[i]) : array2[i] != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> List<T> iteratorToList(Iterator<T> iter) {
|
public static <T> List<T> iteratorToList(Iterator<T> iter) {
|
||||||
List<T> ret = new ArrayList<T>();
|
List<T> ret = new ArrayList<T>();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
|
@ -189,4 +200,19 @@ public class Tools {
|
||||||
EntityPersister entityPersister = sessionImplementor.getFactory().getEntityPersister(entityName);
|
EntityPersister entityPersister = sessionImplementor.getFactory().getEntityPersister(entityName);
|
||||||
return entityPersister.getMappedClass();
|
return entityPersister.getMappedClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts map's value set to an array. {@code keys} parameter specifies requested elements and their order.
|
||||||
|
* @param data Source map.
|
||||||
|
* @param keys Array of keys that represent requested map values.
|
||||||
|
* @return Array of values stored in the map under specified keys. If map does not contain requested key,
|
||||||
|
* {@code null} is inserted.
|
||||||
|
*/
|
||||||
|
public static Object[] mapToArray(Map<String, Object> data, String[] keys) {
|
||||||
|
Object[] ret = new Object[keys.length];
|
||||||
|
for (int i = 0; i < keys.length; ++i) {
|
||||||
|
ret[i] = data.get(keys[i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package org.hibernate.envers.test.integration.merge;
|
||||||
|
|
||||||
|
import org.hibernate.envers.test.AbstractSessionTest;
|
||||||
|
import org.hibernate.envers.test.Priority;
|
||||||
|
import org.hibernate.envers.test.entities.StrTestEntity;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
|
*/
|
||||||
|
@TestForIssue(jiraKey = "HHH-6753")
|
||||||
|
public class AddDelTest extends AbstractSessionTest {
|
||||||
|
@Override
|
||||||
|
protected void initMappings() {
|
||||||
|
config.addAnnotatedClass(StrTestEntity.class);
|
||||||
|
config.addAnnotatedClass(GivenIdStrEntity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Priority(10)
|
||||||
|
public void initData() {
|
||||||
|
// Revision 1
|
||||||
|
getSession().getTransaction().begin();
|
||||||
|
GivenIdStrEntity entity = new GivenIdStrEntity(1, "data");
|
||||||
|
getSession().persist(entity);
|
||||||
|
getSession().getTransaction().commit();
|
||||||
|
|
||||||
|
// Revision 2
|
||||||
|
getSession().getTransaction().begin();
|
||||||
|
getSession().persist(new StrTestEntity("another data")); // Just to create second revision.
|
||||||
|
entity = (GivenIdStrEntity) getSession().get(GivenIdStrEntity.class, 1);
|
||||||
|
getSession().delete(entity); // First try to remove the entity.
|
||||||
|
getSession().save(entity); // Then save it.
|
||||||
|
getSession().getTransaction().commit();
|
||||||
|
|
||||||
|
// Revision 3
|
||||||
|
getSession().getTransaction().begin();
|
||||||
|
entity = (GivenIdStrEntity) getSession().get(GivenIdStrEntity.class, 1);
|
||||||
|
getSession().delete(entity); // First try to remove the entity.
|
||||||
|
entity.setData("modified data"); // Then change it's state.
|
||||||
|
getSession().save(entity); // Finally save it.
|
||||||
|
getSession().getTransaction().commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRevisionsCountOfGivenIdStrEntity() {
|
||||||
|
// Revision 2 has not changed entity's state.
|
||||||
|
Assert.assertEquals(Arrays.asList(1, 3), getAuditReader().getRevisions(GivenIdStrEntity.class, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistoryOfGivenIdStrEntity() {
|
||||||
|
Assert.assertEquals(new GivenIdStrEntity(1, "data"), getAuditReader().find(GivenIdStrEntity.class, 1, 1));
|
||||||
|
Assert.assertEquals(new GivenIdStrEntity(1, "modified data"), getAuditReader().find(GivenIdStrEntity.class, 1, 3));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package org.hibernate.envers.test.integration.merge;
|
||||||
|
|
||||||
|
import org.hibernate.envers.Audited;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Audited
|
||||||
|
public class GivenIdStrEntity {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private String data;
|
||||||
|
|
||||||
|
public GivenIdStrEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public GivenIdStrEntity(Integer id, String data) {
|
||||||
|
this.id = id;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof GivenIdStrEntity)) return false;
|
||||||
|
|
||||||
|
GivenIdStrEntity that = (GivenIdStrEntity) 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 "GivenIdStrEntity(id = " + id + ", data = " + data + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue