HHH-5580 - Modified EntityTrackingRevisionListener interface

This commit is contained in:
Lukasz Antoniak 2011-05-07 19:06:21 +02:00
parent 88eda2b081
commit 53699cecac
28 changed files with 267 additions and 138 deletions

View File

@ -210,22 +210,28 @@ public interface AuditReader {
throws HibernateException;
/**
* Find all entities changed (added, updated and removed) in a given revision.
* Find all entities changed (added, updated and removed) in a given revision. Executes <i>n+1</i> queries,
* where <i>n</i> is a number of entity classes modified within specified revision.
* @param revision Revision number.
* @return Snapshots of all audited entities changed in a given revision.
* @throws IllegalStateException If the associated entity manager is closed.
* @throws IllegalArgumentException If a revision number is <code>null</code>, less or equal to 0.
* @throws AuditException If none of the following conditions is satisfied:
* <ul>
* <li><code>org.hibernate.envers.track_entities_changed_in_revision</code> parameter is set to <code>true</code>.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) extends {@link DefaultTrackingModifiedTypesRevisionEntity} base class.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) encapsulates a field marked with {@link ModifiedEntityNames} interface.</li>
* <li><code>org.hibernate.envers.track_entities_changed_in_revision</code>
* parameter is set to <code>true</code>.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity})
* extends {@link DefaultTrackingModifiedTypesRevisionEntity} base class.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) encapsulates a field
* marked with {@link ModifiedEntityNames} interface.</li>
* </ul>
*/
List findEntitiesChangedInRevision(Number revision) throws IllegalStateException, IllegalArgumentException, AuditException;
List<Object> findEntitiesChangedInRevision(Number revision)
throws IllegalStateException, IllegalArgumentException, AuditException;
/**
* Find all entities changed (added, updated or removed) in a given revision.
* Find all entities changed (added, updated or removed) in a given revision. Executes <i>n+1</i> queries,
* where <i>n</i> is a number of entity classes modified within specified revision.
* @param revision Revision number.
* @param revisionType Type of modification.
* @return Snapshots of all audited entities changed in a given revision filtered by modification type.
@ -233,27 +239,40 @@ public interface AuditReader {
* @throws IllegalArgumentException If a revision number is <code>null</code>, less or equal to 0.
* @throws AuditException If none of the following conditions is satisfied:
* <ul>
* <li><code>org.hibernate.envers.track_entities_changed_in_revision</code> parameter is set to <code>true</code>.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) extends {@link DefaultTrackingModifiedTypesRevisionEntity} base class.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) encapsulates a field marked with {@link ModifiedEntityNames} interface.</li>
* <li><code>org.hibernate.envers.track_entities_changed_in_revision</code>
* parameter is set to <code>true</code>.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity})
* extends {@link DefaultTrackingModifiedTypesRevisionEntity} base class.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) encapsulates a field
* marked with {@link ModifiedEntityNames} interface.</li>
* </ul>
*/
List findEntitiesChangedInRevision(Number revision, RevisionType revisionType) throws IllegalStateException, IllegalArgumentException, AuditException;
List<Object> findEntitiesChangedInRevision(Number revision, RevisionType revisionType)
throws IllegalStateException, IllegalArgumentException, AuditException;
/**
* Find all entities changed (added, updated and removed) in a given revision grouped by modification type.
* Executes <i>mn+1</i> queries, where:
* <ul>
* <li><i>n</i> - number of entity classes modified within specified revision.
* <li><i>m</i> - number of different revision types. See {@link RevisionType} enum.
* </ul>
* @param revision Revision number.
* @return Map containing lists of entity snapshots grouped by modification operation (e.g. addition, update, removal).
* @throws IllegalStateException If the associated entity manager is closed.
* @throws IllegalArgumentException If a revision number is <code>null</code>, less or equal to 0.
* @throws AuditException If none of the following conditions is satisfied:
* <ul>
* <li><code>org.hibernate.envers.track_entities_changed_in_revision</code> parameter is set to <code>true</code>.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) extends {@link DefaultTrackingModifiedTypesRevisionEntity} base class.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) encapsulates a field marked with {@link ModifiedEntityNames} interface.</li>
* <li><code>org.hibernate.envers.track_entities_changed_in_revision</code>
* parameter is set to <code>true</code>.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity})
* extends {@link DefaultTrackingModifiedTypesRevisionEntity} base class.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) encapsulates a field
* marked with {@link ModifiedEntityNames} interface.</li>
* </ul>
*/
Map<RevisionType, List> findEntitiesChangedInRevisionGroupByRevisionType(Number revision) throws IllegalStateException, IllegalArgumentException, AuditException;
Map<RevisionType, List<Object>> findEntitiesChangedInRevisionGroupByRevisionType(Number revision)
throws IllegalStateException, IllegalArgumentException, AuditException;
/**
* Returns list of entity classes modified in a given revision.
@ -263,10 +282,14 @@ public interface AuditReader {
* @throws IllegalArgumentException If a revision number is <code>null</code>, less or equal to 0.
* @throws AuditException If none of the following conditions is satisfied:
* <ul>
* <li><code>org.hibernate.envers.track_entities_changed_in_revision</code> parameter is set to <code>true</code>.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) extends {@link DefaultTrackingModifiedTypesRevisionEntity} base class.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) encapsulates a field marked with {@link ModifiedEntityNames} interface.</li>
* <li><code>org.hibernate.envers.track_entities_changed_in_revision</code>
* parameter is set to <code>true</code>.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity})
* extends {@link DefaultTrackingModifiedTypesRevisionEntity} base class.</li>
* <li>Custom revision entity (annotated with {@link RevisionEntity}) encapsulates a field
* marked with {@link ModifiedEntityNames} interface.</li>
* </ul>
*/
List<Class> findEntityTypesChangedInRevision(Number revision) throws IllegalStateException, IllegalArgumentException, AuditException;
List<Class> findEntityTypesChangedInRevision(Number revision)
throws IllegalStateException, IllegalArgumentException, AuditException;
}

