Merge branch 'HHH-5580-POC' of git://github.com/lukasz-antoniak/hibernate-core
Conflicts: hibernate-envers/src/main/java/org/hibernate/envers/reader/AuditReaderImpl.java
This commit is contained in:
commit
f865be9739
|
@ -16,7 +16,8 @@
|
|||
which can be used to identify groups of changes (much like a change set in source control). As the revisions
|
||||
are global, having a revision number, you can query for various entities at that revision, retrieving a
|
||||
(partial) view of the database at that revision. You can find a revision number having a date, and the other
|
||||
way round, you can get the date at which a revision was committed.
|
||||
way round, you can get the date at which a revision was committed. Since version 4.0, Envers enables you
|
||||
to retrieve all changes performed in a certain revision.
|
||||
</para>
|
||||
</preface>
|
||||
|
||||
|
@ -243,6 +244,24 @@
|
|||
evaluates to true
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<property>org.hibernate.envers.track_entities_changed_in_revision</property>
|
||||
</entry>
|
||||
<entry>
|
||||
false
|
||||
</entry>
|
||||
<entry>
|
||||
Should entity types, that have been modified during each revision, be tracked. The default
|
||||
implementation creates <literal>REVCHANGES</literal> table that stores fully qualified names
|
||||
of Java classes modified in a specified revision. Single record encapsulates the revision
|
||||
identifier (foreign key to <literal>REVINFO</literal> table) and a string value. This
|
||||
feature shall be used when entity name can be clearly identified by Java class type. Otherwise
|
||||
extend <interfacename>org.hibernate.envers.EntityTrackingRevisionListener</interfacename>
|
||||
interface. For more information refer to <xref linkend="envers-tracking-modified-entities-reventity"/>
|
||||
and <xref linkend="envers-tracking-modified-entities-queries"/>.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
@ -446,6 +465,134 @@ public class ExampleListener implements RevisionListener {
|
|||
|
||||
</example>
|
||||
|
||||
<section id="envers-tracking-modified-entities-reventity">
|
||||
<title>Tracking entity classes modified in revision</title>
|
||||
<para>
|
||||
By default entity types that have been changed in each revision are not being tracked. This implies the
|
||||
necessity to query all tables storing audited data in order to retrieve changes made during
|
||||
specified revision. Envers provides a simple mechanism that creates <literal>REVCHANGES</literal>
|
||||
table which stores fully qualified names of Java classes modified in each revision.
|
||||
Single record encapsulates the revision identifier (foreign key to <literal>REVINFO</literal> table)
|
||||
and a string value. Note that this mechanism shall be used when entity name can be clearly identified
|
||||
by Java class type. Otherwise extend <interfacename>org.hibernate.envers.EntityTrackingRevisionListener</interfacename>
|
||||
interface (described further).
|
||||
</para>
|
||||
<para>
|
||||
Tracking of modified entity types can be enabled in three different ways:
|
||||
</para>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
Set <property>org.hibernate.envers.track_entities_changed_in_revision</property> parameter to
|
||||
<literal>true</literal>. In this case
|
||||
<classname>org.hibernate.envers.DefaultTrackingModifiedTypesRevisionEntity</classname> will
|
||||
be implicitly used as the revision log entity.
|
||||
</listitem>
|
||||
<listitem>
|
||||
Create a custom revision entity that extends
|
||||
<classname>org.hibernate.envers.DefaultTrackingModifiedTypesRevisionEntity</classname> class.
|
||||
<programlisting>
|
||||
<![CDATA[@Entity
|
||||
@RevisionEntity
|
||||
public class ExtendedRevisionEntity extends DefaultTrackingModifiedTypesRevisionEntity {
|
||||
...
|
||||
}]]></programlisting>
|
||||
</listitem>
|
||||
<listitem>
|
||||
Mark an appropriate field of a custom revision entity with
|
||||
<interfacename>@org.hibernate.envers.ModifiedEntityTypes</interfacename> annotation. The property is
|
||||
required to be of <literal><![CDATA[Set<String>]]></literal> type.
|
||||
<programlisting>
|
||||
<![CDATA[@Entity
|
||||
@RevisionEntity
|
||||
public class AnnotatedTrackingRevisionEntity {
|
||||
...
|
||||
|
||||
@ElementCollection
|
||||
@JoinTable(name = "REVCHANGES", joinColumns = @JoinColumn(name = "REV"))
|
||||
@Column(name = "ENTITYTYPE")
|
||||
@ModifiedEntityTypes
|
||||
private Set<String> modifiedEntityTypes;
|
||||
|
||||
...
|
||||
}]]></programlisting>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
<para>
|
||||
Users, that have chosen one of the approaches listed above, can retrieve all entities modified in
|
||||
specified revision by utilizing API described in <xref linkend="envers-tracking-modified-entities-queries"/>.
|
||||
</para>
|
||||
<para>
|
||||
Users are also allowed to implement custom mechanism of tracking modified entity types. In this case, they
|
||||
shall pass their own implementation of
|
||||
<interfacename>org.hibernate.envers.EntityTrackingRevisionListener</interfacename> interface as the value
|
||||
of <interfacename>@org.hibernate.envers.RevisionEntity</interfacename> annotation.
|
||||
<interfacename>EntityTrackingRevisionListener</interfacename> interface exposes one method that notifies
|
||||
whenever audited entity instance has been added, modified or removed within current revision boundaries.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Custom implementation of tracking entity classes modified during revisions</title>
|
||||
<programlisting>
|
||||
<filename>CustomEntityTrackingRevisionListener.java</filename>
|
||||
<![CDATA[
|
||||
public class CustomEntityTrackingRevisionListener implements EntityTrackingRevisionListener {
|
||||
@Override
|
||||
public void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
|
||||
Object revisionEntity) {
|
||||
((CustomTrackingRevisionEntity)revisionEntity).addModifiedEntityType(entityClass.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newRevision(Object revisionEntity) {
|
||||
}
|
||||
}]]></programlisting>
|
||||
<programlisting>
|
||||
<filename>CustomTrackingRevisionEntity.java</filename>
|
||||
<![CDATA[
|
||||
@Entity
|
||||
@RevisionEntity(CustomEntityTrackingRevisionListener.class)
|
||||
public class CustomTrackingRevisionEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
@RevisionNumber
|
||||
private int customId;
|
||||
|
||||
@RevisionTimestamp
|
||||
private long customTimestamp;
|
||||
|
||||
@OneToMany(mappedBy="revision", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
|
||||
private Set<ModifiedEntityTypeEntity> modifiedEntityTypes = new HashSet<ModifiedEntityTypeEntity>();
|
||||
|
||||
public void addModifiedEntityType(String entityClassName) {
|
||||
modifiedEntityTypes.add(new ModifiedEntityTypeEntity(this, entityClassName));
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
]]></programlisting>
|
||||
<programlisting>
|
||||
<filename>ModifiedEntityTypeEntity.java</filename>
|
||||
<![CDATA[
|
||||
@Entity
|
||||
public class ModifiedEntityTypeEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
|
||||
@ManyToOne
|
||||
private CustomTrackingRevisionEntity revision;
|
||||
|
||||
private String entityClassName;
|
||||
|
||||
...
|
||||
}
|
||||
]]></programlisting>
|
||||
<programlisting><![CDATA[CustomTrackingRevisionEntity revEntity =
|
||||
getAuditReader().findRevision(CustomTrackingRevisionEntity.class, revisionNumber);
|
||||
Set<ModifiedEntityTypeEntity> modifiedEntityTypes = revEntity.getModifiedEntityTypes()]]></programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="envers-queries">
|
||||
|
@ -637,6 +784,42 @@ query.add(AuditEntity.relatedId("address").eq(relatedEntityId));]]></programlist
|
|||
|
||||
</section>
|
||||
|
||||
<section id="envers-tracking-modified-entities-queries">
|
||||
<title>Querying for entities modified in a given revision</title>
|
||||
<para>
|
||||
The basic query allows retrieving entity types changed in a specified revision:
|
||||
</para>
|
||||
<programlisting><![CDATA[Set<Class> modifiedEntityTypes = getAuditReader()
|
||||
.findEntityTypesChangedInRevision(revisionNumber);]]></programlisting>
|
||||
<para>
|
||||
Other queries (accessible from <interfacename>org.hibernate.envers.AuditReader</interfacename>):
|
||||
</para>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<firstterm><methodname>List<![CDATA[<Object>]]> findEntitiesChangedInRevision(Number)</methodname></firstterm>
|
||||
- Returns snapshots of all audited entities changed (added, updated and removed) in a given revision.
|
||||
Executes <literal>n+1</literal> SQL queries, where <literal>n</literal> is a number of different entity
|
||||
classes modified within specified revision.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<firstterm><methodname>List<![CDATA[<Object>]]> findEntitiesChangedInRevision(Number, RevisionType)</methodname></firstterm>
|
||||
- Returns snapshots of all audited entities changed (added, updated or removed) in a given revision
|
||||
filtered by modification type. Executes <literal>n+1</literal> SQL queries, where <literal>n</literal>
|
||||
is a number of different entity classes modified within specified revision.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<firstterm><methodname><![CDATA[Map<RevisionType, List<Object>>]]> findEntitiesChangedInRevisionGroupByRevisionType(Number)</methodname></firstterm>
|
||||
- Returns a map containing lists of entity snapshots grouped by modification operation (e.g.
|
||||
addition, update and removal). Executes <literal>3n+1</literal> SQL queries, where <literal>n</literal>
|
||||
is a number of different entity classes modified within specified revision.
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
<para>
|
||||
Note that methods described above can be legally used only when default mechanism of
|
||||
tracking changed entity types is enabled (see <xref linkend="envers-tracking-modified-entities-reventity"/>).
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -27,13 +27,15 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.exception.NotAuditedException;
|
||||
import org.hibernate.envers.exception.RevisionDoesNotExistException;
|
||||
import org.hibernate.envers.query.AuditQueryCreator;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Hernán Chanfreau
|
||||
* @author Hernán Chanfreau
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public interface AuditReader {
|
||||
/**
|
||||
|
@ -176,35 +178,118 @@ public interface AuditReader {
|
|||
AuditQueryCreator createQuery();
|
||||
|
||||
/**
|
||||
* Checks if the entityClass was configured to be audited. Calling
|
||||
* isEntityNameAudited() with the string of the class name will return the
|
||||
* same value.
|
||||
* Checks if the entityClass was configured to be audited. Calling
|
||||
* isEntityNameAudited() with the string of the class name will return the
|
||||
* same value.
|
||||
*
|
||||
* @param entityClass
|
||||
* Class of the entity asking for audit support
|
||||
* @param entityClass
|
||||
* Class of the entity asking for audit support
|
||||
* @return true if the entityClass is audited.
|
||||
*/
|
||||
boolean isEntityClassAudited(Class<?> entityClass);
|
||||
|
||||
/**
|
||||
* Checks if the entityName was configured to be audited.
|
||||
*
|
||||
* @param entityClass
|
||||
* EntityName of the entity asking for audit support.
|
||||
* @return true if the entityName is audited.
|
||||
*/
|
||||
boolean isEntityNameAudited(String entityName);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entity
|
||||
* that was obtained previously from the same AuditReader.
|
||||
*
|
||||
* @return the entityName for the given entity, null in case the entity is
|
||||
* not associated with this AuditReader instance.
|
||||
*/
|
||||
String getEntityName(Object primaryKey, Number revision, Object entity)
|
||||
throws HibernateException;
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the entityName was configured to be audited.
|
||||
*
|
||||
* @param entityClass
|
||||
* EntityName of the entity asking for audit support.
|
||||
* @return true if the entityName is audited.
|
||||
*/
|
||||
boolean isEntityNameAudited(String entityName);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entity
|
||||
* that was obtained previously from the same AuditReader.
|
||||
*
|
||||
* @return the entityName for the given entity, null in case the entity is
|
||||
* not associated with this AuditReader instance.
|
||||
*/
|
||||
String getEntityName(Object primaryKey, Number revision, Object entity)
|
||||
throws HibernateException;
|
||||
|
||||
/**
|
||||
* Find all entities changed (added, updated and removed) in a given revision. Executes <i>n+1</i> SQL queries,
|
||||
* where <i>n</i> is a number of different 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 ModifiedEntityTypes} interface.</li>
|
||||
* </ul>
|
||||
*/
|
||||
List<Object> findEntitiesChangedInRevision(Number revision)
|
||||
throws IllegalStateException, IllegalArgumentException, AuditException;
|
||||
|
||||
/**
|
||||
* Find all entities changed (added, updated or removed) in a given revision. Executes <i>n+1</i> SQL queries,
|
||||
* where <i>n</i> is a number of different 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 and filtered by modification type.
|
||||
* @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 ModifiedEntityTypes} interface.</li>
|
||||
* </ul>
|
||||
*/
|
||||
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> SQL queries, where:
|
||||
* <ul>
|
||||
* <li><i>n</i> - number of different 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 ModifiedEntityTypes} interface.</li>
|
||||
* </ul>
|
||||
*/
|
||||
Map<RevisionType, List<Object>> findEntitiesChangedInRevisionGroupByRevisionType(Number revision)
|
||||
throws IllegalStateException, IllegalArgumentException, AuditException;
|
||||
|
||||
/**
|
||||
* Returns set of entity classes modified in a given revision.
|
||||
* @param revision Revision number.
|
||||
* @return Set of classes modified 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 ModifiedEntityTypes} interface.</li>
|
||||
* </ul>
|
||||
*/
|
||||
Set<Class> findEntityTypesChangedInRevision(Number revision)
|
||||
throws IllegalStateException, IllegalArgumentException, AuditException;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package org.hibernate.envers;
|
||||
|
||||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Extension of {@link DefaultRevisionEntity} that allows tracking entity types changed in each revision. This revision
|
||||
* entity is implicitly used when <code>org.hibernate.envers.track_entities_changed_in_revision</code> parameter
|
||||
* is set to <code>true</code>.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public class DefaultTrackingModifiedTypesRevisionEntity extends DefaultRevisionEntity {
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@JoinTable(name = "REVCHANGES", joinColumns = @JoinColumn(name = "REV"))
|
||||
@Column(name = "ENTITYTYPE")
|
||||
@Fetch(FetchMode.JOIN)
|
||||
@ModifiedEntityTypes
|
||||
private Set<String> modifiedEntityTypes = new HashSet<String>();
|
||||
|
||||
public Set<String> getModifiedEntityTypes() {
|
||||
return modifiedEntityTypes;
|
||||
}
|
||||
|
||||
public void setModifiedEntityTypes(Set<String> modifiedEntityTypes) {
|
||||
this.modifiedEntityTypes = modifiedEntityTypes;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof DefaultTrackingModifiedTypesRevisionEntity)) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
|
||||
DefaultTrackingModifiedTypesRevisionEntity that = (DefaultTrackingModifiedTypesRevisionEntity) o;
|
||||
|
||||
if (modifiedEntityTypes != null ? !modifiedEntityTypes.equals(that.modifiedEntityTypes)
|
||||
: that.modifiedEntityTypes != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + (modifiedEntityTypes != null ? modifiedEntityTypes.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "DefaultTrackingModifiedTypesRevisionEntity(" + super.toString() + ", modifiedEntityTypes = " + modifiedEntityTypes + ")";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.hibernate.envers;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 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 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 entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
|
||||
Object revisionEntity);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.hibernate.envers;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a property which holds entity class names that have been modified during each revision.
|
||||
* This annotation expects field of <code>{@literal Set<String>}</code> type.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||
public @interface ModifiedEntityTypes {
|
||||
}
|
|
@ -30,6 +30,7 @@ import org.hibernate.annotations.common.reflection.ReflectionManager;
|
|||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.envers.entities.EntitiesConfigurations;
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
import org.hibernate.envers.revisioninfo.ModifiedEntityTypesReader;
|
||||
import org.hibernate.envers.revisioninfo.RevisionInfoNumberReader;
|
||||
import org.hibernate.envers.revisioninfo.RevisionInfoQueryCreator;
|
||||
import org.hibernate.envers.strategy.AuditStrategy;
|
||||
|
@ -50,6 +51,7 @@ public class AuditConfiguration {
|
|||
private final EntitiesConfigurations entCfg;
|
||||
private final RevisionInfoQueryCreator revisionInfoQueryCreator;
|
||||
private final RevisionInfoNumberReader revisionInfoNumberReader;
|
||||
private final ModifiedEntityTypesReader modifiedEntityTypesReader;
|
||||
|
||||
public AuditEntitiesConfiguration getAuditEntCfg() {
|
||||
return auditEntCfg;
|
||||
|
@ -75,6 +77,10 @@ public class AuditConfiguration {
|
|||
return revisionInfoNumberReader;
|
||||
}
|
||||
|
||||
public ModifiedEntityTypesReader getModifiedEntityTypesReader() {
|
||||
return modifiedEntityTypesReader;
|
||||
}
|
||||
|
||||
public AuditStrategy getAuditStrategy() {
|
||||
return auditStrategy;
|
||||
}
|
||||
|
@ -90,6 +96,7 @@ public class AuditConfiguration {
|
|||
auditProcessManager = new AuditProcessManager(revInfoCfgResult.getRevisionInfoGenerator());
|
||||
revisionInfoQueryCreator = revInfoCfgResult.getRevisionInfoQueryCreator();
|
||||
revisionInfoNumberReader = revInfoCfgResult.getRevisionInfoNumberReader();
|
||||
modifiedEntityTypesReader = revInfoCfgResult.getModifiedEntityTypesReader();
|
||||
auditStrategy = initializeAuditStrategy(revInfoCfgResult.getRevisionInfoClass(),
|
||||
revInfoCfgResult.getRevisionInfoTimestampData());
|
||||
entCfg = new EntitiesConfigurator().configure(cfg, reflectionManager, globalCfg, auditEntCfg, auditStrategy,
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Properties;
|
|||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Nicolas Doroskevich
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class GlobalConfiguration {
|
||||
// Should a revision be generated when a not-owned relation field changes
|
||||
|
@ -45,6 +46,9 @@ public class GlobalConfiguration {
|
|||
// The default name of the catalog of the audit tables.
|
||||
private final String defaultCatalogName;
|
||||
|
||||
// Should Envers track (persist) entity types that have been changed during each revision.
|
||||
private boolean trackEntitiesChangedInRevisionEnabled;
|
||||
|
||||
/*
|
||||
Which operator to use in correlated subqueries (when we want a property to be equal to the result of
|
||||
a correlated subquery, for example: e.p <operator> (select max(e2.p) where e2.p2 = e.p2 ...).
|
||||
|
@ -77,6 +81,12 @@ public class GlobalConfiguration {
|
|||
|
||||
correlatedSubqueryOperator = "org.hibernate.dialect.HSQLDialect".equals(
|
||||
properties.getProperty("hibernate.dialect")) ? "in" : "=";
|
||||
|
||||
String trackEntitiesChangedInRevisionEnabledStr = getProperty(properties,
|
||||
"org.hibernate.envers.track_entities_changed_in_revision",
|
||||
"org.hibernate.envers.track_entities_changed_in_revision",
|
||||
"false");
|
||||
trackEntitiesChangedInRevisionEnabled = Boolean.parseBoolean(trackEntitiesChangedInRevisionEnabledStr);
|
||||
}
|
||||
|
||||
public boolean isGenerateRevisionsForCollections() {
|
||||
|
@ -102,4 +112,12 @@ public class GlobalConfiguration {
|
|||
public String getDefaultCatalogName() {
|
||||
return defaultCatalogName;
|
||||
}
|
||||
|
||||
public boolean isTrackEntitiesChangedInRevisionEnabled() {
|
||||
return trackEntitiesChangedInRevisionEnabled;
|
||||
}
|
||||
|
||||
public void setTrackEntitiesChangedInRevisionEnabled(boolean trackEntitiesChangedInRevisionEnabled) {
|
||||
this.trackEntitiesChangedInRevisionEnabled = trackEntitiesChangedInRevisionEnabled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
package org.hibernate.envers.configuration;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentHelper;
|
||||
|
@ -35,6 +36,8 @@ import org.hibernate.annotations.common.reflection.XProperty;
|
|||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.envers.Audited;
|
||||
import org.hibernate.envers.DefaultRevisionEntity;
|
||||
import org.hibernate.envers.DefaultTrackingModifiedTypesRevisionEntity;
|
||||
import org.hibernate.envers.ModifiedEntityTypes;
|
||||
import org.hibernate.envers.RevisionEntity;
|
||||
import org.hibernate.envers.RevisionListener;
|
||||
import org.hibernate.envers.RevisionNumber;
|
||||
|
@ -43,6 +46,8 @@ import org.hibernate.envers.configuration.metadata.AuditTableData;
|
|||
import org.hibernate.envers.configuration.metadata.MetadataTools;
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
import org.hibernate.envers.revisioninfo.DefaultRevisionInfoGenerator;
|
||||
import org.hibernate.envers.revisioninfo.DefaultTrackingModifiedTypesRevisionInfoGenerator;
|
||||
import org.hibernate.envers.revisioninfo.ModifiedEntityTypesReader;
|
||||
import org.hibernate.envers.revisioninfo.RevisionInfoGenerator;
|
||||
import org.hibernate.envers.revisioninfo.RevisionInfoNumberReader;
|
||||
import org.hibernate.envers.revisioninfo.RevisionInfoQueryCreator;
|
||||
|
@ -53,11 +58,13 @@ import org.hibernate.type.Type;
|
|||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class RevisionInfoConfiguration {
|
||||
private String revisionInfoEntityName;
|
||||
private PropertyData revisionInfoIdData;
|
||||
private PropertyData revisionInfoTimestampData;
|
||||
private PropertyData modifiedEntityTypesData;
|
||||
private Type revisionInfoTimestampType;
|
||||
private GlobalConfiguration globalCfg;
|
||||
|
||||
|
@ -69,6 +76,7 @@ public class RevisionInfoConfiguration {
|
|||
revisionInfoEntityName = "org.hibernate.envers.DefaultRevisionEntity";
|
||||
revisionInfoIdData = new PropertyData("id", "id", "field", null);
|
||||
revisionInfoTimestampData = new PropertyData("timestamp", "timestamp", "field", null);
|
||||
modifiedEntityTypesData = new PropertyData("modifiedEntityTypes", "modifiedEntityTypes", "field", null);
|
||||
revisionInfoTimestampType = new LongType();
|
||||
|
||||
revisionPropType = "integer";
|
||||
|
@ -90,9 +98,41 @@ public class RevisionInfoConfiguration {
|
|||
revisionInfoTimestampType.getName(), true, false);
|
||||
MetadataTools.addColumn(timestampProperty, "REVTSTMP", null, 0, 0, null, null, null, false);
|
||||
|
||||
if (globalCfg.isTrackEntitiesChangedInRevisionEnabled()) {
|
||||
generateEntityTypesTrackingTableMapping(class_mapping, "modifiedEntityTypes", "REVCHANGES", "REV", "ENTITYTYPE", "string");
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates mapping that represents a set of primitive types.<br />
|
||||
* <code>
|
||||
* <set name="propertyName" table="joinTableName" cascade="persist, delete" lazy="false" fetch="join"><br />
|
||||
* <key column="joinTablePrimaryKeyColumnName" /><br />
|
||||
* <element type="joinTableValueColumnType"><br />
|
||||
* <column name="joinTableValueColumnName" /><br />
|
||||
* </element><br />
|
||||
* </set>
|
||||
* </code>
|
||||
*/
|
||||
private void generateEntityTypesTrackingTableMapping(Element class_mapping, String propertyName,
|
||||
String joinTableName, String joinTablePrimaryKeyColumnName,
|
||||
String joinTableValueColumnName, String joinTableValueColumnType) {
|
||||
Element set = class_mapping.addElement("set");
|
||||
set.addAttribute("name", propertyName);
|
||||
set.addAttribute("table", joinTableName);
|
||||
set.addAttribute("cascade", "persist, delete");
|
||||
set.addAttribute("fetch", "join");
|
||||
set.addAttribute("lazy", "false");
|
||||
Element key = set.addElement("key");
|
||||
key.addAttribute("column", joinTablePrimaryKeyColumnName);
|
||||
Element element = set.addElement("element");
|
||||
element.addAttribute("type", joinTableValueColumnType);
|
||||
Element column = element.addElement("column");
|
||||
column.addAttribute("name", joinTableValueColumnName);
|
||||
}
|
||||
|
||||
private Element generateRevisionInfoRelationMapping() {
|
||||
Document document = DocumentHelper.createDocument();
|
||||
Element rev_rel_mapping = document.addElement("key-many-to-one");
|
||||
|
@ -109,10 +149,11 @@ public class RevisionInfoConfiguration {
|
|||
|
||||
private void searchForRevisionInfoCfgInProperties(XClass clazz, ReflectionManager reflectionManager,
|
||||
MutableBoolean revisionNumberFound, MutableBoolean revisionTimestampFound,
|
||||
String accessType) {
|
||||
MutableBoolean modifiedEntityTypesFound, String accessType) {
|
||||
for (XProperty property : clazz.getDeclaredProperties(accessType)) {
|
||||
RevisionNumber revisionNumber = property.getAnnotation(RevisionNumber.class);
|
||||
RevisionTimestamp revisionTimestamp = property.getAnnotation(RevisionTimestamp.class);
|
||||
ModifiedEntityTypes modifiedEntityTypes = property.getAnnotation(ModifiedEntityTypes.class);
|
||||
|
||||
if (revisionNumber != null) {
|
||||
if (revisionNumberFound.isSet()) {
|
||||
|
@ -162,20 +203,35 @@ public class RevisionInfoConfiguration {
|
|||
"long, Long, java.util.Date or java.sql.Date");
|
||||
}
|
||||
}
|
||||
|
||||
if (modifiedEntityTypes != null) {
|
||||
if (modifiedEntityTypesFound.isSet()) {
|
||||
throw new MappingException("Only one property may be annotated with @ModifiedEntityTypes!");
|
||||
}
|
||||
XClass modifiedEntityTypesClass = property.getType();
|
||||
if (reflectionManager.equals(modifiedEntityTypesClass, Set.class) &&
|
||||
reflectionManager.equals(property.getElementClass(), String.class)) {
|
||||
modifiedEntityTypesData = new PropertyData(property.getName(), property.getName(), accessType, null);
|
||||
modifiedEntityTypesFound.set();
|
||||
} else {
|
||||
throw new MappingException("The field annotated with @ModifiedEntityTypes must be of Set<String> type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void searchForRevisionInfoCfg(XClass clazz, ReflectionManager reflectionManager,
|
||||
MutableBoolean revisionNumberFound, MutableBoolean revisionTimestampFound) {
|
||||
MutableBoolean revisionNumberFound, MutableBoolean revisionTimestampFound,
|
||||
MutableBoolean modifiedEntityTypesFound) {
|
||||
XClass superclazz = clazz.getSuperclass();
|
||||
if (!"java.lang.Object".equals(superclazz.getName())) {
|
||||
searchForRevisionInfoCfg(superclazz, reflectionManager, revisionNumberFound, revisionTimestampFound);
|
||||
searchForRevisionInfoCfg(superclazz, reflectionManager, revisionNumberFound, revisionTimestampFound, modifiedEntityTypesFound);
|
||||
}
|
||||
|
||||
searchForRevisionInfoCfgInProperties(clazz, reflectionManager, revisionNumberFound, revisionTimestampFound,
|
||||
"field");
|
||||
modifiedEntityTypesFound, "field");
|
||||
searchForRevisionInfoCfgInProperties(clazz, reflectionManager, revisionNumberFound, revisionTimestampFound,
|
||||
"property");
|
||||
modifiedEntityTypesFound, "property");
|
||||
}
|
||||
|
||||
public RevisionInfoConfigurationResult configure(Configuration cfg, ReflectionManager reflectionManager) {
|
||||
|
@ -209,8 +265,9 @@ public class RevisionInfoConfiguration {
|
|||
|
||||
MutableBoolean revisionNumberFound = new MutableBoolean();
|
||||
MutableBoolean revisionTimestampFound = new MutableBoolean();
|
||||
MutableBoolean modifiedEntityTypesFound = new MutableBoolean();
|
||||
|
||||
searchForRevisionInfoCfg(clazz, reflectionManager, revisionNumberFound, revisionTimestampFound);
|
||||
searchForRevisionInfoCfg(clazz, reflectionManager, revisionNumberFound, revisionTimestampFound, modifiedEntityTypesFound);
|
||||
|
||||
if (!revisionNumberFound.isSet()) {
|
||||
throw new MappingException("An entity annotated with @RevisionEntity must have a field annotated " +
|
||||
|
@ -226,8 +283,19 @@ public class RevisionInfoConfiguration {
|
|||
|
||||
revisionInfoClass = pc.getMappedClass();
|
||||
revisionInfoTimestampType = pc.getProperty(revisionInfoTimestampData.getName()).getType();
|
||||
revisionInfoGenerator = new DefaultRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,
|
||||
revisionEntity.value(), revisionInfoTimestampData, isTimestampAsDate());
|
||||
if (globalCfg.isTrackEntitiesChangedInRevisionEnabled() ||
|
||||
DefaultTrackingModifiedTypesRevisionEntity.class.isAssignableFrom(revisionInfoClass) ||
|
||||
modifiedEntityTypesFound.isSet()) {
|
||||
// If tracking modified entities parameter is enabled, custom revision info entity is a subtype
|
||||
// of DefaultTrackingModifiedTypesRevisionEntity class, or @ModifiedEntityTypes annotation is used.
|
||||
revisionInfoGenerator = new DefaultTrackingModifiedTypesRevisionInfoGenerator(revisionInfoEntityName,
|
||||
revisionInfoClass, revisionEntity.value(), revisionInfoTimestampData, isTimestampAsDate(),
|
||||
modifiedEntityTypesData);
|
||||
globalCfg.setTrackEntitiesChangedInRevisionEnabled(true);
|
||||
} else {
|
||||
revisionInfoGenerator = new DefaultRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,
|
||||
revisionEntity.value(), revisionInfoTimestampData, isTimestampAsDate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,9 +303,16 @@ public class RevisionInfoConfiguration {
|
|||
Document revisionInfoXmlMapping = null;
|
||||
|
||||
if (revisionInfoGenerator == null) {
|
||||
revisionInfoClass = DefaultRevisionEntity.class;
|
||||
revisionInfoGenerator = new DefaultRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,
|
||||
RevisionListener.class, revisionInfoTimestampData, isTimestampAsDate());
|
||||
if (globalCfg.isTrackEntitiesChangedInRevisionEnabled()) {
|
||||
revisionInfoClass = DefaultTrackingModifiedTypesRevisionEntity.class;
|
||||
revisionInfoEntityName = DefaultTrackingModifiedTypesRevisionEntity.class.getName();
|
||||
revisionInfoGenerator = new DefaultTrackingModifiedTypesRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,
|
||||
RevisionListener.class, revisionInfoTimestampData, isTimestampAsDate(), modifiedEntityTypesData);
|
||||
} else {
|
||||
revisionInfoClass = DefaultRevisionEntity.class;
|
||||
revisionInfoGenerator = new DefaultRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,
|
||||
RevisionListener.class, revisionInfoTimestampData, isTimestampAsDate());
|
||||
}
|
||||
revisionInfoXmlMapping = generateDefaultRevisionInfoXmlMapping();
|
||||
}
|
||||
|
||||
|
@ -246,8 +321,10 @@ public class RevisionInfoConfiguration {
|
|||
new RevisionInfoQueryCreator(revisionInfoEntityName, revisionInfoIdData.getName(),
|
||||
revisionInfoTimestampData.getName(), isTimestampAsDate()),
|
||||
generateRevisionInfoRelationMapping(),
|
||||
new RevisionInfoNumberReader(revisionInfoClass, revisionInfoIdData), revisionInfoEntityName,
|
||||
revisionInfoClass, revisionInfoTimestampData);
|
||||
new RevisionInfoNumberReader(revisionInfoClass, revisionInfoIdData),
|
||||
globalCfg.isTrackEntitiesChangedInRevisionEnabled() ? new ModifiedEntityTypesReader(revisionInfoClass, modifiedEntityTypesData)
|
||||
: null,
|
||||
revisionInfoEntityName, revisionInfoClass, revisionInfoTimestampData);
|
||||
}
|
||||
|
||||
private boolean isTimestampAsDate() {
|
||||
|
@ -262,20 +339,22 @@ class RevisionInfoConfigurationResult {
|
|||
private final RevisionInfoQueryCreator revisionInfoQueryCreator;
|
||||
private final Element revisionInfoRelationMapping;
|
||||
private final RevisionInfoNumberReader revisionInfoNumberReader;
|
||||
private final ModifiedEntityTypesReader modifiedEntityTypesReader;
|
||||
private final String revisionInfoEntityName;
|
||||
private final Class<?> revisionInfoClass;
|
||||
private final PropertyData revisionInfoTimestampData;
|
||||
|
||||
RevisionInfoConfigurationResult(RevisionInfoGenerator revisionInfoGenerator,
|
||||
Document revisionInfoXmlMapping, RevisionInfoQueryCreator revisionInfoQueryCreator,
|
||||
Element revisionInfoRelationMapping,
|
||||
RevisionInfoNumberReader revisionInfoNumberReader, String revisionInfoEntityName, Class<?> revisionInfoClass,
|
||||
PropertyData revisionInfoTimestampData) {
|
||||
Element revisionInfoRelationMapping, RevisionInfoNumberReader revisionInfoNumberReader,
|
||||
ModifiedEntityTypesReader modifiedEntityTypesReader, String revisionInfoEntityName,
|
||||
Class<?> revisionInfoClass, PropertyData revisionInfoTimestampData) {
|
||||
this.revisionInfoGenerator = revisionInfoGenerator;
|
||||
this.revisionInfoXmlMapping = revisionInfoXmlMapping;
|
||||
this.revisionInfoQueryCreator = revisionInfoQueryCreator;
|
||||
this.revisionInfoRelationMapping = revisionInfoRelationMapping;
|
||||
this.revisionInfoNumberReader = revisionInfoNumberReader;
|
||||
this.modifiedEntityTypesReader = modifiedEntityTypesReader;
|
||||
this.revisionInfoEntityName = revisionInfoEntityName;
|
||||
this.revisionInfoClass = revisionInfoClass;
|
||||
this.revisionInfoTimestampData = revisionInfoTimestampData;
|
||||
|
@ -313,4 +392,7 @@ class RevisionInfoConfigurationResult {
|
|||
return revisionInfoTimestampData;
|
||||
}
|
||||
|
||||
public ModifiedEntityTypesReader getModifiedEntityTypesReader() {
|
||||
return modifiedEntityTypesReader;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ package org.hibernate.envers.query;
|
|||
import static org.hibernate.envers.tools.ArgumentsTools.checkNotNull;
|
||||
import static org.hibernate.envers.tools.ArgumentsTools.checkPositive;
|
||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||
import org.hibernate.envers.query.impl.EntitiesModifiedAtRevisionQuery;
|
||||
import org.hibernate.envers.query.impl.EntitiesAtRevisionQuery;
|
||||
import org.hibernate.envers.query.impl.RevisionsOfEntityQuery;
|
||||
import org.hibernate.envers.reader.AuditReaderImplementor;
|
||||
|
@ -32,6 +33,7 @@ import org.hibernate.envers.reader.AuditReaderImplementor;
|
|||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Hern<EFBFBD>n Chanfreau
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class AuditQueryCreator {
|
||||
private final AuditConfiguration auditCfg;
|
||||
|
@ -71,7 +73,38 @@ public class AuditQueryCreator {
|
|||
checkNotNull(revision, "Entity revision");
|
||||
checkPositive(revision, "Entity revision");
|
||||
return new EntitiesAtRevisionQuery(auditCfg, auditReaderImplementor, c, entityName, revision);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 entityName Name of the entity (if can't be guessed basing on the {@code c}).
|
||||
* @param revision Revision number at which to execute the query.
|
||||
* @return A query for entities changed at a given revision, to which conditions can be added and which
|
||||
* can then be executed.
|
||||
* @see #forEntitiesAtRevision(Class, String, Number)
|
||||
*/
|
||||
public AuditQuery forEntitiesModifiedAtRevision(Class<?> c, String entityName, Number revision) {
|
||||
checkNotNull(revision, "Entity revision");
|
||||
checkPositive(revision, "Entity revision");
|
||||
return new EntitiesModifiedAtRevisionQuery(auditCfg, auditReaderImplementor, c, entityName, revision);
|
||||
}
|
||||
|
||||
/**
|
||||
* In comparison to {@link #forEntitiesAtRevision(Class, 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.
|
||||
* @return A query for entities changed at a given revision, to which conditions can be added and which
|
||||
* can then be executed.
|
||||
* @see #forEntitiesAtRevision(Class, Number)
|
||||
*/
|
||||
public AuditQuery forEntitiesModifiedAtRevision(Class<?> c, Number revision) {
|
||||
checkNotNull(revision, "Entity revision");
|
||||
checkPositive(revision, "Entity revision");
|
||||
return new EntitiesModifiedAtRevisionQuery(auditCfg, auditReaderImplementor, c, revision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query, which selects the revisions, at which the given entity was modified.
|
||||
|
@ -121,5 +154,4 @@ public class AuditQueryCreator {
|
|||
public AuditQuery forRevisionsOfEntity(Class<?> c, String entityName, boolean selectEntitiesOnly, boolean selectDeletedEntities) {
|
||||
return new RevisionsOfEntityQuery(auditCfg, auditReaderImplementor, c, entityName, selectEntitiesOnly,selectDeletedEntities);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package org.hibernate.envers.query.impl;
|
||||
|
||||
import org.hibernate.Query;
|
||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
|
||||
import org.hibernate.envers.query.criteria.AuditCriterion;
|
||||
import org.hibernate.envers.reader.AuditReaderImplementor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public class EntitiesModifiedAtRevisionQuery extends AbstractAuditQuery {
|
||||
private final Number revision;
|
||||
|
||||
public EntitiesModifiedAtRevisionQuery(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
|
||||
Class<?> cls, Number revision) {
|
||||
super(verCfg, versionsReader, cls);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
public EntitiesModifiedAtRevisionQuery(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
|
||||
Class<?> cls, String entityName, Number revision) {
|
||||
super(verCfg, versionsReader, cls, entityName);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public List list() {
|
||||
/*
|
||||
* The query that we need to create:
|
||||
* SELECT new list(e) FROM versionsReferencedEntity e
|
||||
* WHERE
|
||||
* (all specified conditions, transformed, on the "e" entity) AND
|
||||
* e.revision = :revision
|
||||
*/
|
||||
AuditEntitiesConfiguration verEntCfg = verCfg.getAuditEntCfg();
|
||||
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
|
||||
qb.getRootParameters().addWhereWithParam(revisionPropertyPath, "=", revision);
|
||||
|
||||
// all specified conditions
|
||||
for (AuditCriterion criterion : criterions) {
|
||||
criterion.addToQuery(verCfg, entityName, qb, qb.getRootParameters());
|
||||
}
|
||||
|
||||
Query query = buildQuery();
|
||||
List queryResult = query.list();
|
||||
|
||||
if (hasProjection) {
|
||||
return queryResult;
|
||||
} else {
|
||||
List result = new ArrayList();
|
||||
entityInstantiator.addInstancesFromVersionsEntities(entityName, result, queryResult, revision);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,8 @@
|
|||
package org.hibernate.envers.reader;
|
||||
import static org.hibernate.envers.tools.ArgumentsTools.checkNotNull;
|
||||
import static org.hibernate.envers.tools.ArgumentsTools.checkPositive;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -31,17 +33,19 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.persistence.NoResultException;
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.NonUniqueResultException;
|
||||
import org.hibernate.Query;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.RevisionType;
|
||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.exception.NotAuditedException;
|
||||
import org.hibernate.envers.exception.RevisionDoesNotExistException;
|
||||
import org.hibernate.envers.query.AuditEntity;
|
||||
import org.hibernate.envers.query.AuditQueryCreator;
|
||||
import org.hibernate.envers.query.criteria.RevisionTypeAuditExpression;
|
||||
import org.hibernate.envers.synchronization.AuditProcess;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
|
@ -49,6 +53,7 @@ import org.hibernate.proxy.HibernateProxy;
|
|||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Hernán Chanfreau
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class AuditReaderImpl implements AuditReaderImplementor {
|
||||
private final AuditConfiguration verCfg;
|
||||
|
@ -152,7 +157,7 @@ public class AuditReaderImpl implements AuditReaderImplementor {
|
|||
checkPositive(revision, "Entity revision");
|
||||
checkSession();
|
||||
|
||||
Query query = verCfg.getRevisionInfoQueryCreator().getRevisionDateQuery(session, revision);
|
||||
Criteria query = verCfg.getRevisionInfoQueryCreator().getRevisionDateQuery(session, revision);
|
||||
|
||||
try {
|
||||
Object timestampObject = query.uniqueResult();
|
||||
|
@ -171,7 +176,7 @@ public class AuditReaderImpl implements AuditReaderImplementor {
|
|||
checkNotNull(date, "Date of revision");
|
||||
checkSession();
|
||||
|
||||
Query query = verCfg.getRevisionInfoQueryCreator().getRevisionNumberForDateQuery(session, date);
|
||||
Criteria query = verCfg.getRevisionInfoQueryCreator().getRevisionNumberForDateQuery(session, date);
|
||||
|
||||
try {
|
||||
Number res = (Number) query.uniqueResult();
|
||||
|
@ -194,7 +199,7 @@ public class AuditReaderImpl implements AuditReaderImplementor {
|
|||
|
||||
Set<Number> revisions = new HashSet<Number>(1);
|
||||
revisions.add(revision);
|
||||
Query query = verCfg.getRevisionInfoQueryCreator().getRevisionsQuery(session, revisions);
|
||||
Criteria query = verCfg.getRevisionInfoQueryCreator().getRevisionsQuery(session, revisions);
|
||||
|
||||
try {
|
||||
T revisionData = (T) query.uniqueResult();
|
||||
|
@ -220,7 +225,7 @@ public class AuditReaderImpl implements AuditReaderImplementor {
|
|||
}
|
||||
checkSession();
|
||||
|
||||
Query query = verCfg.getRevisionInfoQueryCreator().getRevisionsQuery(session, revisions);
|
||||
Criteria query = verCfg.getRevisionInfoQueryCreator().getRevisionsQuery(session, revisions);
|
||||
|
||||
try {
|
||||
List<T> revisionList = query.list();
|
||||
|
@ -235,6 +240,67 @@ public class AuditReaderImpl implements AuditReaderImplementor {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public List<Object> findEntitiesChangedInRevision(Number revision) throws IllegalStateException,
|
||||
IllegalArgumentException, AuditException {
|
||||
Set<Class> clazz = findEntityTypesChangedInRevision(revision);
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
for (Class c : clazz) {
|
||||
result.addAll(createQuery().forEntitiesModifiedAtRevision(c, revision).getResultList());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public List<Object> findEntitiesChangedInRevision(Number revision, RevisionType revisionType)
|
||||
throws IllegalStateException, IllegalArgumentException, AuditException {
|
||||
Set<Class> clazz = findEntityTypesChangedInRevision(revision);
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
for (Class c : clazz) {
|
||||
result.addAll(createQuery().forEntitiesModifiedAtRevision(c, revision)
|
||||
.add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public Map<RevisionType, List<Object>> findEntitiesChangedInRevisionGroupByRevisionType(Number revision)
|
||||
throws IllegalStateException, IllegalArgumentException, AuditException {
|
||||
Set<Class> clazz = findEntityTypesChangedInRevision(revision);
|
||||
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<Object> list = createQuery().forEntitiesModifiedAtRevision(c, revision)
|
||||
.add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList();
|
||||
result.get(revisionType).addAll(list);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public Set<Class> findEntityTypesChangedInRevision(Number revision) throws IllegalStateException,
|
||||
IllegalArgumentException, AuditException {
|
||||
checkNotNull(revision, "Entity revision");
|
||||
checkPositive(revision, "Entity revision");
|
||||
checkSession();
|
||||
if (!verCfg.getGlobalCfg().isTrackEntitiesChangedInRevisionEnabled()) {
|
||||
throw new AuditException("This query is designed for Envers default mechanism of tracking entities modified in a given revision."
|
||||
+ " Extend DefaultTrackingModifiedTypesRevisionEntity, utilize @ModifiedEntityTypes annotation or set "
|
||||
+ "'org.hibernate.envers.track_entities_changed_in_revision' parameter to true.");
|
||||
}
|
||||
Set<Number> revisions = new HashSet<Number>(1);
|
||||
revisions.add(revision);
|
||||
Criteria query = verCfg.getRevisionInfoQueryCreator().getRevisionsQuery(session, revisions);
|
||||
Object revisionInfo = query.uniqueResult();
|
||||
if (revisionInfo != null) {
|
||||
// If revision exists
|
||||
return verCfg.getModifiedEntityTypesReader().getModifiedEntityTypes(revisionInfo);
|
||||
}
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <T> T getCurrentRevision(Class<T> revisionEntityClass, boolean persist) {
|
||||
if (!(session instanceof EventSource)) {
|
||||
|
|
|
@ -22,16 +22,22 @@
|
|||
* 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)
|
||||
*/
|
||||
public class DefaultRevisionInfoGenerator implements RevisionInfoGenerator {
|
||||
private final String revisionInfoEntityName;
|
||||
|
@ -86,4 +92,12 @@ public class DefaultRevisionInfoGenerator implements RevisionInfoGenerator {
|
|||
|
||||
return revisionInfo;
|
||||
}
|
||||
|
||||
public void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
|
||||
Object revisionInfo) {
|
||||
if (listener instanceof EntityTrackingRevisionListener) {
|
||||
((EntityTrackingRevisionListener) listener).entityChanged(entityClass, entityName, entityId, revisionType,
|
||||
revisionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package org.hibernate.envers.revisioninfo;
|
||||
|
||||
import org.hibernate.envers.DefaultTrackingModifiedTypesRevisionEntity;
|
||||
import org.hibernate.envers.ModifiedEntityTypes;
|
||||
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 entity class names, that have been changed during current revision, to revision entity.
|
||||
* @see ModifiedEntityTypes
|
||||
* @see DefaultTrackingModifiedTypesRevisionEntity
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class DefaultTrackingModifiedTypesRevisionInfoGenerator extends DefaultRevisionInfoGenerator {
|
||||
private final Setter modifiedEntityTypesSetter;
|
||||
private final Getter modifiedEntityTypesGetter;
|
||||
|
||||
public DefaultTrackingModifiedTypesRevisionInfoGenerator(String revisionInfoEntityName, Class<?> revisionInfoClass,
|
||||
Class<? extends RevisionListener> listenerClass,
|
||||
PropertyData revisionInfoTimestampData, boolean timestampAsDate,
|
||||
PropertyData modifiedEntityTypesData) {
|
||||
super(revisionInfoEntityName, revisionInfoClass, listenerClass, revisionInfoTimestampData, timestampAsDate);
|
||||
modifiedEntityTypesSetter = ReflectionTools.getSetter(revisionInfoClass, modifiedEntityTypesData);
|
||||
modifiedEntityTypesGetter = ReflectionTools.getGetter(revisionInfoClass, modifiedEntityTypesData);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
|
||||
Object revisionEntity) {
|
||||
super.entityChanged(entityClass, entityName, entityId, revisionType, revisionEntity);
|
||||
Set<String> modifiedEntityTypes = (Set<String>) modifiedEntityTypesGetter.get(revisionEntity);
|
||||
if (modifiedEntityTypes == null) {
|
||||
modifiedEntityTypes = new HashSet<String>();
|
||||
modifiedEntityTypesSetter.set(revisionEntity, modifiedEntityTypes, null);
|
||||
}
|
||||
modifiedEntityTypes.add(entityClass.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.hibernate.envers.revisioninfo;
|
||||
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
import org.hibernate.envers.tools.reflection.ReflectionTools;
|
||||
import org.hibernate.property.Getter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Returns modified entity types from a persisted revision info entity.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class ModifiedEntityTypesReader {
|
||||
private final Getter modifiedEntityTypesGetter;
|
||||
|
||||
public ModifiedEntityTypesReader(Class<?> revisionInfoClass, PropertyData modifiedEntityTypesData) {
|
||||
modifiedEntityTypesGetter = ReflectionTools.getGetter(revisionInfoClass, modifiedEntityTypesData);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public Set<Class> getModifiedEntityTypes(Object revisionEntity) {
|
||||
// The default mechanism of tracking entity types that have been changed during each revision stores
|
||||
// fully qualified Java class names.
|
||||
Set<String> modifiedEntityClassNames = (Set<String>) modifiedEntityTypesGetter.get(revisionEntity);
|
||||
if (modifiedEntityClassNames != null) {
|
||||
Set<Class> result = new HashSet<Class>(modifiedEntityClassNames.size());
|
||||
for (String entityClassName : modifiedEntityClassNames) {
|
||||
try {
|
||||
result.add(Thread.currentThread().getContextClassLoader().loadClass(entityClassName));
|
||||
} catch (ClassNotFoundException e) {
|
||||
// This shall never happen
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
@ -30,4 +35,10 @@ import org.hibernate.Session;
|
|||
public interface RevisionInfoGenerator {
|
||||
void saveRevisionData(Session session, Object revisionData);
|
||||
Object generate();
|
||||
|
||||
/**
|
||||
* @see EntityTrackingRevisionListener#entityChanged(Class, String, Serializable, RevisionType, Object)
|
||||
*/
|
||||
void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
|
||||
Object revisionEntity);
|
||||
}
|
||||
|
|
|
@ -22,52 +22,44 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.envers.revisioninfo;
|
||||
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.criterion.Projections;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import org.hibernate.Query;
|
||||
import org.hibernate.Session;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class RevisionInfoQueryCreator {
|
||||
private final String revisionDateQuery;
|
||||
private final String revisionNumberForDateQuery;
|
||||
private final String revisionsQuery;
|
||||
private final String revisionInfoEntityName;
|
||||
private final String revisionInfoIdName;
|
||||
private final String revisionInfoTimestampName;
|
||||
private final boolean timestampAsDate;
|
||||
|
||||
public RevisionInfoQueryCreator(String revisionInfoEntityName, String revisionInfoIdName,
|
||||
String revisionInfoTimestampName, boolean timestampAsDate) {
|
||||
this.revisionInfoEntityName = revisionInfoEntityName;
|
||||
this.revisionInfoIdName = revisionInfoIdName;
|
||||
this.revisionInfoTimestampName = revisionInfoTimestampName;
|
||||
this.timestampAsDate = timestampAsDate;
|
||||
|
||||
revisionDateQuery = new StringBuilder()
|
||||
.append("select rev.").append(revisionInfoTimestampName)
|
||||
.append(" from ").append(revisionInfoEntityName)
|
||||
.append(" rev where ").append(revisionInfoIdName).append(" = :_revision_number")
|
||||
.toString();
|
||||
|
||||
revisionNumberForDateQuery = new StringBuilder()
|
||||
.append("select max(rev.").append(revisionInfoIdName)
|
||||
.append(") from ").append(revisionInfoEntityName)
|
||||
.append(" rev where ").append(revisionInfoTimestampName).append(" <= :_revision_date")
|
||||
.toString();
|
||||
|
||||
revisionsQuery = new StringBuilder()
|
||||
.append("select rev from ").append(revisionInfoEntityName)
|
||||
.append(" rev where ").append(revisionInfoIdName)
|
||||
.append(" in (:_revision_numbers)")
|
||||
.toString();
|
||||
}
|
||||
|
||||
public Query getRevisionDateQuery(Session session, Number revision) {
|
||||
return session.createQuery(revisionDateQuery).setParameter("_revision_number", revision);
|
||||
public Criteria getRevisionDateQuery(Session session, Number revision) {
|
||||
return session.createCriteria(revisionInfoEntityName).setProjection(Projections.property(revisionInfoTimestampName))
|
||||
.add(Restrictions.eq(revisionInfoIdName, revision));
|
||||
}
|
||||
|
||||
public Query getRevisionNumberForDateQuery(Session session, Date date) {
|
||||
return session.createQuery(revisionNumberForDateQuery).setParameter("_revision_date", timestampAsDate ? date : date.getTime());
|
||||
public Criteria getRevisionNumberForDateQuery(Session session, Date date) {
|
||||
return session.createCriteria(revisionInfoEntityName).setProjection(Projections.max(revisionInfoIdName))
|
||||
.add(Restrictions.le(revisionInfoTimestampName, timestampAsDate ? date : date.getTime()));
|
||||
}
|
||||
|
||||
public Query getRevisionsQuery(Session session, Set<Number> revisions) {
|
||||
return session.createQuery(revisionsQuery).setParameterList("_revision_numbers", revisions);
|
||||
public Criteria getRevisionsQuery(Session session, Set<Number> revisions) {
|
||||
return session.createCriteria(revisionInfoEntityName).add(Restrictions.in(revisionInfoIdName, revisions));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.envers.synchronization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
@ -44,6 +45,7 @@ public class AuditProcess implements BeforeTransactionCompletionProcess {
|
|||
private final LinkedList<AuditWorkUnit> workUnits;
|
||||
private final Queue<AuditWorkUnit> undoQueue;
|
||||
private final Map<Pair<String, Object>, AuditWorkUnit> usedIds;
|
||||
private final EntityChangeNotifier entityChangeNotifier;
|
||||
|
||||
private Object revisionData;
|
||||
|
||||
|
@ -54,6 +56,7 @@ public class AuditProcess implements BeforeTransactionCompletionProcess {
|
|||
workUnits = new LinkedList<AuditWorkUnit>();
|
||||
undoQueue = new LinkedList<AuditWorkUnit>();
|
||||
usedIds = new HashMap<Pair<String, Object>, AuditWorkUnit>();
|
||||
entityChangeNotifier = new EntityChangeNotifier(revisionInfoGenerator, session);
|
||||
}
|
||||
|
||||
private void removeWorkUnit(AuditWorkUnit vwu) {
|
||||
|
@ -98,7 +101,7 @@ public class AuditProcess implements BeforeTransactionCompletionProcess {
|
|||
|
||||
private void executeInSession(Session session) {
|
||||
// Making sure the revision data is persisted.
|
||||
getCurrentRevisionData(session, true);
|
||||
Object currentRevisionData = getCurrentRevisionData(session, true);
|
||||
|
||||
AuditWorkUnit vwu;
|
||||
|
||||
|
@ -109,6 +112,7 @@ public class AuditProcess implements BeforeTransactionCompletionProcess {
|
|||
|
||||
while ((vwu = workUnits.poll()) != null) {
|
||||
vwu.perform(session, revisionData);
|
||||
entityChangeNotifier.entityChanged(session, currentRevisionData, vwu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package org.hibernate.envers.synchronization;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.envers.RevisionType;
|
||||
import org.hibernate.envers.revisioninfo.RevisionInfoGenerator;
|
||||
import org.hibernate.envers.synchronization.work.AuditWorkUnit;
|
||||
import org.hibernate.envers.synchronization.work.PersistentCollectionChangeWorkUnit;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Notifies {@link RevisionInfoGenerator} about changes made in the current revision.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class EntityChangeNotifier {
|
||||
private final RevisionInfoGenerator revisionInfoGenerator;
|
||||
private final SessionImplementor sessionImplementor;
|
||||
|
||||
public EntityChangeNotifier(RevisionInfoGenerator revisionInfoGenerator, SessionImplementor sessionImplementor) {
|
||||
this.revisionInfoGenerator = revisionInfoGenerator;
|
||||
this.sessionImplementor = sessionImplementor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies {@link RevisionInfoGenerator} about changes made in the current revision. Provides information
|
||||
* about modified entity class, entity name and its id, as well as {@link RevisionType} and revision log entity.
|
||||
* @param session Active session.
|
||||
* @param currentRevisionData Revision log entity.
|
||||
* @param vwu Performed work unit.
|
||||
*/
|
||||
public void entityChanged(Session session, Object currentRevisionData, AuditWorkUnit vwu) {
|
||||
Serializable entityId = vwu.getEntityId();
|
||||
if (entityId instanceof PersistentCollectionChangeWorkUnit.PersistentCollectionChangeWorkUnitId) {
|
||||
// Notify about a change in collection owner entity.
|
||||
entityId = ((PersistentCollectionChangeWorkUnit.PersistentCollectionChangeWorkUnitId) entityId).getOwnerId();
|
||||
}
|
||||
Class entityClass = getEntityClass(session, vwu.getEntityName());
|
||||
revisionInfoGenerator.entityChanged(entityClass, vwu.getEntityName(), entityId, vwu.getRevisionType(),
|
||||
currentRevisionData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Java class mapped to specified entity name.
|
||||
*/
|
||||
private Class getEntityClass(Session session, String entityName) {
|
||||
EntityPersister entityPersister = sessionImplementor.getFactory().getEntityPersister(entityName);
|
||||
return entityPersister.getClassMetadata().getMappedClass(session.getEntityMode());
|
||||
}
|
||||
}
|
|
@ -22,6 +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;
|
||||
|
@ -35,6 +36,7 @@ import org.hibernate.envers.strategy.AuditStrategy;
|
|||
/**
|
||||
* @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 +44,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 +77,7 @@ public abstract class AbstractAuditWorkUnit implements AuditWorkUnit {
|
|||
setPerformed(data);
|
||||
}
|
||||
|
||||
public Object getEntityId() {
|
||||
public Serializable getEntityId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -95,4 +99,8 @@ public abstract class AbstractAuditWorkUnit implements AuditWorkUnit {
|
|||
session.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public RevisionType getRevisionType() {
|
||||
return revisionType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,14 +23,16 @@
|
|||
*/
|
||||
package org.hibernate.envers.synchronization.work;
|
||||
import java.util.Map;
|
||||
import java.io.Serializable;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.envers.RevisionType;
|
||||
|
||||
/**
|
||||
* 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 +54,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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -39,7 +39,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 +51,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;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,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()), RevisionType.MOD);
|
||||
|
||||
this.referencingPropertyName = referencingPropertyName;
|
||||
|
||||
|
@ -59,7 +59,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, RevisionType.MOD);
|
||||
|
||||
this.collectionChanges = collectionChanges;
|
||||
this.referencingPropertyName = referencingPropertyName;
|
||||
|
@ -171,7 +171,7 @@ public class PersistentCollectionChangeWorkUnit extends AbstractAuditWorkUnit im
|
|||
* the entity plus the name of the field (the role). This is needed because such collections aren't entities
|
||||
* in the "normal" mapping, but they are entities for Envers.
|
||||
*/
|
||||
private static class PersistentCollectionChangeWorkUnitId implements Serializable {
|
||||
public static class PersistentCollectionChangeWorkUnitId implements Serializable {
|
||||
private static final long serialVersionUID = -8007831518629167537L;
|
||||
|
||||
private final Serializable ownerId;
|
||||
|
@ -202,5 +202,9 @@ public class PersistentCollectionChangeWorkUnit extends AbstractAuditWorkUnit im
|
|||
result = 31 * result + (role != null ? role.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Serializable getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package org.hibernate.envers.test.entities.reventity.trackmodifiedentities;
|
||||
|
||||
import org.hibernate.envers.ModifiedEntityTypes;
|
||||
import org.hibernate.envers.RevisionEntity;
|
||||
import org.hibernate.envers.RevisionNumber;
|
||||
import org.hibernate.envers.RevisionTimestamp;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sample revision entity that uses {@link ModifiedEntityTypes} annotation.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
@RevisionEntity
|
||||
public class AnnotatedTrackingRevisionEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
@RevisionNumber
|
||||
private int customId;
|
||||
|
||||
@RevisionTimestamp
|
||||
private long customTimestamp;
|
||||
|
||||
@ElementCollection
|
||||
@JoinTable(name = "REVCHANGES", joinColumns = @JoinColumn(name = "REV"))
|
||||
@Column(name = "ENTITYTYPE")
|
||||
@ModifiedEntityTypes
|
||||
private Set<String> entityTypes;
|
||||
|
||||
public int getCustomId() {
|
||||
return customId;
|
||||
}
|
||||
|
||||
public void setCustomId(int customId) {
|
||||
this.customId = customId;
|
||||
}
|
||||
|
||||
public long getCustomTimestamp() {
|
||||
return customTimestamp;
|
||||
}
|
||||
|
||||
public void setCustomTimestamp(long customTimestamp) {
|
||||
this.customTimestamp = customTimestamp;
|
||||
}
|
||||
|
||||
public Set<String> getEntityTypes() {
|
||||
return entityTypes;
|
||||
}
|
||||
|
||||
public void setEntityTypes(Set<String> entityTypes) {
|
||||
this.entityTypes = entityTypes;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof AnnotatedTrackingRevisionEntity)) return false;
|
||||
|
||||
AnnotatedTrackingRevisionEntity that = (AnnotatedTrackingRevisionEntity) o;
|
||||
|
||||
if (customId != that.customId) return false;
|
||||
if (customTimestamp != that.customTimestamp) return false;
|
||||
if (entityTypes != null ? !entityTypes.equals(that.entityTypes) : that.entityTypes != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = customId;
|
||||
result = 31 * result + (int) (customTimestamp ^ (customTimestamp >>> 32));
|
||||
result = 31 * result + (entityTypes != null ? entityTypes.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AnnotatedTrackingRevisionEntity(customId = " + customId + ", customTimestamp = " + customTimestamp + ", entityTypes=" + entityTypes + ")";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package org.hibernate.envers.test.entities.reventity.trackmodifiedentities;
|
||||
|
||||
import org.hibernate.envers.RevisionEntity;
|
||||
import org.hibernate.envers.RevisionNumber;
|
||||
import org.hibernate.envers.RevisionTimestamp;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Revision entity which {@code modifiedEntityTypes} field is manually populated by {@link CustomTrackingRevisionListener}.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
@RevisionEntity(CustomTrackingRevisionListener.class)
|
||||
public class CustomTrackingRevisionEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
@RevisionNumber
|
||||
private int customId;
|
||||
|
||||
@RevisionTimestamp
|
||||
private long customTimestamp;
|
||||
|
||||
@OneToMany(mappedBy="revision", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
|
||||
private Set<ModifiedEntityTypeEntity> modifiedEntityTypes = new HashSet<ModifiedEntityTypeEntity>();
|
||||
|
||||
public int getCustomId() {
|
||||
return customId;
|
||||
}
|
||||
|
||||
public void setCustomId(int customId) {
|
||||
this.customId = customId;
|
||||
}
|
||||
|
||||
public long getCustomTimestamp() {
|
||||
return customTimestamp;
|
||||
}
|
||||
|
||||
public void setCustomTimestamp(long customTimestamp) {
|
||||
this.customTimestamp = customTimestamp;
|
||||
}
|
||||
|
||||
public Set<ModifiedEntityTypeEntity> getModifiedEntityTypes() {
|
||||
return modifiedEntityTypes;
|
||||
}
|
||||
|
||||
public void setModifiedEntityTypes(Set<ModifiedEntityTypeEntity> modifiedEntityTypes) {
|
||||
this.modifiedEntityTypes = modifiedEntityTypes;
|
||||
}
|
||||
|
||||
public void addModifiedEntityType(String entityClassName) {
|
||||
modifiedEntityTypes.add(new ModifiedEntityTypeEntity(this, entityClassName));
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CustomTrackingRevisionEntity)) return false;
|
||||
|
||||
CustomTrackingRevisionEntity that = (CustomTrackingRevisionEntity) o;
|
||||
|
||||
if (customId != that.customId) return false;
|
||||
if (customTimestamp != that.customTimestamp) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = customId;
|
||||
result = 31 * result + (int) (customTimestamp ^ (customTimestamp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CustomTrackingRevisionEntity(customId = " + customId + ", customTimestamp = " + customTimestamp + ")";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
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 CustomTrackingRevisionListener implements EntityTrackingRevisionListener {
|
||||
@Override
|
||||
public void entityChanged(Class entityClass, String entityName, Serializable entityId, RevisionType revisionType,
|
||||
Object revisionEntity) {
|
||||
((CustomTrackingRevisionEntity)revisionEntity).addModifiedEntityType(entityClass.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newRevision(Object revisionEntity) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.hibernate.envers.test.entities.reventity.trackmodifiedentities;
|
||||
|
||||
import org.hibernate.envers.DefaultRevisionEntity;
|
||||
import org.hibernate.envers.DefaultTrackingModifiedTypesRevisionEntity;
|
||||
import org.hibernate.envers.RevisionEntity;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
@RevisionEntity(ExtendedRevisionListener.class)
|
||||
public class ExtendedRevisionEntity extends DefaultTrackingModifiedTypesRevisionEntity {
|
||||
private String commnent;
|
||||
|
||||
public String getCommnent() {
|
||||
return commnent;
|
||||
}
|
||||
|
||||
public void setCommnent(String commnent) {
|
||||
this.commnent = commnent;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.hibernate.envers.test.entities.reventity.trackmodifiedentities;
|
||||
|
||||
import org.hibernate.envers.RevisionListener;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class ExtendedRevisionListener implements RevisionListener {
|
||||
public static final String COMMENT_VALUE = "Comment";
|
||||
|
||||
public void newRevision(Object revisionEntity) {
|
||||
((ExtendedRevisionEntity)revisionEntity).setCommnent(COMMENT_VALUE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package org.hibernate.envers.test.entities.reventity.trackmodifiedentities;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
/**
|
||||
* Custom detail of revision entity.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
public class ModifiedEntityTypeEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
|
||||
@ManyToOne
|
||||
private CustomTrackingRevisionEntity revision;
|
||||
|
||||
private String entityClassName;
|
||||
|
||||
public ModifiedEntityTypeEntity() {
|
||||
}
|
||||
|
||||
public ModifiedEntityTypeEntity(String entityClassName) {
|
||||
this.entityClassName = entityClassName;
|
||||
}
|
||||
|
||||
public ModifiedEntityTypeEntity(CustomTrackingRevisionEntity revision, String entityClassName) {
|
||||
this.revision = revision;
|
||||
this.entityClassName = entityClassName;
|
||||
}
|
||||
|
||||
public CustomTrackingRevisionEntity getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public void setRevision(CustomTrackingRevisionEntity revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
public String getEntityClassName() {
|
||||
return entityClassName;
|
||||
}
|
||||
|
||||
public void setEntityClassName(String entityClassName) {
|
||||
this.entityClassName = entityClassName;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ModifiedEntityTypeEntity)) return false;
|
||||
|
||||
ModifiedEntityTypeEntity that = (ModifiedEntityTypeEntity) o;
|
||||
|
||||
if (entityClassName != null ? !entityClassName.equals(that.entityClassName) : that.entityClassName != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return entityClassName != null ? entityClassName.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CustomTrackingRevisionEntity(entityClassName = " + entityClassName + ")";
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import org.hibernate.envers.test.AbstractEntityTest;
|
|||
import org.hibernate.envers.test.Priority;
|
||||
import org.hibernate.envers.test.entities.StrIntTestEntity;
|
||||
import org.hibernate.envers.test.tools.TestTools;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
@ -40,6 +41,7 @@ import java.util.List;
|
|||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class SimpleQuery extends AbstractEntityTest {
|
||||
|
@ -267,4 +269,59 @@ public class SimpleQuery extends AbstractEntityTest {
|
|||
|
||||
assert result.size() == 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntitiesAddedAtRevision() {
|
||||
StrIntTestEntity site1 = new StrIntTestEntity("a", 10, id1);
|
||||
StrIntTestEntity site2 = new StrIntTestEntity("a", 10, id2);
|
||||
StrIntTestEntity site3 = new StrIntTestEntity("b", 5, id3);
|
||||
|
||||
List result = getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, StrIntTestEntity.class.getName(), 1).getResultList();
|
||||
RevisionType revisionType = (RevisionType) getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 1)
|
||||
.addProjection(AuditEntity.revisionType()).add(AuditEntity.id().eq(id1))
|
||||
.getSingleResult();
|
||||
|
||||
Assert.assertTrue(TestTools.checkList(result, site1, site2, site3));
|
||||
Assert.assertEquals(revisionType, RevisionType.ADD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntitiesChangedAtRevision() {
|
||||
StrIntTestEntity site1 = new StrIntTestEntity("c", 10, id1);
|
||||
StrIntTestEntity site2 = new StrIntTestEntity("a", 20, id2);
|
||||
|
||||
List result = getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 2).getResultList();
|
||||
RevisionType revisionType = (RevisionType) getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 2)
|
||||
.addProjection(AuditEntity.revisionType()).add(AuditEntity.id().eq(id1))
|
||||
.getSingleResult();
|
||||
|
||||
Assert.assertTrue(TestTools.checkList(result, site1, site2));
|
||||
Assert.assertEquals(revisionType, RevisionType.MOD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntitiesRemovedAtRevision() {
|
||||
StrIntTestEntity site1 = new StrIntTestEntity(null, null, id1);
|
||||
|
||||
List result = getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 4).getResultList();
|
||||
RevisionType revisionType = (RevisionType) getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 4)
|
||||
.addProjection(AuditEntity.revisionType()).add(AuditEntity.id().eq(id1))
|
||||
.getSingleResult();
|
||||
|
||||
Assert.assertTrue(TestTools.checkList(result, site1));
|
||||
Assert.assertEquals(revisionType, RevisionType.DEL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntityNotModifiedAtRevision() {
|
||||
List result = getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 3)
|
||||
.add(AuditEntity.id().eq(id1)).getResultList();
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoEntitiesModifiedAtRevision() {
|
||||
List result = getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 5).getResultList();
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package org.hibernate.envers.test.integration.reventity.trackmodifiedentities;
|
||||
|
||||
import org.hibernate.ejb.Ejb3Configuration;
|
||||
import org.hibernate.envers.ModifiedEntityTypes;
|
||||
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.AnnotatedTrackingRevisionEntity;
|
||||
|
||||
/**
|
||||
* Tests proper behavior of revision entity that utilizes {@link ModifiedEntityTypes} annotation.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class AnnotatedTrackingEntitiesTest extends DefaultTrackingEntitiesTest {
|
||||
@Override
|
||||
public void configure(Ejb3Configuration cfg) {
|
||||
super.configure(cfg);
|
||||
cfg.addAnnotatedClass(AnnotatedTrackingRevisionEntity.class);
|
||||
cfg.setProperty("org.hibernate.envers.track_entities_changed_in_revision", "false");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
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.CustomTrackingRevisionEntity;
|
||||
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.CustomTrackingRevisionListener;
|
||||
import org.hibernate.envers.test.entities.reventity.trackmodifiedentities.ModifiedEntityTypeEntity;
|
||||
import org.hibernate.envers.test.tools.TestTools;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* Tests proper behavior of entity listener that implements {@link EntityTrackingRevisionListener}
|
||||
* interface. {@link CustomTrackingRevisionListener} 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 {
|
||||
private Integer steId = null;
|
||||
private Integer siteId = null;
|
||||
|
||||
@Override
|
||||
public void configure(Ejb3Configuration cfg) {
|
||||
cfg.addAnnotatedClass(ModifiedEntityTypeEntity.class);
|
||||
cfg.addAnnotatedClass(CustomTrackingRevisionEntity.class);
|
||||
cfg.addAnnotatedClass(StrTestEntity.class);
|
||||
cfg.addAnnotatedClass(StrIntTestEntity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
EntityManager em = getEntityManager();
|
||||
|
||||
// Revision 1 - Adding two entities
|
||||
em.getTransaction().begin();
|
||||
StrTestEntity ste = new StrTestEntity("x");
|
||||
StrIntTestEntity site = new StrIntTestEntity("y", 1);
|
||||
em.persist(ste);
|
||||
em.persist(site);
|
||||
steId = ste.getId();
|
||||
siteId = site.getId();
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 2 - Modifying one entity
|
||||
em.getTransaction().begin();
|
||||
site = em.find(StrIntTestEntity.class, siteId);
|
||||
site.setNumber(2);
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 3 - Deleting both entities
|
||||
em.getTransaction().begin();
|
||||
ste = em.find(StrTestEntity.class, steId);
|
||||
site = em.find(StrIntTestEntity.class, siteId);
|
||||
em.remove(ste);
|
||||
em.remove(site);
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackAddedEntities() {
|
||||
ModifiedEntityTypeEntity steDescriptor = new ModifiedEntityTypeEntity(StrTestEntity.class.getName());
|
||||
ModifiedEntityTypeEntity siteDescriptor = new ModifiedEntityTypeEntity(StrIntTestEntity.class.getName());
|
||||
|
||||
AuditReader vr = getAuditReader();
|
||||
CustomTrackingRevisionEntity ctre = vr.findRevision(CustomTrackingRevisionEntity.class, 1);
|
||||
|
||||
assert ctre.getModifiedEntityTypes() != null;
|
||||
assert ctre.getModifiedEntityTypes().size() == 2;
|
||||
assert TestTools.makeSet(steDescriptor, siteDescriptor).equals(ctre.getModifiedEntityTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackModifiedEntities() {
|
||||
ModifiedEntityTypeEntity siteDescriptor = new ModifiedEntityTypeEntity(StrIntTestEntity.class.getName());
|
||||
|
||||
AuditReader vr = getAuditReader();
|
||||
CustomTrackingRevisionEntity ctre = vr.findRevision(CustomTrackingRevisionEntity.class, 2);
|
||||
|
||||
assert ctre.getModifiedEntityTypes() != null;
|
||||
assert ctre.getModifiedEntityTypes().size() == 1;
|
||||
assert TestTools.makeSet(siteDescriptor).equals(ctre.getModifiedEntityTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackDeletedEntities() {
|
||||
ModifiedEntityTypeEntity steDescriptor = new ModifiedEntityTypeEntity(StrTestEntity.class.getName());
|
||||
ModifiedEntityTypeEntity siteDescriptor = new ModifiedEntityTypeEntity(StrIntTestEntity.class.getName());
|
||||
|
||||
AuditReader vr = getAuditReader();
|
||||
CustomTrackingRevisionEntity ctre = vr.findRevision(CustomTrackingRevisionEntity.class, 3);
|
||||
|
||||
assert ctre.getModifiedEntityTypes() != null;
|
||||
assert ctre.getModifiedEntityTypes().size() == 2;
|
||||
assert TestTools.makeSet(steDescriptor, siteDescriptor).equals(ctre.getModifiedEntityTypes());
|
||||
}
|
||||
|
||||
@Test(expected = AuditException.class)
|
||||
public void testFindEntitiesChangedInRevisionException() {
|
||||
getAuditReader().findEntitiesChangedInRevision(1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package org.hibernate.envers.test.integration.reventity.trackmodifiedentities;
|
||||
|
||||
import org.hibernate.ejb.Ejb3Configuration;
|
||||
import org.hibernate.envers.RevisionType;
|
||||
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.tools.TestTools;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.Iterator;
|
||||
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"})
|
||||
public class DefaultTrackingEntitiesTest extends AbstractEntityTest {
|
||||
private Integer steId = null;
|
||||
private Integer siteId = null;
|
||||
|
||||
@Override
|
||||
public void configure(Ejb3Configuration cfg) {
|
||||
cfg.setProperty("org.hibernate.envers.track_entities_changed_in_revision", "true");
|
||||
cfg.addAnnotatedClass(StrTestEntity.class);
|
||||
cfg.addAnnotatedClass(StrIntTestEntity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
EntityManager em = getEntityManager();
|
||||
|
||||
// Revision 1 - Adding two entities
|
||||
em.getTransaction().begin();
|
||||
StrTestEntity ste = new StrTestEntity("x");
|
||||
StrIntTestEntity site = new StrIntTestEntity("y", 1);
|
||||
em.persist(ste);
|
||||
em.persist(site);
|
||||
steId = ste.getId();
|
||||
siteId = site.getId();
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 2 - Modifying one entity
|
||||
em.getTransaction().begin();
|
||||
site = em.find(StrIntTestEntity.class, siteId);
|
||||
site.setNumber(2);
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 3 - Deleting both entities
|
||||
em.getTransaction().begin();
|
||||
ste = em.find(StrTestEntity.class, steId);
|
||||
site = em.find(StrIntTestEntity.class, siteId);
|
||||
em.remove(ste);
|
||||
em.remove(site);
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevEntityTableCreation() {
|
||||
Iterator<Table> tableIterator = getCfg().getTableMappings();
|
||||
while (tableIterator.hasNext()) {
|
||||
Table table = tableIterator.next();
|
||||
if ("REVCHANGES".equals(table.getName())) {
|
||||
assert table.getColumnSpan() == 2;
|
||||
assert table.getColumn(new Column("REV")) != null;
|
||||
assert table.getColumn(new Column("ENTITYTYPE")) != null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert false;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackAddedEntities() {
|
||||
StrTestEntity ste = new StrTestEntity("x", steId);
|
||||
StrIntTestEntity site = new StrIntTestEntity("y", 1, siteId);
|
||||
|
||||
assert TestTools.checkList(getAuditReader().findEntitiesChangedInRevision(1), ste, site);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackModifiedEntities() {
|
||||
StrIntTestEntity site = new StrIntTestEntity("y", 2, siteId);
|
||||
|
||||
assert TestTools.checkList(getAuditReader().findEntitiesChangedInRevision(2), site);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackDeletedEntities() {
|
||||
StrTestEntity ste = new StrTestEntity(null, steId);
|
||||
StrIntTestEntity site = new StrIntTestEntity(null, null, siteId);
|
||||
|
||||
assert TestTools.checkList(getAuditReader().findEntitiesChangedInRevision(3), site, ste);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindChangesInInvalidRevision() {
|
||||
assert getAuditReader().findEntitiesChangedInRevision(4).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackAddedEntitiesGroupByRevisionType() {
|
||||
StrTestEntity ste = new StrTestEntity("x", steId);
|
||||
StrIntTestEntity site = new StrIntTestEntity("y", 1, siteId);
|
||||
|
||||
Map<RevisionType, List<Object>> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(1);
|
||||
assert TestTools.checkList(result.get(RevisionType.ADD), site, ste);
|
||||
assert TestTools.checkList(result.get(RevisionType.MOD));
|
||||
assert TestTools.checkList(result.get(RevisionType.DEL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackModifiedEntitiesGroupByRevisionType() {
|
||||
StrIntTestEntity site = new StrIntTestEntity("y", 2, siteId);
|
||||
|
||||
Map<RevisionType, List<Object>> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(2);
|
||||
assert TestTools.checkList(result.get(RevisionType.ADD));
|
||||
assert TestTools.checkList(result.get(RevisionType.MOD), site);
|
||||
assert TestTools.checkList(result.get(RevisionType.DEL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackDeletedEntitiesGroupByRevisionType() {
|
||||
StrTestEntity ste = new StrTestEntity(null, steId);
|
||||
StrIntTestEntity site = new StrIntTestEntity(null, null, siteId);
|
||||
|
||||
Map<RevisionType, List<Object>> result = getAuditReader().findEntitiesChangedInRevisionGroupByRevisionType(3);
|
||||
assert TestTools.checkList(result.get(RevisionType.ADD));
|
||||
assert TestTools.checkList(result.get(RevisionType.MOD));
|
||||
assert TestTools.checkList(result.get(RevisionType.DEL), site, ste);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindChangedEntitiesByRevisionTypeADD() {
|
||||
StrTestEntity ste = new StrTestEntity("x", steId);
|
||||
StrIntTestEntity site = new StrIntTestEntity("y", 1, siteId);
|
||||
|
||||
assert TestTools.checkList(getAuditReader().findEntitiesChangedInRevision(1, RevisionType.ADD), ste, site);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindChangedEntitiesByRevisionTypeMOD() {
|
||||
StrIntTestEntity site = new StrIntTestEntity("y", 2, siteId);
|
||||
|
||||
assert TestTools.checkList(getAuditReader().findEntitiesChangedInRevision(2, RevisionType.MOD), site);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindChangedEntitiesByRevisionTypeDEL() {
|
||||
StrTestEntity ste = new StrTestEntity(null, steId);
|
||||
StrIntTestEntity site = new StrIntTestEntity(null, null, siteId);
|
||||
|
||||
assert TestTools.checkList(getAuditReader().findEntitiesChangedInRevision(3, RevisionType.DEL), ste, site);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindEntityTypesChangedInRevision() {
|
||||
assert TestTools.makeSet(StrTestEntity.class, StrIntTestEntity.class).equals(getAuditReader().findEntityTypesChangedInRevision(1));
|
||||
assert TestTools.makeSet(StrIntTestEntity.class).equals(getAuditReader().findEntityTypesChangedInRevision(2));
|
||||
assert TestTools.makeSet(StrTestEntity.class, StrIntTestEntity.class).equals(getAuditReader().findEntityTypesChangedInRevision(3));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
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.hibernate.envers.test.tools.TestTools;
|
||||
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 TestTools.makeSet(Car.class, Person.class).equals(getAuditReader().findEntityTypesChangedInRevision(1));
|
||||
assert TestTools.makeSet(Car.class, Person.class).equals(getAuditReader().findEntityTypesChangedInRevision(2));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
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 {
|
||||
@Override
|
||||
public void configure(Ejb3Configuration cfg) {
|
||||
super.configure(cfg);
|
||||
cfg.addAnnotatedClass(ExtendedRevisionEntity.class);
|
||||
cfg.setProperty("org.hibernate.envers.track_entities_changed_in_revision", "false");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentPropertyValue() {
|
||||
AuditReader vr = getAuditReader();
|
||||
ExtendedRevisionEntity ere = vr.findRevision(ExtendedRevisionEntity.class, 1);
|
||||
|
||||
assert ExtendedRevisionListener.COMMENT_VALUE.equals(ere.getCommnent());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
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.StrTestEntity;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class TrackingEntitiesMultipleChangesTest extends AbstractEntityTest {
|
||||
private Integer steId1 = null;
|
||||
private Integer steId2 = null;
|
||||
|
||||
@Override
|
||||
public void configure(Ejb3Configuration cfg) {
|
||||
cfg.setProperty("org.hibernate.envers.track_entities_changed_in_revision", "true");
|
||||
cfg.addAnnotatedClass(StrTestEntity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
EntityManager em = getEntityManager();
|
||||
|
||||
// Revision 1 - Adding two entities
|
||||
em.getTransaction().begin();
|
||||
StrTestEntity ste1 = new StrTestEntity("x");
|
||||
StrTestEntity ste2 = new StrTestEntity("y");
|
||||
em.persist(ste1);
|
||||
em.persist(ste2);
|
||||
steId1 = ste1.getId();
|
||||
steId2 = ste2.getId();
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 2 - Adding first and removing second entity
|
||||
em.getTransaction().begin();
|
||||
ste1 = em.find(StrTestEntity.class, steId1);
|
||||
ste2 = em.find(StrTestEntity.class, steId2);
|
||||
ste1.setStr("z");
|
||||
em.remove(ste2);
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 3 - Modifying and removing the same entity.
|
||||
em.getTransaction().begin();
|
||||
ste1 = em.find(StrTestEntity.class, steId1);
|
||||
ste1.setStr("a");
|
||||
em.merge(ste1);
|
||||
em.remove(ste1);
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackAddedTwoEntities() {
|
||||
StrTestEntity ste1 = new StrTestEntity("x", steId1);
|
||||
StrTestEntity ste2 = new StrTestEntity("y", steId2);
|
||||
|
||||
assert Arrays.asList(ste1, ste2).equals(getAuditReader().findEntitiesChangedInRevision(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackUpdateAndRemoveDifferentEntities() {
|
||||
StrTestEntity ste1 = new StrTestEntity("z", steId1);
|
||||
StrTestEntity ste2 = new StrTestEntity(null, steId2);
|
||||
|
||||
assert Arrays.asList(ste1, ste2).equals(getAuditReader().findEntitiesChangedInRevision(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrackUpdateAndRemoveTheSameEntity() {
|
||||
StrTestEntity ste1 = new StrTestEntity(null, steId1);
|
||||
|
||||
assert Arrays.asList(ste1).equals(getAuditReader().findEntitiesChangedInRevision(3));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue