Merge pull request #204 from lukasz-antoniak/HHH-6753

HHH-6753 - Envers WorkUnits: Problems with merge
This commit is contained in:
Adam Warski 2011-11-08 03:07:56 -08:00
commit 199f3e300c
5 changed files with 173 additions and 3 deletions

View File

@ -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;
} }

View File

@ -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) {

View File

@ -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;
}
} }

View File

@ -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));
}
}

View File

@ -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;
}
}