View File

@ -1,23 +1,23 @@
package org.hibernate.envers;
import java.io.Serializable;
/**
* Extension of standard {@link RevisionListener} that notifies whenever tracking of each audited entity
* instance is started or stopped within the current revision boundaries.
* Extension of standard {@link RevisionListener} that notifies whenever an entity instance has been
* added, modified or removed within current revision boundaries.
* @see RevisionListener
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public interface EntityTrackingRevisionListener extends RevisionListener {
/**
* Called after audited entity data has been persisted.
* @param entityName Name of the audited entity.
* @param entityClass Audited entity class.
* @param entityName Name of the audited entity. May be useful when Java class is mapped multiple times,
* potentially to different tables.
* @param entityId Identifier of modified entity.
* @param revisionType Modification type (addition, update or removal).
* @param revisionEntity An instance of the entity annotated with {@link RevisionEntity}.
*/
void addEntityToRevision(String entityName, Object revisionEntity);
/**
* Called when persistence of a given audited entity snapshot has been already performed in a previous unit of work.
* @param entityName Name of the audited entity.
* @param revisionEntity An instance of the entity annotated with {@link RevisionEntity}.
*/
void removeEntityFromRevision(String entityName, Object revisionEntity);
void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
Object revisionEntity);
}

View File

@ -7,7 +7,7 @@ import java.lang.annotation.Target;
/**
* Marks a property which will hold the collection of entity names modified during each revision.
* This annotation expects field of Set&lt;String&gt; type.
* This annotation expects field of <code>{@literal Set<String>}</code> type.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Retention(RetentionPolicy.RUNTIME)

View File

@ -269,10 +269,14 @@ public class RevisionInfoConfiguration {
revisionInfoClass = pc.getMappedClass();
revisionInfoTimestampType = pc.getProperty(revisionInfoTimestampData.getName()).getType();
if (globalCfg.isTrackEntitiesChangedInRevisionEnabled() || DefaultTrackingModifiedTypesRevisionEntity.class.isAssignableFrom(revisionInfoClass) || modifiedEntityNamesFound.isSet()) {
// If modified entities tracking parameter is enabled, custom revision info class is a subtype of DefaultTrackingModifiedTypesRevisionEntity, or @ModifiedEntityNames annotation is used.
revisionInfoGenerator = new DefaultTrackingModifiedTypesRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,
revisionEntity.value(), revisionInfoTimestampData, isTimestampAsDate(), modifiedEntityNamesData);
if (globalCfg.isTrackEntitiesChangedInRevisionEnabled() ||
DefaultTrackingModifiedTypesRevisionEntity.class.isAssignableFrom(revisionInfoClass) ||
modifiedEntityNamesFound.isSet()) {
// If tracking modified entities parameter is enabled, custom revision info entity is a subtype
// of DefaultTrackingModifiedTypesRevisionEntity class, or @ModifiedEntityNames annotation is used.
revisionInfoGenerator = new DefaultTrackingModifiedTypesRevisionInfoGenerator(revisionInfoEntityName,
revisionInfoClass, revisionEntity.value(), revisionInfoTimestampData, isTimestampAsDate(),
modifiedEntityNamesData);
globalCfg.setTrackEntitiesChangedInRevisionEnabled(true);
} else {
revisionInfoGenerator = new DefaultRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,

View File

@ -77,7 +77,7 @@ public class AuditQueryCreator {
}
/**
* In comparison to {@link #forEntitiesAtRevision(Class, String, Number)} this method will return an empty
* In comparison to {@link #forEntitiesAtRevision(Class, String, Number)} this query will return an empty
* collection if an entity of a certain type has not been changed in a given revision.
* @param c Class of the entities for which to query.
* @param revision Revision number at which to execute the query.
@ -85,7 +85,7 @@ public class AuditQueryCreator {
* can then be executed.
* @see #forEntitiesAtRevision(Class, String, Number)
*/
public AuditQuery forEntitiesAtCertainRevision(Class<?> c, Number revision) {
public AuditQuery forEntitiesModifiedAtRevision(Class<?> c, Number revision) {
checkNotNull(revision, "Entity revision");
checkPositive(revision, "Entity revision");
return new EntitiesModifiedAtRevisionQuery(auditCfg, auditReaderImplementor, c, revision);

View File

@ -10,7 +10,7 @@ import java.util.ArrayList;
import java.util.List;
/**
* In comparison to {@link EntitiesAtRevisionQuery} this query will return an empty collection if an entity
* In comparison to {@link EntitiesAtRevisionQuery} this query returns an empty collection if an entity
* of a certain type has not been changed in a given revision.
* @see EntitiesAtRevisionQuery
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
@ -18,8 +18,8 @@ import java.util.List;
public class EntitiesModifiedAtRevisionQuery extends AbstractAuditQuery {
private final Number revision;
public EntitiesModifiedAtRevisionQuery(AuditConfiguration verCfg, AuditReaderImplementor versionsReader, Class<?> cls,
Number revision) {
public EntitiesModifiedAtRevisionQuery(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
Class<?> cls, Number revision) {
super(verCfg, versionsReader, cls);
this.revision = revision;
}

View File

@ -235,33 +235,38 @@ public class AuditReaderImpl implements AuditReaderImplementor {
}
@SuppressWarnings({"unchecked"})
public List findEntitiesChangedInRevision(Number revision) throws IllegalStateException, IllegalArgumentException, AuditException {
public List<Object> findEntitiesChangedInRevision(Number revision) throws IllegalStateException,
IllegalArgumentException, AuditException {
List<Class> clazz = findEntityTypesChangedInRevision(revision);
List result = new ArrayList(clazz.size());
List<Object> result = new ArrayList<Object>();
for (Class c : clazz) {
result.addAll(createQuery().forEntitiesAtCertainRevision(c, revision).getResultList());
result.addAll(createQuery().forEntitiesModifiedAtRevision(c, revision).getResultList());
}
return result;
}
@SuppressWarnings({"unchecked"})
public List findEntitiesChangedInRevision(Number revision, RevisionType revisionType) throws IllegalStateException, IllegalArgumentException, AuditException {
public List<Object> findEntitiesChangedInRevision(Number revision, RevisionType revisionType)
throws IllegalStateException, IllegalArgumentException, AuditException {
List<Class> clazz = findEntityTypesChangedInRevision(revision);
List result = new ArrayList(clazz.size());
List<Object> result = new ArrayList<Object>();
for (Class c : clazz) {
result.addAll(createQuery().forEntitiesAtCertainRevision(c, revision).add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList());
result.addAll(createQuery().forEntitiesModifiedAtRevision(c, revision)
.add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList());
}
return result;
}
@SuppressWarnings({"unchecked"})
public Map<RevisionType, List> findEntitiesChangedInRevisionGroupByRevisionType(Number revision) throws IllegalStateException, IllegalArgumentException, AuditException {
public Map<RevisionType, List<Object>> findEntitiesChangedInRevisionGroupByRevisionType(Number revision)
throws IllegalStateException, IllegalArgumentException, AuditException {
List<Class> clazz = findEntityTypesChangedInRevision(revision);
Map<RevisionType, List> result = new HashMap<RevisionType, List>();
Map<RevisionType, List<Object>> result = new HashMap<RevisionType, List<Object>>();
for (RevisionType revisionType : RevisionType.values()) {
result.put(revisionType, new ArrayList());
for (Class c : clazz) {
List list = createQuery().forEntitiesAtCertainRevision(c, revision).add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList();
List<Object> list = createQuery().forEntitiesModifiedAtRevision(c, revision)
.add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList();
result.get(revisionType).addAll(list);
}
}
@ -269,7 +274,8 @@ public class AuditReaderImpl implements AuditReaderImplementor {
}
@SuppressWarnings({"unchecked"})
public List<Class> findEntityTypesChangedInRevision(Number revision) throws IllegalStateException, IllegalArgumentException, AuditException {
public List<Class> findEntityTypesChangedInRevision(Number revision) throws IllegalStateException,
IllegalArgumentException, AuditException {
checkNotNull(revision, "Entity revision");
checkPositive(revision, "Entity revision");
checkSession();

View File

@ -22,16 +22,19 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.revisioninfo;
import java.util.Date;
import org.hibernate.MappingException;
import org.hibernate.Session;
import org.hibernate.envers.EntityTrackingRevisionListener;
import org.hibernate.envers.RevisionListener;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.entities.PropertyData;
import org.hibernate.envers.tools.reflection.ReflectionTools;
import org.hibernate.property.Setter;
import java.io.Serializable;
import java.util.Date;
/**
* @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
@ -90,15 +93,11 @@ public class DefaultRevisionInfoGenerator implements RevisionInfoGenerator {
return revisionInfo;
}
public void addEntityToRevision(String entityName, Object revisionInfo) {
public void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
Object revisionInfo) {
if (listener instanceof EntityTrackingRevisionListener) {
((EntityTrackingRevisionListener) listener).addEntityToRevision(entityName, revisionInfo);
}
}
public void removeEntityFromRevision(String entityName, Object revisionInfo) {
if (listener instanceof EntityTrackingRevisionListener) {
((EntityTrackingRevisionListener) listener).removeEntityFromRevision(entityName, revisionInfo);
((EntityTrackingRevisionListener) listener).entityChanged(entityClass, entityName, entityId, revisionType,
revisionInfo);
}
}
}

View File

@ -1,16 +1,18 @@
package org.hibernate.envers.revisioninfo;
import org.hibernate.envers.RevisionListener;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.entities.PropertyData;
import org.hibernate.envers.tools.reflection.ReflectionTools;
import org.hibernate.property.Getter;
import org.hibernate.property.Setter;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
/**
* Automatically adds and removes entity names changed during current revision.
* Automatically adds entity names changed during current revision.
* @see org.hibernate.envers.ModifiedEntityNames
* @see org.hibernate.envers.DefaultTrackingModifiedTypesRevisionEntity
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
@ -31,25 +33,14 @@ public class DefaultTrackingModifiedTypesRevisionInfoGenerator extends DefaultRe
@Override
@SuppressWarnings({"unchecked"})
public void addEntityToRevision(String entityName, Object revisionInfo) {
super.addEntityToRevision(entityName, revisionInfo);
Set<String> modifiedEntityNames = (Set<String>) modifiedEntityNamesGetter.get(revisionInfo);
public void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
Object revisionEntity) {
super.entityChanged(entityClass, entityName, entityId, revisionType, revisionEntity);
Set<String> modifiedEntityNames = (Set<String>) modifiedEntityNamesGetter.get(revisionEntity);
if (modifiedEntityNames == null) {
modifiedEntityNames = new HashSet<String>();
modifiedEntityNamesSetter.set(revisionEntity, modifiedEntityNames, null);
}
modifiedEntityNames.add(entityName);
modifiedEntityNamesSetter.set(revisionInfo, modifiedEntityNames, null);
}
@Override
@SuppressWarnings({"unchecked"})
public void removeEntityFromRevision(String entityName, Object revisionInfo) {
super.removeEntityFromRevision(entityName, revisionInfo);
Set<String> modifiedEntityNames = (Set<String>) modifiedEntityNamesGetter.get(revisionInfo);
if (modifiedEntityNames == null) {
return;
}
modifiedEntityNames.remove(entityName);
modifiedEntityNamesSetter.set(revisionInfo, modifiedEntityNames, null);
modifiedEntityNames.add(entityClass.getName());
}
}

View File

@ -22,7 +22,12 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.revisioninfo;
import org.hibernate.Session;
import org.hibernate.envers.EntityTrackingRevisionListener;
import org.hibernate.envers.RevisionType;
import java.io.Serializable;
/**
* @author Adam Warski (adam at warski dot org)
@ -32,12 +37,8 @@ public interface RevisionInfoGenerator {
Object generate();
/**
* @see org.hibernate.envers.EntityTrackingRevisionListener#addEntityToRevision(String, Object)
* @see EntityTrackingRevisionListener#entityChanged(Class, String, Serializable, RevisionType, Object)
*/
void addEntityToRevision(String entityName, Object revisionInfo);
/**
* @see org.hibernate.envers.EntityTrackingRevisionListener#removeEntityFromRevision(String, Object)
*/
void removeEntityFromRevision(String entityName, Object revisionInfo);
void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
Object revisionEntity);
}

View File

@ -22,17 +22,21 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.synchronization;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.envers.revisioninfo.RevisionInfoGenerator;
import org.hibernate.envers.synchronization.work.AuditWorkUnit;
import org.hibernate.envers.synchronization.work.PersistentCollectionChangeWorkUnit;
import org.hibernate.envers.tools.Pair;
import org.hibernate.persister.entity.EntityPersister;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
/**
* @author Adam Warski (adam at warski dot org)
@ -105,15 +109,25 @@ public class AuditProcess implements BeforeTransactionCompletionProcess {
// First undoing any performed work units
while ((vwu = undoQueue.poll()) != null) {
vwu.undo(session);
revisionInfoGenerator.removeEntityFromRevision(vwu.getEntityName(), currentRevisionData);
}
while ((vwu = workUnits.poll()) != null) {
vwu.perform(session, revisionData);
revisionInfoGenerator.addEntityToRevision(vwu.getEntityName(), currentRevisionData);
if (!(vwu instanceof PersistentCollectionChangeWorkUnit)) {
Class entityClass = getEntityClass(this.session, session, vwu.getEntityName());
revisionInfoGenerator.entityChanged(entityClass, vwu.getEntityName(), vwu.getEntityId(), vwu.getRevisionType(), currentRevisionData);
}
}
}
/**
* @return Java class mapped to specified entity name.
*/
private Class getEntityClass(SessionImplementor sessionImplementor, Session session, String entityName) {
EntityPersister entityPersister = sessionImplementor.getFactory().getEntityPersister(entityName);
return entityPersister.getClassMetadata().getMappedClass(session.getEntityMode());
}
public Object getCurrentRevisionData(Session session, boolean persist) {
// Generating the revision data if not yet generated
if (revisionData == null) {

View File

@ -22,9 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.synchronization.work;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.Session;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.envers.RevisionType;
@ -32,9 +30,14 @@ import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.strategy.AuditStrategy;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* @author Adam Warski (adam at warski dot org)
* @author Stephanie Pau at Markit Group Plc
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public abstract class AbstractAuditWorkUnit implements AuditWorkUnit {
protected final SessionImplementor sessionImplementor;
@ -42,19 +45,21 @@ public abstract class AbstractAuditWorkUnit implements AuditWorkUnit {
protected final Serializable id;
protected final String entityName;
protected final AuditStrategy auditStrategy;
protected final RevisionType revisionType;
private Object performedData;
protected AbstractAuditWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
Serializable id) {
Serializable id, RevisionType revisionType) {
this.sessionImplementor = sessionImplementor;
this.verCfg = verCfg;
this.id = id;
this.entityName = entityName;
this.revisionType = revisionType;
this.auditStrategy = verCfg.getAuditStrategy();
}
protected void fillDataWithId(Map<String, Object> data, Object revision, RevisionType revisionType) {
protected void fillDataWithId(Map<String, Object> data, Object revision) {
AuditEntitiesConfiguration entitiesCfg = verCfg.getAuditEntCfg();
Map<String, Object> originalId = new HashMap<String, Object>();
@ -73,7 +78,7 @@ public abstract class AbstractAuditWorkUnit implements AuditWorkUnit {
setPerformed(data);
}
public Object getEntityId() {
public Serializable getEntityId() {
return id;
}
@ -95,4 +100,8 @@ public abstract class AbstractAuditWorkUnit implements AuditWorkUnit {
session.flush();
}
}
public RevisionType getRevisionType() {
return revisionType;
}
}

View File

@ -38,7 +38,7 @@ public class AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
public AddWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
Serializable id, EntityPersister entityPersister, Object[] state) {
super(sessionImplementor, entityName, verCfg, id);
super(sessionImplementor, entityName, verCfg, id, RevisionType.ADD);
data = new HashMap<String, Object>();
verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(sessionImplementor, data,
@ -47,7 +47,7 @@ public class AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
public AddWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
Serializable id, Map<String, Object> data) {
super(sessionImplementor, entityName, verCfg, id);
super(sessionImplementor, entityName, verCfg, id, RevisionType.ADD);
this.data = data;
}
@ -57,7 +57,7 @@ public class AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
}
public Map<String, Object> generateData(Object revisionData) {
fillDataWithId(data, revisionData, RevisionType.ADD);
fillDataWithId(data, revisionData);
return data;
}

View File

@ -22,15 +22,19 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.synchronization.work;
import java.util.Map;
import org.hibernate.Session;
import org.hibernate.envers.RevisionType;
import java.io.Serializable;
import java.util.Map;
/**
* TODO: refactor constructors into factory methods
* @author Adam Warski (adam at warski dot org)
*/
public interface AuditWorkUnit extends WorkUnitMergeVisitor, WorkUnitMergeDispatcher {
Object getEntityId();
Serializable getEntityId();
String getEntityName();
boolean containsWork();
@ -52,4 +56,9 @@ public interface AuditWorkUnit extends WorkUnitMergeVisitor, WorkUnitMergeDispat
* @return Generates data that should be saved when performing this work unit.
*/
Map<String, Object> generateData(Object revisionData);
/**
* @return Performed modification type.
*/
RevisionType getRevisionType();
}

View File

@ -37,7 +37,7 @@ public class CollectionChangeWorkUnit extends AbstractAuditWorkUnit implements A
public CollectionChangeWorkUnit(SessionImplementor session, String entityName, AuditConfiguration verCfg,
Serializable id, Object entity) {
super(session, entityName, verCfg, id);
super(session, entityName, verCfg, id, RevisionType.MOD);
this.entity = entity;
}
@ -48,7 +48,7 @@ public class CollectionChangeWorkUnit extends AbstractAuditWorkUnit implements A
public Map<String, Object> generateData(Object revisionData) {
Map<String, Object> data = new HashMap<String, Object>();
fillDataWithId(data, revisionData, RevisionType.MOD);
fillDataWithId(data, revisionData);
verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().mapToMapFromEntity(sessionImplementor,
data, entity, null);

View File

@ -39,7 +39,7 @@ public class DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
public DelWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
Serializable id, EntityPersister entityPersister, Object[] state) {
super(sessionImplementor, entityName, verCfg, id);
super(sessionImplementor, entityName, verCfg, id, RevisionType.DEL);
this.state = state;
this.propertyNames = entityPersister.getPropertyNames();
@ -51,7 +51,7 @@ public class DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
public Map<String, Object> generateData(Object revisionData) {
Map<String, Object> data = new HashMap<String, Object>();
fillDataWithId(data, revisionData, RevisionType.DEL);
fillDataWithId(data, revisionData);
if (verCfg.getGlobalCfg().isStoreDataAtDelete()) {
verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(sessionImplementor, data,

View File

@ -28,7 +28,7 @@ public class FakeBidirectionalRelationWorkUnit extends AbstractAuditWorkUnit imp
RelationDescription rd, RevisionType revisionType,
Object index,
AuditWorkUnit nestedWorkUnit) {
super(sessionImplementor, entityName, verCfg, id);
super(sessionImplementor, entityName, verCfg, id, revisionType);
this.nestedWorkUnit = nestedWorkUnit;
// Adding the change for the relation.
@ -39,14 +39,14 @@ public class FakeBidirectionalRelationWorkUnit extends AbstractAuditWorkUnit imp
public FakeBidirectionalRelationWorkUnit(FakeBidirectionalRelationWorkUnit original,
Map<String, FakeRelationChange> fakeRelationChanges,
AuditWorkUnit nestedWorkUnit) {
super(original.sessionImplementor, original.entityName, original.verCfg, original.id);
super(original.sessionImplementor, original.entityName, original.verCfg, original.id, original.revisionType);
this.fakeRelationChanges = fakeRelationChanges;
this.nestedWorkUnit = nestedWorkUnit;
}
public FakeBidirectionalRelationWorkUnit(FakeBidirectionalRelationWorkUnit original, AuditWorkUnit nestedWorkUnit) {
super(original.sessionImplementor, original.entityName, original.verCfg, original.id);
super(original.sessionImplementor, original.entityName, original.verCfg, original.id, original.revisionType);
this.nestedWorkUnit = nestedWorkUnit;

View File

@ -23,6 +23,8 @@
*/
package org.hibernate.envers.synchronization.work;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.engine.SessionImplementor;
@ -39,7 +41,7 @@ public class ModWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
public ModWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
Serializable id, EntityPersister entityPersister, Object[] newState, Object[] oldState) {
super(sessionImplementor, entityName, verCfg, id);
super(sessionImplementor, entityName, verCfg, id, RevisionType.MOD);
data = new HashMap<String, Object>();
changes = verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(sessionImplementor, data,
@ -51,7 +53,7 @@ public class ModWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit
}
public Map<String, Object> generateData(Object revisionData) {
fillDataWithId(data, revisionData, RevisionType.MOD);
fillDataWithId(data, revisionData);
return data;
}

View File

@ -23,10 +23,8 @@
*/
package org.hibernate.envers.synchronization.work;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import org.hibernate.Session;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.engine.CollectionEntry;
@ -47,7 +45,7 @@ public class PersistentCollectionChangeWorkUnit extends AbstractAuditWorkUnit im
AuditConfiguration auditCfg, PersistentCollection collection,
CollectionEntry collectionEntry, Serializable snapshot, Serializable id,
String referencingPropertyName) {
super(sessionImplementor, entityName, auditCfg, new PersistentCollectionChangeWorkUnitId(id, collectionEntry.getRole()));
super(sessionImplementor, entityName, auditCfg, new PersistentCollectionChangeWorkUnitId(id, collectionEntry.getRole()), null);
this.referencingPropertyName = referencingPropertyName;
@ -59,7 +57,7 @@ public class PersistentCollectionChangeWorkUnit extends AbstractAuditWorkUnit im
AuditConfiguration verCfg, Serializable id,
List<PersistentCollectionChangeData> collectionChanges,
String referencingPropertyName) {
super(sessionImplementor, entityName, verCfg, id);
super(sessionImplementor, entityName, verCfg, id, null);
this.collectionChanges = collectionChanges;
this.referencingPropertyName = referencingPropertyName;
@ -81,7 +79,7 @@ public class PersistentCollectionChangeWorkUnit extends AbstractAuditWorkUnit im
// Setting the revision number
((Map<String, Object>) persistentCollectionChangeData.getData().get(entitiesCfg.getOriginalIdPropName()))
.put(entitiesCfg.getRevisionFieldName(), revisionData);
auditStrategy.performCollectionChange(session, verCfg, persistentCollectionChangeData, revisionData);
}
}

View File

@ -10,6 +10,7 @@ import java.util.HashSet;
import java.util.Set;
/**
* Sample revision entity that uses {@link ModifiedEntityNames} annotation.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity
@ -27,7 +28,7 @@ public class AnnotatedTrackingRevisionEntity {
@JoinTable(name = "REVENTITY", joinColumns = @JoinColumn(name = "REV"))
@Column(name = "ENTITYNAME")
@ModifiedEntityNames
private Set<String> modifiedEntityNames;
private Set<String> entityNames;
public int getCustomId() {
return customId;
@ -45,12 +46,12 @@ public class AnnotatedTrackingRevisionEntity {
this.customTimestamp = customTimestamp;
}
public Set<String> getModifiedEntityNames() {
return modifiedEntityNames;
public Set<String> getEntityNames() {
return entityNames;
}
public void setModifiedEntityNames(Set<String> modifiedEntityNames) {
this.modifiedEntityNames = modifiedEntityNames;
public void setEntityNames(Set<String> entityNames) {
this.entityNames = entityNames;
}
public boolean equals(Object o) {

View File

@ -1,19 +1,18 @@
package org.hibernate.envers.test.entities.reventity.trackmodifiedentities;
import org.hibernate.envers.EntityTrackingRevisionListener;
import org.hibernate.envers.RevisionType;
import java.io.Serializable;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class CustomEntityTrackingRevisionListener implements EntityTrackingRevisionListener {
@Override
public void addEntityToRevision(String entityName, Object revisionEntity) {
((CustomTrackingRevisionEntity)revisionEntity).addModifiedEntityName(entityName);
}
@Override
public void removeEntityFromRevision(String entityName, Object revisionEntity) {
((CustomTrackingRevisionEntity)revisionEntity).removeModifiedEntityName(entityName);
public void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
Object revisionEntity) {
((CustomTrackingRevisionEntity)revisionEntity).addModifiedEntityName(entityClass.getName());
}
@Override

View File

@ -9,6 +9,7 @@ import java.util.HashSet;
import java.util.Set;
/**
* Revision entity which {@code modifiedEntityNames} field is manually populated by {@link CustomEntityTrackingRevisionListener}.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity

View File

@ -6,6 +6,7 @@ import javax.persistence.Id;
import javax.persistence.ManyToOne;
/**
* Custom detail of revision entity.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity

View File

@ -1,16 +1,11 @@
package org.hibernate.envers.test.integration.reventity.trackmodifiedentities;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.test.AbstractEntityTest;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.entities.StrIntTestEntity;
import org.hibernate.envers.test.entities.StrTestEntity;
import org.hibernate.envers.ModifiedEntityNames;
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.AnnotatedTrackingRevisionEntity;
import org.junit.Test;
import javax.persistence.EntityManager;
/**
* Tests proper behavior of revision entity that utilizes {@link ModifiedEntityNames} annotation.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class AnnotatedTrackingEntitiesTest extends DefaultTrackingEntitiesTest {

View File

@ -2,11 +2,13 @@ package org.hibernate.envers.test.integration.reventity.trackmodifiedentities;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.AuditReader;
import org.hibernate.envers.EntityTrackingRevisionListener;
import org.hibernate.envers.exception.AuditException;
import org.hibernate.envers.test.AbstractEntityTest;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.entities.StrIntTestEntity;
import org.hibernate.envers.test.entities.StrTestEntity;
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.CustomEntityTrackingRevisionListener;
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.CustomTrackingRevisionEntity;
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.ModifiedEntityNameEntity;
import org.hibernate.envers.test.tools.TestTools;
@ -15,6 +17,9 @@ import org.junit.Test;
import javax.persistence.EntityManager;
/**
* Tests proper behavior of entity listener that implements {@link EntityTrackingRevisionListener}
* interface. {@link CustomEntityTrackingRevisionListener} shall be notified whenever an entity instance has been
* added, modified or removed, so that changed entity type can be persisted.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class CustomTrackingEntitiesTest extends AbstractEntityTest {

View File

@ -17,6 +17,8 @@ import java.util.List;
import java.util.Map;
/**
* Tests proper behavior of tracking modified entity types when {@code org.hibernate.envers.track_entities_changed_in_revision}
* parameter is set to {@code true}.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@SuppressWarnings({"unchecked"})
@ -109,7 +111,7 @@ public class DefaultTrackingEntitiesTest extends AbstractEntityTest {
StrTestEntity ste = new StrTestEntity("x", steId);
StrIntTestEntity site = new StrIntTestEntity("y", 1, siteId);
Map<RevisionType, List> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(1);
Map<RevisionType, List<Object>> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(1);
assert Arrays.asList(ste, site).equals(result.get(RevisionType.ADD));
assert Arrays.asList().equals(result.get(RevisionType.MOD));
assert Arrays.asList().equals(result.get(RevisionType.DEL));
@ -119,7 +121,7 @@ public class DefaultTrackingEntitiesTest extends AbstractEntityTest {
public void testTrackModifiedEntitiesGroupByRevisionType() {
StrIntTestEntity site = new StrIntTestEntity("y", 2, siteId);
Map<RevisionType, List> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(2);
Map<RevisionType, List<Object>> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(2);
assert Arrays.asList().equals(result.get(RevisionType.ADD));
assert Arrays.asList(site).equals(result.get(RevisionType.MOD));
assert Arrays.asList().equals(result.get(RevisionType.DEL));
@ -130,7 +132,7 @@ public class DefaultTrackingEntitiesTest extends AbstractEntityTest {
StrTestEntity ste = new StrTestEntity(null, steId);
StrIntTestEntity site = new StrIntTestEntity(null, null, siteId);
Map<RevisionType, List> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(3);
Map<RevisionType, List<Object>> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(3);
assert Arrays.asList().equals(result.get(RevisionType.ADD));
assert Arrays.asList().equals(result.get(RevisionType.MOD));
assert Arrays.asList(ste, site).equals(result.get(RevisionType.DEL));

View File

@ -0,0 +1,67 @@
package org.hibernate.envers.test.integration.reventity.trackmodifiedentities;
import org.hibernate.MappingException;
import org.hibernate.envers.test.AbstractSessionTest;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.integration.entityNames.manyToManyAudited.Car;
import org.hibernate.envers.test.integration.entityNames.manyToManyAudited.Person;
import org.junit.Test;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class EntityNamesTest extends AbstractSessionTest {
protected void initMappings() throws MappingException, URISyntaxException {
URL url = Thread.currentThread().getContextClassLoader().getResource("mappings/entityNames/manyToManyAudited/mappings.hbm.xml");
config.addFile(new File(url.toURI()));
config.setProperty("org.hibernate.envers.track_entities_changed_in_revision", "true");
}
@Test
@Priority(10)
public void initData() {
Person pers1 = new Person("Hernan", 28);
Person pers2 = new Person("Leandro", 29);
Person pers3 = new Person("Barba", 32);
Person pers4 = new Person("Camomo", 15);
// Revision 1
getSession().getTransaction().begin();
List<Person > owners = new ArrayList<Person>();
owners.add(pers1);
owners.add(pers2);
owners.add(pers3);
Car car1 = new Car(5, owners);
getSession().persist(car1);
getSession().getTransaction().commit();
long person1Id = pers1.getId();
// Revision 2
owners = new ArrayList<Person>();
owners.add(pers2);
owners.add(pers3);
owners.add(pers4);
Car car2 = new Car(27, owners);
getSession().getTransaction().begin();
Person person1 = (Person)getSession().get("Personaje", person1Id);
person1.setName("Hernan David");
person1.setAge(40);
getSession().persist(car1);
getSession().persist(car2);
getSession().getTransaction().commit();
}
@Test
@SuppressWarnings("unchecked")
public void testModifiedEntityTypes() {
assert Arrays.asList(Car.class, Person.class).equals(getAuditReader().findEntityTypesChangedInRevision(1));
assert Arrays.asList(Car.class, Person.class).equals(getAuditReader().findEntityTypesChangedInRevision(2));
}
}

View File

@ -2,11 +2,13 @@ package org.hibernate.envers.test.integration.reventity.trackmodifiedentities;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.AuditReader;
import org.hibernate.envers.DefaultTrackingModifiedTypesRevisionEntity;
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.ExtendedRevisionEntity;
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.ExtendedRevisionListener;
import org.junit.Test;
/**
* Tests proper behavior of revision entity that extends {@link DefaultTrackingModifiedTypesRevisionEntity}.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class ExtendedRevisionEntityTest extends DefaultTrackingEntitiesTest {