HHH-5845 - Lazy loading of audited entites with revision type DEL

This commit is contained in:
Lukasz Antoniak 2013-04-17 09:46:21 +02:00
parent 2daa528cd1
commit 76fe91cf4a
29 changed files with 1149 additions and 516 deletions

View File

@ -200,14 +200,22 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
protected abstract Initializor<T> getInitializor(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader, Object primaryKey,
Number revision);
Number revision, boolean removed);
public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
AuditReaderImplementor versionsReader, Number revision) {
Setter setter = ReflectionTools.getSetter(obj.getClass(),
commonCollectionMapperData.getCollectionReferencingPropertyData());
Setter setter = ReflectionTools.getSetter(obj.getClass(), commonCollectionMapperData.getCollectionReferencingPropertyData());
try {
setter.set(obj, proxyConstructor.newInstance(getInitializor(verCfg, versionsReader, primaryKey, revision)), null);
setter.set(
obj,
proxyConstructor.newInstance(
getInitializor(
verCfg, versionsReader, primaryKey, revision,
RevisionType.DEL.equals( data.get( verCfg.getAuditEntCfg().getRevisionTypePropName() ) )
)
),
null
);
} catch (InstantiationException e) {
throw new AuditException(e);
} catch (IllegalAccessException e) {

View File

@ -49,9 +49,9 @@ public class BasicCollectionMapper<T extends Collection> extends AbstractCollect
}
protected Initializor<T> getInitializor(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
Object primaryKey, Number revision) {
Object primaryKey, Number revision, boolean removed) {
return new BasicCollectionInitializor<T>(verCfg, versionsReader, commonCollectionMapperData.getQueryGenerator(),
primaryKey, revision, collectionClass, elementComponentData);
primaryKey, revision, removed, collectionClass, elementComponentData);
}
protected Collection getNewCollectionContent(PersistentCollection newCollection) {

View File

@ -54,9 +54,9 @@ public final class ListCollectionMapper extends AbstractCollectionMapper<List> i
}
protected Initializor<List> getInitializor(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
Object primaryKey, Number revision) {
Object primaryKey, Number revision, boolean removed) {
return new ListCollectionInitializor(verCfg, versionsReader, commonCollectionMapperData.getQueryGenerator(),
primaryKey, revision, elementComponentData, indexComponentData);
primaryKey, revision, removed, elementComponentData, indexComponentData);
}
@SuppressWarnings({"unchecked"})

View File

@ -52,9 +52,9 @@ public class MapCollectionMapper<T extends Map> extends AbstractCollectionMapper
}
protected Initializor<T> getInitializor(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
Object primaryKey, Number revision) {
Object primaryKey, Number revision, boolean removed) {
return new MapCollectionInitializor<T>(verCfg, versionsReader, commonCollectionMapperData.getQueryGenerator(),
primaryKey, revision, collectionClass, elementComponentData, indexComponentData);
primaryKey, revision, removed, collectionClass, elementComponentData, indexComponentData);
}
protected Collection getNewCollectionContent(PersistentCollection newCollection) {

View File

@ -46,9 +46,9 @@ public final class SortedMapCollectionMapper extends MapCollectionMapper<SortedM
}
protected Initializor<SortedMap> getInitializor(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
Object primaryKey, Number revision) {
Object primaryKey, Number revision, boolean removed) {
return new SortedMapCollectionInitializor(verCfg, versionsReader, commonCollectionMapperData.getQueryGenerator(),
primaryKey, revision, collectionClass, elementComponentData, indexComponentData, comparator);
primaryKey, revision, removed, collectionClass, elementComponentData, indexComponentData, comparator);
}
}

View File

@ -46,9 +46,8 @@ public final class SortedSetCollectionMapper extends BasicCollectionMapper<Sorte
}
protected Initializor<SortedSet> getInitializor(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
Object primaryKey, Number revision) {
Object primaryKey, Number revision, boolean removed) {
return new SortedSetCollectionInitializor(verCfg, versionsReader, commonCollectionMapperData.getQueryGenerator(),
primaryKey, revision, collectionClass, elementComponentData, comparator);
primaryKey, revision, removed, collectionClass, elementComponentData, comparator);
}
}

View File

@ -37,18 +37,19 @@ public abstract class AbstractCollectionInitializor<T> implements Initializor<T>
private final AuditReaderImplementor versionsReader;
private final RelationQueryGenerator queryGenerator;
private final Object primaryKey;
protected final Number revision;
protected final boolean removed;
protected final EntityInstantiator entityInstantiator;
public AbstractCollectionInitializor(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader,
RelationQueryGenerator queryGenerator,
Object primaryKey, Number revision) {
Object primaryKey, Number revision, boolean removed) {
this.versionsReader = versionsReader;
this.queryGenerator = queryGenerator;
this.primaryKey = primaryKey;
this.revision = revision;
this.removed = removed;
entityInstantiator = new EntityInstantiator(verCfg, versionsReader);
}
@ -58,7 +59,7 @@ public abstract class AbstractCollectionInitializor<T> implements Initializor<T>
protected abstract void addToCollection(T collection, Object collectionRow);
public T initialize() {
List<?> collectionContent = queryGenerator.getQuery(versionsReader, primaryKey, revision).list();
List<?> collectionContent = queryGenerator.getQuery(versionsReader, primaryKey, revision, removed).list();
T collection = initializeCollection(collectionContent.size());

View File

@ -41,10 +41,10 @@ public class ArrayCollectionInitializor extends AbstractCollectionInitializor<Ob
public ArrayCollectionInitializor(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader,
RelationQueryGenerator queryGenerator,
Object primaryKey, Number revision,
Object primaryKey, Number revision, boolean removed,
MiddleComponentData elementComponentData,
MiddleComponentData indexComponentData) {
super(verCfg, versionsReader, queryGenerator, primaryKey, revision);
super(verCfg, versionsReader, queryGenerator, primaryKey, revision, removed);
this.elementComponentData = elementComponentData;
this.indexComponentData = indexComponentData;

View File

@ -46,10 +46,10 @@ public class BasicCollectionInitializor<T extends Collection> extends AbstractCo
public BasicCollectionInitializor(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader,
RelationQueryGenerator queryGenerator,
Object primaryKey, Number revision,
Object primaryKey, Number revision, boolean removed,
Class<? extends T> collectionClass,
MiddleComponentData elementComponentData) {
super(verCfg, versionsReader, queryGenerator, primaryKey, revision);
super(verCfg, versionsReader, queryGenerator, primaryKey, revision, removed);
this.collectionClass = collectionClass;
this.elementComponentData = elementComponentData;

View File

@ -42,10 +42,10 @@ public class ListCollectionInitializor extends AbstractCollectionInitializor<Lis
public ListCollectionInitializor(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader,
RelationQueryGenerator queryGenerator,
Object primaryKey, Number revision,
Object primaryKey, Number revision, boolean removed,
MiddleComponentData elementComponentData,
MiddleComponentData indexComponentData) {
super(verCfg, versionsReader, queryGenerator, primaryKey, revision);
super(verCfg, versionsReader, queryGenerator, primaryKey, revision, removed);
this.elementComponentData = elementComponentData;
this.indexComponentData = indexComponentData;

View File

@ -46,11 +46,11 @@ public class MapCollectionInitializor<T extends Map> extends AbstractCollectionI
public MapCollectionInitializor(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader,
RelationQueryGenerator queryGenerator,
Object primaryKey, Number revision,
Object primaryKey, Number revision, boolean removed,
Class<? extends T> collectionClass,
MiddleComponentData elementComponentData,
MiddleComponentData indexComponentData) {
super(verCfg, versionsReader, queryGenerator, primaryKey, revision);
super(verCfg, versionsReader, queryGenerator, primaryKey, revision, removed);
this.collectionClass = collectionClass;
this.elementComponentData = elementComponentData;

View File

@ -44,11 +44,11 @@ public class SortedMapCollectionInitializor extends MapCollectionInitializor<Sor
public SortedMapCollectionInitializor(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader,
RelationQueryGenerator queryGenerator,
Object primaryKey, Number revision,
Object primaryKey, Number revision, boolean removed,
Class<? extends SortedMap> collectionClass,
MiddleComponentData elementComponentData,
MiddleComponentData indexComponentData, Comparator comparator) {
super(verCfg, versionsReader, queryGenerator, primaryKey, revision, collectionClass, elementComponentData, indexComponentData);
super(verCfg, versionsReader, queryGenerator, primaryKey, revision, removed, collectionClass, elementComponentData, indexComponentData);
this.comparator = comparator;
}

View File

@ -41,8 +41,11 @@ import org.hibernate.envers.reader.AuditReaderImplementor;
public class SortedSetCollectionInitializor extends BasicCollectionInitializor<SortedSet> {
private final Comparator comparator;
public SortedSetCollectionInitializor(AuditConfiguration verCfg, AuditReaderImplementor versionsReader, RelationQueryGenerator queryGenerator, Object primaryKey, Number revision, Class<? extends SortedSet> collectionClass, MiddleComponentData elementComponentData, Comparator comparator) {
super(verCfg, versionsReader, queryGenerator, primaryKey, revision, collectionClass, elementComponentData);
public SortedSetCollectionInitializor(AuditConfiguration verCfg, AuditReaderImplementor versionsReader,
RelationQueryGenerator queryGenerator, Object primaryKey, Number revision, boolean removed,
Class<? extends SortedSet> collectionClass, MiddleComponentData elementComponentData,
Comparator comparator) {
super(verCfg, versionsReader, queryGenerator, primaryKey, revision, removed, collectionClass, elementComponentData);
this.comparator = comparator;
}

View File

@ -23,12 +23,16 @@
*/
package org.hibernate.envers.entities.mapper.relation.query;
import java.util.Collections;
import java.util.Map;
import org.hibernate.Query;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.tools.query.QueryBuilder;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.REVISION_PARAMETER;
@ -50,12 +54,22 @@ public abstract class AbstractRelationQueryGenerator implements RelationQueryGen
this.revisionTypeInId = revisionTypeInId;
}
/**
* @return Query used to retrieve state of audited entity valid at a given revision.
*/
protected abstract String getQueryString();
public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision) {
Query query = versionsReader.getSession().createQuery( getQueryString() );
query.setParameter( REVISION_PARAMETER, revision );
/**
* @return Query executed to retrieve state of audited entity valid at previous revision
* or removed during exactly specified revision number. Used only when traversing deleted
* entities graph.
*/
protected abstract String getQueryRemovedString();
public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision, boolean removed) {
Query query = versionsReader.getSession().createQuery( removed ? getQueryRemovedString() : getQueryString() );
query.setParameter( DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL );
query.setParameter( REVISION_PARAMETER, revision );
for ( QueryParameterData paramData : referencingIdData.getPrefixedMapper().mapToQueryParametersFromId( primaryKey ) ) {
paramData.setParameterValue( query );
}
@ -67,4 +81,14 @@ public abstract class AbstractRelationQueryGenerator implements RelationQueryGen
? verEntCfg.getOriginalIdPropName() + "." + verEntCfg.getRevisionTypePropName()
: verEntCfg.getRevisionTypePropName();
}
}
protected String queryToString(QueryBuilder query) {
return queryToString( query, Collections.<String, Object>emptyMap() );
}
protected String queryToString(QueryBuilder query, Map<String, Object> queryParamValues) {
final StringBuilder sb = new StringBuilder();
query.build( sb, queryParamValues );
return sb.toString();
}
}

View File

@ -23,18 +23,12 @@
*/
package org.hibernate.envers.entities.mapper.relation.query;
import java.util.Collections;
import org.hibernate.Query;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.REFERENCED_ENTITY_ALIAS;
@ -43,65 +37,104 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
/**
* Selects data from an audit entity.
*
* @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString;
private final String queryString;
private final String queryRemovedString;
public OneAuditEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
MiddleIdData referencingIdData,
String referencedEntityName, MiddleIdData referencedIdData,
boolean revisionTypeInId) {
public OneAuditEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy, MiddleIdData referencingIdData,
String referencedEntityName, MiddleIdData referencedIdData, boolean revisionTypeInId) {
super( verEntCfg, referencingIdData, revisionTypeInId );
/*
* The query that we need to create:
* SELECT e FROM versionsReferencedEntity e
* WHERE
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* e.id_ref_ing = :id_ref_ing AND
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidityAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
* (only non-deleted entities)
* e.revision_type != DEL
*/
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
/*
* The valid query that we need to create:
* SELECT e FROM versionsReferencedEntity e
* WHERE
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* e.id_ref_ing = :id_ref_ing AND
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidityAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
* (only non-deleted entities)
* e.revision_type != DEL
*/
final QueryBuilder commonPart = commonQueryPart( verEntCfg.getAuditEntityName( referencedEntityName ) );
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
globalCfg, auditStrategy, referencedIdData, validQuery, validQuery.getRootParameters(), true
);
createValidAndRemovedDataRestrictions( globalCfg, auditStrategy, referencedIdData, removedQuery );
String versionsReferencedEntityName = verEntCfg.getAuditEntityName(referencedEntityName);
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
}
// SELECT e FROM versionsEntity e
QueryBuilder qb = new QueryBuilder(versionsReferencedEntityName, REFERENCED_ENTITY_ALIAS);
qb.addProjection(null, REFERENCED_ENTITY_ALIAS, false, false);
// WHERE
Parameters rootParameters = qb.getRootParameters();
// e.id_ref_ed = :id_ref_ed
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, null, true);
/**
* Compute common part for both queries.
*/
private QueryBuilder commonQueryPart(String versionsReferencedEntityName) {
// SELECT e FROM versionsEntity e
final QueryBuilder qb = new QueryBuilder( versionsReferencedEntityName, REFERENCED_ENTITY_ALIAS );
qb.addProjection( null, REFERENCED_ENTITY_ALIAS, false, false );
// WHERE
// e.id_ref_ed = :id_ref_ed
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( qb.getRootParameters(), null, true );
return qb;
}
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(globalCfg, qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencedIdData,
revisionPropertyPath, originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR);
/**
* Creates query restrictions used to retrieve only actual data.
*/
private void createValidDataRestrictions(GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, QueryBuilder qb, Parameters rootParameters,
boolean inclusive) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(
globalCfg, qb, rootParameters, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencedIdData, revisionPropertyPath,
verEntCfg.getOriginalIdPropName(), REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR,
inclusive
);
// e.revision_type != DEL
rootParameters.addWhereWithNamedParam( getRevisionTypePath(), false, "!=", DEL_REVISION_TYPE_PARAMETER );
}
// e.revision_type != DEL
rootParameters.addWhereWithNamedParam(getRevisionTypePath(), false, "!=", DEL_REVISION_TYPE_PARAMETER);
StringBuilder sb = new StringBuilder();
qb.build(sb, Collections.<String, Object>emptyMap());
queryString = sb.toString();
}
/**
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision.
*/
private void createValidAndRemovedDataRestrictions(GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, QueryBuilder remQb) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
final Parameters valid = disjoint.addSubParameters( "and" ); // Restrictions to match all valid rows.
final Parameters removed = disjoint.addSubParameters( "and" ); // Restrictions to match all rows deleted at exactly given revision.
// Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions( globalCfg, auditStrategy, referencedIdData, remQb, valid, false );
// e.revision = :revision
removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), false, "=", REVISION_PARAMETER );
// e.revision_type = DEL
removed.addWhereWithNamedParam( getRevisionTypePath(), false, "=", DEL_REVISION_TYPE_PARAMETER );
}
@Override
protected String getQueryString() {
return queryString;
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
}

View File

@ -23,18 +23,12 @@
*/
package org.hibernate.envers.entities.mapper.relation.query;
import java.util.Collections;
import org.hibernate.Query;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS;
@ -42,67 +36,106 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
/**
* Selects data from a relation middle-table only.
*
* @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public final class OneEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString;
private final String queryString;
private final String queryRemovedString;
public OneEntityQueryGenerator(AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
boolean revisionTypeInId,
MiddleComponentData... componentDatas) {
public OneEntityQueryGenerator(AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy,
String versionsMiddleEntityName, MiddleIdData referencingIdData,
boolean revisionTypeInId, MiddleComponentData... componentData) {
super( verEntCfg, referencingIdData, revisionTypeInId );
/*
* The query that we need to create:
* SELECT ee FROM middleEntity ee WHERE
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.originalId.id_ref_ing = :id_ref_ing AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* AND
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL
*/
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
/*
* The valid query that we need to create:
* SELECT ee FROM middleEntity ee WHERE
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.originalId.id_ref_ing = :id_ref_ing AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* AND
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL
*/
final QueryBuilder commonPart = commonQueryPart( versionsMiddleEntityName );
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
auditStrategy, versionsMiddleEntityName, validQuery, validQuery.getRootParameters(), true, componentData
);
createValidAndRemovedDataRestrictions( auditStrategy, versionsMiddleEntityName, removedQuery, componentData );
// SELECT ee FROM middleEntity ee
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS);
qb.addProjection(null, MIDDLE_ENTITY_ALIAS, false, false);
// WHERE
Parameters rootParameters = qb.getRootParameters();
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
}
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas);
// ee.revision_type != DEL
rootParameters.addWhereWithNamedParam(getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER);
/**
* Compute common part for both queries.
*/
private QueryBuilder commonQueryPart(String versionsMiddleEntityName) {
// SELECT ee FROM middleEntity ee
final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS );
qb.addProjection( null, MIDDLE_ENTITY_ALIAS, false, false );
// WHERE
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( qb.getRootParameters(), verEntCfg.getOriginalIdPropName(), true );
return qb;
}
StringBuilder sb = new StringBuilder();
qb.build(sb, Collections.<String, Object>emptyMap());
queryString = sb.toString();
}
/**
* Creates query restrictions used to retrieve only actual data.
*/
private void createValidDataRestrictions(AuditStrategy auditStrategy, String versionsMiddleEntityName,
QueryBuilder qb, Parameters rootParameters, boolean inclusive,
MiddleComponentData... componentData) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(
qb, rootParameters, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true,
referencingIdData, versionsMiddleEntityName, eeOriginalIdPropertyPath, revisionPropertyPath,
originalIdPropertyName, MIDDLE_ENTITY_ALIAS, inclusive, componentData
);
// ee.revision_type != DEL
rootParameters.addWhereWithNamedParam( getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER );
}
/**
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision.
*/
private void createValidAndRemovedDataRestrictions(AuditStrategy auditStrategy, String versionsMiddleEntityName,
QueryBuilder remQb, MiddleComponentData... componentData) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
final Parameters valid = disjoint.addSubParameters( "and" ); // Restrictions to match all valid rows.
final Parameters removed = disjoint.addSubParameters( "and" ); // Restrictions to match all rows deleted at exactly given revision.
// Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions( auditStrategy, versionsMiddleEntityName, remQb, valid, false, componentData );
// ee.revision = :revision
removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), "=", REVISION_PARAMETER );
// ee.revision_type = DEL
removed.addWhereWithNamedParam( getRevisionTypePath(), "=", DEL_REVISION_TYPE_PARAMETER );
}
@Override
protected String getQueryString() {
return queryString;
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
}

View File

@ -34,5 +34,5 @@ import org.hibernate.envers.reader.AuditReaderImplementor;
* @author Adam Warski (adam at warski dot org)
*/
public interface RelationQueryGenerator {
Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision);
Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision, boolean removed);
}

View File

@ -23,19 +23,13 @@
*/
package org.hibernate.envers.entities.mapper.relation.query;
import java.util.Collections;
import org.hibernate.Query;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.INDEX_ENTITY_ALIAS;
@ -47,133 +41,188 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
/**
* Selects data from a relation middle-table and a two related versions entity.
*
* @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString;
private final String queryString;
private final String queryRemovedString;
public ThreeEntityQueryGenerator(GlobalConfiguration globalCfg,
AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
MiddleIdData indexIdData,
boolean revisionTypeInId,
MiddleComponentData... componentDatas) {
public ThreeEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy, String versionsMiddleEntityName,
MiddleIdData referencingIdData, MiddleIdData referencedIdData,
MiddleIdData indexIdData, boolean revisionTypeInId,
MiddleComponentData... componentData) {
super( verEntCfg, referencingIdData, revisionTypeInId );
/*
* The query that we need to create:
* SELECT new list(ee, e, f) FROM versionsReferencedEntity e, versionsIndexEntity f, middleEntity ee
* WHERE
* (entities referenced by the middle table; id_ref_ed = id of the referenced entity)
* ee.id_ref_ed = e.id_ref_ed AND
* (entities referenced by the middle table; id_ref_ind = id of the index entity)
* ee.id_ref_ind = f.id_ref_ind AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidityAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
*
* (selecting f entities at revision :revision)
* --> for DefaultAuditStrategy:
* f.revision = (SELECT max(f2.revision) FROM versionsIndexEntity f2
* WHERE f2.revision <= :revision AND f2.id_ref_ed = f.id_ref_ed)
*
* --> for ValidityAuditStrategy:
* f.revision <= :revision and (f.endRevision > :revision or f.endRevision is null)
*
* AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
and (
strtestent1_.REVEND>?
or strtestent1_.REVEND is null
)
and (
strtestent1_.REVEND>?
or strtestent1_.REVEND is null
)
and (
ternarymap0_.REVEND>?
or ternarymap0_.REVEND is null
)
*
*
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL AND
* e.revision_type != DEL AND
* f.revision_type != DEL
*/
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
/*
* The valid query that we need to create:
* SELECT new list(ee, e, f) FROM versionsReferencedEntity e, versionsIndexEntity f, middleEntity ee
* WHERE
* (entities referenced by the middle table; id_ref_ed = id of the referenced entity)
* ee.id_ref_ed = e.id_ref_ed AND
* (entities referenced by the middle table; id_ref_ind = id of the index entity)
* ee.id_ref_ind = f.id_ref_ind AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidityAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
*
* (selecting f entities at revision :revision)
* --> for DefaultAuditStrategy:
* f.revision = (SELECT max(f2.revision) FROM versionsIndexEntity f2
* WHERE f2.revision <= :revision AND f2.id_ref_ed = f.id_ref_ed)
*
* --> for ValidityAuditStrategy:
* f.revision <= :revision and (f.endRevision > :revision or f.endRevision is null)
*
* AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
* and ( strtestent1_.REVEND>? or strtestent1_.REVEND is null )
* and ( strtestent1_.REVEND>? or strtestent1_.REVEND is null )
* and ( ternarymap0_.REVEND>? or ternarymap0_.REVEND is null )
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL AND
* e.revision_type != DEL AND
* f.revision_type != DEL
*/
final QueryBuilder commonPart = commonQueryPart( referencedIdData, indexIdData, versionsMiddleEntityName, verEntCfg.getOriginalIdPropName() );
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, validQuery,
validQuery.getRootParameters(), true, componentData
);
createValidAndRemovedDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, removedQuery, componentData
);
// SELECT new list(ee) FROM middleEntity ee
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS);
qb.addFrom(referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS);
qb.addFrom(indexIdData.getAuditEntityName(), INDEX_ENTITY_ALIAS);
qb.addProjection("new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS + ", " + INDEX_ENTITY_ALIAS, false, false);
// WHERE
Parameters rootParameters = qb.getRootParameters();
// ee.id_ref_ed = e.id_ref_ed
referencedIdData.getPrefixedMapper().addIdsEqualToQuery(rootParameters, eeOriginalIdPropertyPath,
referencedIdData.getOriginalMapper(), REFERENCED_ENTITY_ALIAS + "." + originalIdPropertyName);
// ee.id_ref_ind = f.id_ref_ind
indexIdData.getPrefixedMapper().addIdsEqualToQuery(rootParameters, eeOriginalIdPropertyPath,
indexIdData.getOriginalMapper(), INDEX_ENTITY_ALIAS + "." + originalIdPropertyName);
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
}
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(globalCfg, qb, REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath,
REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), false,
referencedIdData, revisionPropertyPath, originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR);
// (selecting f entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(globalCfg, qb, REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath,
REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), false,
referencedIdData, revisionPropertyPath, originalIdPropertyName, INDEX_ENTITY_ALIAS, INDEX_ENTITY_ALIAS_DEF_AUD_STR);
/**
* Compute common part for both queries.
*/
private QueryBuilder commonQueryPart(MiddleIdData referencedIdData, MiddleIdData indexIdData,
String versionsMiddleEntityName, String originalIdPropertyName) {
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// SELECT new list(ee) FROM middleEntity ee
final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS );
qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS );
qb.addFrom( indexIdData.getAuditEntityName(), INDEX_ENTITY_ALIAS );
qb.addProjection(
"new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS + ", " + INDEX_ENTITY_ALIAS,
false, false
);
// WHERE
final Parameters rootParameters = qb.getRootParameters();
// ee.id_ref_ed = e.id_ref_ed
referencedIdData.getPrefixedMapper().addIdsEqualToQuery(
rootParameters, eeOriginalIdPropertyPath, referencedIdData.getOriginalMapper(),
REFERENCED_ENTITY_ALIAS + "." + originalIdPropertyName
);
// ee.id_ref_ind = f.id_ref_ind
indexIdData.getPrefixedMapper().addIdsEqualToQuery(
rootParameters, eeOriginalIdPropertyPath, indexIdData.getOriginalMapper(),
INDEX_ENTITY_ALIAS + "." + originalIdPropertyName
);
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true );
return qb;
}
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas);
/**
* Creates query restrictions used to retrieve only actual data.
*/
private void createValidDataRestrictions(GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, String versionsMiddleEntityName, QueryBuilder qb,
Parameters rootParameters, boolean inclusive, MiddleComponentData... componentData) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
final String revisionTypePropName = getRevisionTypePath();
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(
globalCfg, qb, rootParameters, REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath,
REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), false, referencedIdData, revisionPropertyPath,
originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, inclusive
);
// (selecting f entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(
globalCfg, qb, rootParameters, REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath,
REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), false, referencedIdData, revisionPropertyPath,
originalIdPropertyName, INDEX_ENTITY_ALIAS, INDEX_ENTITY_ALIAS_DEF_AUD_STR, inclusive
);
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(
qb, rootParameters, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true,
referencingIdData, versionsMiddleEntityName, eeOriginalIdPropertyPath, revisionPropertyPath,
originalIdPropertyName, MIDDLE_ENTITY_ALIAS, inclusive, componentData
);
// ee.revision_type != DEL
rootParameters.addWhereWithNamedParam( revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER );
// e.revision_type != DEL
rootParameters.addWhereWithNamedParam( REFERENCED_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER );
// f.revision_type != DEL
rootParameters.addWhereWithNamedParam( INDEX_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER );
}
// ee.revision_type != DEL
String revisionTypePropName = getRevisionTypePath();
rootParameters.addWhereWithNamedParam(revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER);
// e.revision_type != DEL
rootParameters.addWhereWithNamedParam(REFERENCED_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER);
// f.revision_type != DEL
rootParameters.addWhereWithNamedParam(INDEX_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER);
StringBuilder sb = new StringBuilder();
qb.build(sb, Collections.<String, Object>emptyMap());
queryString = sb.toString();
}
/**
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision.
*/
private void createValidAndRemovedDataRestrictions(GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, String versionsMiddleEntityName,
QueryBuilder remQb, MiddleComponentData... componentData) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
final Parameters valid = disjoint.addSubParameters( "and" ); // Restrictions to match all valid rows.
final Parameters removed = disjoint.addSubParameters( "and" ); // Restrictions to match all rows deleted at exactly given revision.
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String revisionTypePropName = getRevisionTypePath();
// Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, remQb, valid, false, componentData
);
// ee.revision = :revision
removed.addWhereWithNamedParam( revisionPropertyPath, "=", REVISION_PARAMETER );
// e.revision = :revision
removed.addWhereWithNamedParam( REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath, false, "=", REVISION_PARAMETER );
// f.revision = :revision
removed.addWhereWithNamedParam( INDEX_ENTITY_ALIAS + "." + revisionPropertyPath, false, "=", REVISION_PARAMETER );
// ee.revision_type = DEL
removed.addWhereWithNamedParam( revisionTypePropName, "=", DEL_REVISION_TYPE_PARAMETER );
// e.revision_type = DEL
removed.addWhereWithNamedParam( REFERENCED_ENTITY_ALIAS + "." + revisionTypePropName, false, "=", DEL_REVISION_TYPE_PARAMETER );
// f.revision_type = DEL
removed.addWhereWithNamedParam( INDEX_ENTITY_ALIAS + "." + revisionTypePropName, false, "=", DEL_REVISION_TYPE_PARAMETER );
}
@Override
protected String getQueryString() {
return queryString;
}
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
}

View File

@ -23,18 +23,12 @@
*/
package org.hibernate.envers.entities.mapper.relation.query;
import java.util.Collections;
import org.hibernate.Query;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS;
@ -43,74 +37,116 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
/**
* Selects data from a relation middle-table and a related non-audited entity.
*
* @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public final class TwoEntityOneAuditedQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString;
private final String queryString;
private final String queryRemovedString;
public TwoEntityOneAuditedQueryGenerator(AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
boolean revisionTypeInId,
MiddleComponentData... componentDatas) {
public TwoEntityOneAuditedQueryGenerator(AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy,
String versionsMiddleEntityName, MiddleIdData referencingIdData,
MiddleIdData referencedIdData, boolean revisionTypeInId,
MiddleComponentData... componentData) {
super( verEntCfg, referencingIdData, revisionTypeInId );
/*
* The query that we need to create:
* SELECT new list(ee, e) FROM referencedEntity e, middleEntity ee
* WHERE
* (entities referenced by the middle table; id_ref_ed = id of the referenced entity)
* ee.id_ref_ed = e.id_ref_ed AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* AND
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL
*/
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
/*
* The valid query that we need to create:
* SELECT new list(ee, e) FROM referencedEntity e, middleEntity ee
* WHERE
* (entities referenced by the middle table; id_ref_ed = id of the referenced entity)
* ee.id_ref_ed = e.id_ref_ed AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* AND
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL
*/
final QueryBuilder commonPart = commonQueryPart( referencedIdData, versionsMiddleEntityName, verEntCfg.getOriginalIdPropName() );
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
auditStrategy, versionsMiddleEntityName, validQuery, validQuery.getRootParameters(), componentData
);
createValidAndRemovedDataRestrictions( auditStrategy, versionsMiddleEntityName, removedQuery, componentData );
String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
}
// SELECT new list(ee) FROM middleEntity ee
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS);
qb.addFrom(referencedIdData.getEntityName(), REFERENCED_ENTITY_ALIAS);
qb.addProjection("new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, false, false);
// WHERE
Parameters rootParameters = qb.getRootParameters();
// ee.id_ref_ed = e.id_ref_ed
referencedIdData.getPrefixedMapper().addIdsEqualToQuery(rootParameters, eeOriginalIdPropertyPath,
referencedIdData.getOriginalMapper(), REFERENCED_ENTITY_ALIAS);
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
/**
* Compute common part for both queries.
*/
private QueryBuilder commonQueryPart(MiddleIdData referencedIdData, String versionsMiddleEntityName,
String originalIdPropertyName) {
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// SELECT new list(ee) FROM middleEntity ee
final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS );
qb.addFrom( referencedIdData.getEntityName(), REFERENCED_ENTITY_ALIAS );
qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, false, false );
// WHERE
final Parameters rootParameters = qb.getRootParameters();
// ee.id_ref_ed = e.id_ref_ed
referencedIdData.getPrefixedMapper().addIdsEqualToQuery(
rootParameters, eeOriginalIdPropertyPath, referencedIdData.getOriginalMapper(), REFERENCED_ENTITY_ALIAS
);
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true );
return qb;
}
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true,referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas);
/**
* Creates query restrictions used to retrieve only actual data.
*/
private void createValidDataRestrictions(AuditStrategy auditStrategy, String versionsMiddleEntityName, QueryBuilder qb,
Parameters rootParameters, MiddleComponentData... componentData) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(
qb, rootParameters, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true,
referencingIdData, versionsMiddleEntityName, eeOriginalIdPropertyPath, revisionPropertyPath,
originalIdPropertyName, MIDDLE_ENTITY_ALIAS, true, componentData
);
// ee.revision_type != DEL
rootParameters.addWhereWithNamedParam( getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER );
}
// ee.revision_type != DEL
rootParameters.addWhereWithNamedParam(getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER);
StringBuilder sb = new StringBuilder();
qb.build(sb, Collections.<String, Object>emptyMap());
queryString = sb.toString();
}
/**
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision.
*/
private void createValidAndRemovedDataRestrictions(AuditStrategy auditStrategy, String versionsMiddleEntityName,
QueryBuilder remQb, MiddleComponentData... componentData) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
final Parameters valid = disjoint.addSubParameters( "and" ); // Restrictions to match all valid rows.
final Parameters removed = disjoint.addSubParameters( "and" ); // Restrictions to match all rows deleted at exactly given revision.
createValidDataRestrictions( auditStrategy, versionsMiddleEntityName, remQb, valid, componentData );
// ee.revision = :revision
removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), "=", REVISION_PARAMETER );
// ee.revision_type = DEL
removed.addWhereWithNamedParam( getRevisionTypePath(), "=", DEL_REVISION_TYPE_PARAMETER );
}
@Override
protected String getQueryString() {
return queryString;
}
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
}

View File

@ -23,19 +23,13 @@
*/
package org.hibernate.envers.entities.mapper.relation.query;
import java.util.Collections;
import org.hibernate.Query;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS;
@ -45,94 +39,148 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
/**
* Selects data from a relation middle-table and a related versions entity.
*
* @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public final class TwoEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString;
private final String queryString;
private final String queryRemovedString;
public TwoEntityQueryGenerator(GlobalConfiguration globalCfg,
AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
boolean revisionTypeInId,
MiddleComponentData... componentDatas) {
public TwoEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy, String versionsMiddleEntityName,
MiddleIdData referencingIdData, MiddleIdData referencedIdData,
boolean revisionTypeInId, MiddleComponentData... componentData) {
super( verEntCfg, referencingIdData, revisionTypeInId );
/*
* The query that we need to create:
* SELECT new list(ee, e) FROM versionsReferencedEntity e, middleEntity ee
* WHERE
* (entities referenced by the middle table; id_ref_ed = id of the referenced entity)
* ee.id_ref_ed = e.id_ref_ed AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
*
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidityAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL AND
* e.revision_type != DEL
*/
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
/*
* The valid query that we need to create:
* SELECT new list(ee, e) FROM versionsReferencedEntity e, middleEntity ee
* WHERE
* (entities referenced by the middle table; id_ref_ed = id of the referenced entity)
* ee.id_ref_ed = e.id_ref_ed AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
*
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidityAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL AND
* e.revision_type != DEL
*/
final QueryBuilder commonPart = commonQueryPart( referencedIdData, versionsMiddleEntityName, verEntCfg.getOriginalIdPropName() );
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, validQuery,
validQuery.getRootParameters(), true, componentData
);
createValidAndRemovedDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, removedQuery, componentData
);
String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
}
// SELECT new list(ee) FROM middleEntity ee
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS);
qb.addFrom(referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS);
qb.addProjection("new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, false, false);
// WHERE
Parameters rootParameters = qb.getRootParameters();
// ee.id_ref_ed = e.id_ref_ed
referencedIdData.getPrefixedMapper().addIdsEqualToQuery(rootParameters, eeOriginalIdPropertyPath,
referencedIdData.getOriginalMapper(), REFERENCED_ENTITY_ALIAS + "." + originalIdPropertyName);
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
/**
* Compute common part for both queries.
*/
private QueryBuilder commonQueryPart(MiddleIdData referencedIdData, String versionsMiddleEntityName,
String originalIdPropertyName) {
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// SELECT new list(ee) FROM middleEntity ee
QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS );
qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS );
qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, false, false );
// WHERE
final Parameters rootParameters = qb.getRootParameters();
// ee.id_ref_ed = e.id_ref_ed
referencedIdData.getPrefixedMapper().addIdsEqualToQuery(
rootParameters, eeOriginalIdPropertyPath, referencedIdData.getOriginalMapper(),
REFERENCED_ENTITY_ALIAS + "." + originalIdPropertyName
);
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true );
return qb;
}
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(globalCfg, qb, REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath,
REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), false,
referencedIdData, revisionPropertyPath, originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR);
/**
* Creates query restrictions used to retrieve only actual data.
*/
private void createValidDataRestrictions(GlobalConfiguration globalCfg, AuditStrategy auditStrategy, MiddleIdData referencedIdData,
String versionsMiddleEntityName, QueryBuilder qb, Parameters rootParameters,
boolean inclusive, MiddleComponentData... componentData) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
final String revisionTypePropName = getRevisionTypePath();
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(
globalCfg, qb, rootParameters, REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath,
REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), false, referencedIdData, revisionPropertyPath,
originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, inclusive
);
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction( qb, rootParameters, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS,
inclusive, componentData
);
// ee.revision_type != DEL
rootParameters.addWhereWithNamedParam( revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER );
// e.revision_type != DEL
rootParameters.addWhereWithNamedParam( REFERENCED_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER );
}
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas);
// ee.revision_type != DEL
String revisionTypePropName = getRevisionTypePath();
rootParameters.addWhereWithNamedParam(revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER);
// e.revision_type != DEL
rootParameters.addWhereWithNamedParam(REFERENCED_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER);
StringBuilder sb = new StringBuilder();
qb.build(sb, Collections.<String, Object>emptyMap());
queryString = sb.toString();
}
/**
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision.
*/
private void createValidAndRemovedDataRestrictions(GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, String versionsMiddleEntityName,
QueryBuilder remQb, MiddleComponentData... componentData) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
final Parameters valid = disjoint.addSubParameters( "and" ); // Restrictions to match all valid rows.
final Parameters removed = disjoint.addSubParameters( "and" ); // Restrictions to match all rows deleted at exactly given revision.
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String revisionTypePropName = getRevisionTypePath();
// Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions( globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, remQb, valid, false, componentData );
// ee.revision = :revision
removed.addWhereWithNamedParam( revisionPropertyPath, "=", REVISION_PARAMETER );
// e.revision = :revision
removed.addWhereWithNamedParam( REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath, false, "=", REVISION_PARAMETER );
// ee.revision_type = DEL
removed.addWhereWithNamedParam( revisionTypePropName, "=", DEL_REVISION_TYPE_PARAMETER );
// e.revision_type = DEL
removed.addWhereWithNamedParam( REFERENCED_ENTITY_ALIAS + "." + revisionTypePropName, false, "=", DEL_REVISION_TYPE_PARAMETER );
}
@Override
protected String getQueryString() {
return queryString;
}
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
}

View File

@ -91,9 +91,9 @@ public class EntitiesAtRevisionQuery extends AbstractAuditQuery {
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
verCfg.getAuditStrategy().addEntityAtRevisionRestriction(verCfg.getGlobalCfg(), qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencedIdData,
revisionPropertyPath, originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR);
verCfg.getAuditStrategy().addEntityAtRevisionRestriction(verCfg.getGlobalCfg(), qb, qb.getRootParameters(),
revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true, referencedIdData,
revisionPropertyPath, originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, true);
if (!includeDeletions) {
// e.revision_type != DEL

View File

@ -9,6 +9,7 @@ import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
/**
@ -59,6 +60,7 @@ public interface AuditStrategy {
*
* @param globalCfg the {@link GlobalConfiguration}
* @param rootQueryBuilder the {@link QueryBuilder} that will be updated
* @param parameters root parameters to which restrictions shall be added
* @param revisionProperty property of the revision column
* @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy})
* @param addAlias {@code boolean} indicator if a left alias is needed
@ -67,10 +69,11 @@ public interface AuditStrategy {
* @param originalIdPropertyName name of the id property (only used for {@link ValidityAuditStrategy})
* @param alias1 an alias used for subquery (only used for {@link ValidityAuditStrategy})
* @param alias2 an alias used for subquery (only used for {@link ValidityAuditStrategy})
* @param inclusive indicates whether revision number shall be treated as inclusive or exclusive
*/
void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder,
void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder, Parameters parameters,
String revisionProperty, String revisionEndProperty, boolean addAlias, MiddleIdData idData,
String revisionPropertyPath, String originalIdPropertyName, String alias1, String alias2);
String revisionPropertyPath, String originalIdPropertyName, String alias1, String alias2, boolean inclusive);
/**
* Update the rootQueryBuilder with an extra WHERE clause to restrict the revision for a middle-entity
@ -85,6 +88,7 @@ public interface AuditStrategy {
* </ul>
*
* @param rootQueryBuilder the {@link QueryBuilder} that will be updated
* @param parameters root parameters to which restrictions shall be added
* @param revisionProperty property of the revision column
* @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy})
* @param addAlias {@code boolean} indicator if a left alias is needed
@ -94,11 +98,11 @@ public interface AuditStrategy {
* @param revisionPropertyPath path of the revision property (only used for {@link ValidityAuditStrategy})
* @param originalIdPropertyName name of the id property (only used for {@link ValidityAuditStrategy})
* @param alias1 an alias used for subqueries (only used for {@link DefaultAuditStrategy})
* @param inclusive indicates whether revision number shall be treated as inclusive or exclusive
* @param componentDatas information about the middle-entity relation
*/
void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty,
void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, Parameters parameters, String revisionProperty,
String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData,
String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath,
String originalIdPropertyName, String alias1, MiddleComponentData... componentDatas);
String originalIdPropertyName, String alias1, boolean inclusive, MiddleComponentData... componentDatas);
}

View File

@ -41,11 +41,9 @@ public class DefaultAuditStrategy implements AuditStrategy {
}
public void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder, String revisionProperty,
String revisionEndProperty, boolean addAlias, MiddleIdData idData, String revisionPropertyPath,
String originalIdPropertyName, String alias1, String alias2) {
Parameters rootParameters = rootQueryBuilder.getRootParameters();
public void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder, Parameters parameters,
String revisionProperty, String revisionEndProperty, boolean addAlias, MiddleIdData idData, String revisionPropertyPath,
String originalIdPropertyName, String alias1, String alias2, boolean inclusive) {
// create a subquery builder
// SELECT max(e.revision) FROM versionsReferencedEntity e2
QueryBuilder maxERevQb = rootQueryBuilder.newSubQueryBuilder(idData.getAuditEntityName(), alias2);
@ -53,29 +51,27 @@ public class DefaultAuditStrategy implements AuditStrategy {
// WHERE
Parameters maxERevQbParameters = maxERevQb.getRootParameters();
// e2.revision <= :revision
maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", REVISION_PARAMETER);
maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, inclusive ? "<=" : "<", REVISION_PARAMETER);
// e2.id_ref_ed = e.id_ref_ed
idData.getOriginalMapper().addIdsEqualToQuery(maxERevQbParameters,
alias1 + "." + originalIdPropertyName, alias2 +"." + originalIdPropertyName);
// add subquery to rootParameters
String subqueryOperator = globalCfg.getCorrelatedSubqueryOperator();
rootParameters.addWhere(revisionProperty, addAlias, subqueryOperator, maxERevQb);
parameters.addWhere(revisionProperty, addAlias, subqueryOperator, maxERevQb);
}
public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty,
public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, Parameters parameters, String revisionProperty,
String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData, String versionsMiddleEntityName,
String eeOriginalIdPropertyPath, String revisionPropertyPath,
String originalIdPropertyName, String alias1, MiddleComponentData... componentDatas) {
Parameters rootParameters = rootQueryBuilder.getRootParameters();
// SELECT max(ee2.revision) FROM middleEntity ee2
String eeOriginalIdPropertyPath, String revisionPropertyPath, String originalIdPropertyName, String alias1,
boolean inclusive, MiddleComponentData... componentDatas) {
// SELECT max(ee2.revision) FROM middleEntity ee2
QueryBuilder maxEeRevQb = rootQueryBuilder.newSubQueryBuilder(versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS_DEF_AUD_STR);
maxEeRevQb.addProjection("max", revisionPropertyPath, false);
// WHERE
Parameters maxEeRevQbParameters = maxEeRevQb.getRootParameters();
// ee2.revision <= :revision
maxEeRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", REVISION_PARAMETER);
maxEeRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, inclusive ? "<=" : "<", REVISION_PARAMETER);
// ee2.originalId.* = ee.originalId.*
String ee2OriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS_DEF_AUD_STR + "." + originalIdPropertyName;
referencingIdData.getPrefixedMapper().addIdsEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
@ -84,7 +80,7 @@ public class DefaultAuditStrategy implements AuditStrategy {
}
// add subquery to rootParameters
rootParameters.addWhere(revisionProperty, addAlias, "=", maxEeRevQb);
parameters.addWhere(revisionProperty, addAlias, "=", maxEeRevQb);
}
}

View File

@ -85,7 +85,6 @@ public class ValidityAuditStrategy implements AuditStrategy {
final Serializable id,
Object data,
final Object revision) {
final AuditEntitiesConfiguration audEntitiesCfg = auditCfg.getAuditEntCfg();
final String auditedEntityName = audEntitiesCfg.getAuditEntityName( entityName );
final String revisionInfoEntityName = auditCfg.getAuditEntCfg().getRevisionInfoEntityName();
@ -290,32 +289,29 @@ public class ValidityAuditStrategy implements AuditStrategy {
}
public void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder,
String revisionProperty,String revisionEndProperty, boolean addAlias,
Parameters parameters, String revisionProperty,String revisionEndProperty, boolean addAlias,
MiddleIdData idData, String revisionPropertyPath, String originalIdPropertyName,
String alias1, String alias2) {
Parameters rootParameters = rootQueryBuilder.getRootParameters();
addRevisionRestriction(rootParameters, revisionProperty, revisionEndProperty, addAlias);
String alias1, String alias2, boolean inclusive) {
addRevisionRestriction(parameters, revisionProperty, revisionEndProperty, addAlias, inclusive);
}
public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty,
public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, Parameters parameters, String revisionProperty,
String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData,
String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath,
String originalIdPropertyName, String alias1, MiddleComponentData... componentDatas) {
Parameters rootParameters = rootQueryBuilder.getRootParameters();
addRevisionRestriction(rootParameters, revisionProperty, revisionEndProperty, addAlias);
String originalIdPropertyName, String alias1, boolean inclusive, MiddleComponentData... componentDatas) {
addRevisionRestriction(parameters, revisionProperty, revisionEndProperty, addAlias, inclusive);
}
public void setRevisionTimestampGetter(Getter revisionTimestampGetter) {
this.revisionTimestampGetter = revisionTimestampGetter;
}
private void addRevisionRestriction(Parameters rootParameters,
String revisionProperty, String revisionEndProperty, boolean addAlias) {
private void addRevisionRestriction(Parameters rootParameters, String revisionProperty, String revisionEndProperty,
boolean addAlias, boolean inclusive) {
// e.revision <= _revision and (e.endRevision > _revision or e.endRevision is null)
Parameters subParm = rootParameters.addSubParameters("or");
rootParameters.addWhereWithNamedParam(revisionProperty, addAlias, "<=", REVISION_PARAMETER);
subParm.addWhereWithNamedParam(revisionEndProperty + ".id", addAlias, ">", REVISION_PARAMETER);
rootParameters.addWhereWithNamedParam(revisionProperty, addAlias, inclusive ? "<=" : "<", REVISION_PARAMETER);
subParm.addWhereWithNamedParam(revisionEndProperty + ".id", addAlias, inclusive ? ">" : ">=", REVISION_PARAMETER);
subParm.addWhere(revisionEndProperty, addAlias, "is", "null", false);
}
@ -327,7 +323,6 @@ public class ValidityAuditStrategy implements AuditStrategy {
@SuppressWarnings({"unchecked"})
private void updateLastRevision(Session session, AuditConfiguration auditCfg, List<Object> l,
Object id, String auditedEntityName, Object revision) {
// There should be one entry
if (l.size() == 1) {
// Setting the end revision to be the current rev

View File

@ -36,6 +36,10 @@ public class MutableInteger {
this.value = value;
}
public MutableInteger deepCopy() {
return new MutableInteger( value );
}
public int getAndIncrease() {
return value++;
}

View File

@ -31,8 +31,8 @@ package org.hibernate.envers.tools;
* @author Adam Warski (adamw@aster.pl)
*/
public class Pair<T1, T2> {
private T1 obj1;
private T2 obj2;
private final T1 obj1;
private final T2 obj2;
public Pair(T1 obj1, T2 obj2) {
this.obj1 = obj1;

View File

@ -79,6 +79,28 @@ public class Parameters {
localQueryParamValues = new HashMap<String, Object>();
}
// Only for deep copy purpose.
private Parameters(Parameters other) {
this.alias = other.alias;
this.connective = other.connective;
this.queryParamCounter = other.queryParamCounter.deepCopy();
subParameters = new ArrayList<Parameters>( other.subParameters.size() );
for ( Parameters p : other.subParameters ) {
subParameters.add( p.deepCopy() );
}
negatedParameters = new ArrayList<Parameters>( other.negatedParameters.size() );
for ( Parameters p : other.negatedParameters ) {
negatedParameters.add( p.deepCopy() );
}
expressions = new ArrayList<String>( other.expressions );
localQueryParamValues = new HashMap<String, Object>( other.localQueryParamValues );
}
public Parameters deepCopy() {
return new Parameters( this );
}
private String generateQueryParam() {
return "_p" + queryParamCounter.getAndIncrease();
}

View File

@ -92,6 +92,23 @@ public class QueryBuilder {
addFrom(entityName, alias);
}
// Only for deep copy purpose.
private QueryBuilder(QueryBuilder other) {
this.entityName = other.entityName;
this.alias = other.alias;
this.aliasCounter = other.aliasCounter.deepCopy();
this.paramCounter = other.paramCounter.deepCopy();
this.rootParameters = other.rootParameters.deepCopy();
froms = new ArrayList<Pair<String, String>>( other.froms );
orders = new ArrayList<Pair<String, Boolean>>( other.orders );
projections = new ArrayList<String>( other.projections );
}
public QueryBuilder deepCopy() {
return new QueryBuilder( this );
}
/**
* Add an entity from which to select.
* @param entityName Name of the entity from which to select.

View File

@ -1,83 +1,444 @@
package org.hibernate.envers.test.integration.proxy;
import org.hibernate.Hibernate;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.query.AuditEntity;
import org.hibernate.envers.query.AuditQuery;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.entities.onetomany.SetRefEdEntity;
import org.hibernate.envers.test.entities.onetomany.SetRefIngEntity;
import org.hibernate.envers.test.tools.TestTools;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import org.hibernate.Hibernate;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.enhanced.SequenceIdRevisionEntity;
import org.hibernate.envers.query.AuditEntity;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.entities.IntTestPrivSeqEntity;
import org.hibernate.envers.test.entities.StrTestPrivSeqEntity;
import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
import org.hibernate.envers.test.entities.collection.StringSetEntity;
import org.hibernate.envers.test.entities.manytomany.SetOwnedEntity;
import org.hibernate.envers.test.entities.manytomany.SetOwningEntity;
import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MIndexedListTargetNotAuditedEntity;
import org.hibernate.envers.test.entities.onetomany.SetRefEdEntity;
import org.hibernate.envers.test.entities.onetomany.SetRefIngEntity;
import org.hibernate.envers.test.integration.manytomany.ternary.TernaryMapEntity;
import org.hibernate.envers.test.tools.TestTools;
import org.hibernate.testing.TestForIssue;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@TestForIssue(jiraKey = "HHH-5845")
public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase {
@Override
@SuppressWarnings("unchecked")
protected void addConfigOptions(Map options) {
options.put("org.hibernate.envers.store_data_at_delete", "true");
}
private Integer stringSetId = null;
private Integer ternaryMapId = null;
private UnversionedStrTestEntity unversionedEntity1 = null;
private UnversionedStrTestEntity unversionedEntity2 = null;
private StrTestPrivSeqEntity stringEntity1 = null;
private StrTestPrivSeqEntity stringEntity2 = null;
private IntTestPrivSeqEntity intEntity1 = null;
private IntTestPrivSeqEntity intEntity2 = null;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { SetRefEdEntity.class, SetRefIngEntity.class };
}
@Override
protected void addConfigOptions(Map options) {
super.addConfigOptions( options );
options.put( "org.hibernate.envers.store_data_at_delete", "true" );
}
@Test
@Priority(10)
public void initData() {
EntityManager em = getEntityManager();
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
SetRefEdEntity.class, SetRefIngEntity.class, SetOwnedEntity.class, SetOwningEntity.class,
StringSetEntity.class, UnversionedStrTestEntity.class, M2MIndexedListTargetNotAuditedEntity.class,
TernaryMapEntity.class, StrTestPrivSeqEntity.class, IntTestPrivSeqEntity.class
};
}
SetRefEdEntity refEdEntity = new SetRefEdEntity(1, "Demo Data");
SetRefIngEntity refIngEntity = new SetRefIngEntity(2, "Example Data", refEdEntity);
@Test
@Priority(10)
public void initData() {
EntityManager em = getEntityManager();
em.getTransaction().begin();
em.persist(refEdEntity);
em.persist(refIngEntity);
em.getTransaction().commit();
SetRefEdEntity refEdEntity1 = new SetRefEdEntity( 1, "Demo Data 1" );
SetRefIngEntity refIngEntity1 = new SetRefIngEntity( 2, "Example Data 1", refEdEntity1 );
em.getTransaction().begin();
refIngEntity = em.find(SetRefIngEntity.class, 2);
em.remove(refIngEntity);
em.remove(refEdEntity);
em.getTransaction().commit();
}
// Revision 1
em.getTransaction().begin();
em.persist( refEdEntity1 );
em.persist( refIngEntity1 );
em.getTransaction().commit();
@Test
public void testFindDeletedReference() {
AuditQuery query = getAuditReader().createQuery().forRevisionsOfEntity(SetRefIngEntity.class, false, true)
.add(AuditEntity.revisionType().eq(RevisionType.DEL));
List queryResult = (List) query.getResultList();
// Revision 2 - removing both object in the same revision
em.getTransaction().begin();
refEdEntity1 = em.find( SetRefEdEntity.class, 1 );
refIngEntity1 = em.find( SetRefIngEntity.class, 2 );
em.remove( refIngEntity1 );
em.remove( refEdEntity1 );
em.getTransaction().commit();
Object[] objArray = (Object[]) queryResult.get(0);
SetRefIngEntity refIngEntity = (SetRefIngEntity) objArray[0];
Assert.assertEquals("Example Data", refIngEntity.getData());
SetRefEdEntity refEdEntity2 = new SetRefEdEntity( 3, "Demo Data 2" );
SetRefIngEntity refIngEntity2 = new SetRefIngEntity( 4, "Example Data 2", refEdEntity2 );
Hibernate.initialize(refIngEntity.getReference());
Assert.assertEquals("Demo Data", refIngEntity.getReference().getData());
}
// Revision 3
em.getTransaction().begin();
em.persist( refEdEntity2 );
em.persist( refIngEntity2 );
em.getTransaction().commit();
@FailureExpected(jiraKey = "HHH-5845") // TODO: doesn't work until collection queries are fixed
@Test
public void testFindDeletedReferring() {
AuditQuery query = getAuditReader().createQuery().forRevisionsOfEntity(SetRefEdEntity.class, false, true)
.add(AuditEntity.revisionType().eq(RevisionType.DEL));
List queryResult = (List) query.getResultList();
// Revision 4 - removing child object
em.getTransaction().begin();
refIngEntity2 = em.find( SetRefIngEntity.class, 4 );
em.remove( refIngEntity2 );
em.getTransaction().commit();
Object[] objArray = (Object[]) queryResult.get(0);
SetRefEdEntity refEdEntity = (SetRefEdEntity) objArray[0];
Assert.assertEquals("Demo Data", refEdEntity.getData());
// Revision 5 - removing parent object
em.getTransaction().begin();
refEdEntity2 = em.find( SetRefEdEntity.class, 3 );
em.remove( refEdEntity2 );
em.getTransaction().commit();
Hibernate.initialize(refEdEntity.getReffering());
Assert.assertEquals(TestTools.makeSet(new SetRefIngEntity(2, "Example Data")), refEdEntity.getReffering());
}
}
SetOwningEntity setOwningEntity1 = new SetOwningEntity( 5, "Demo Data 1" );
SetOwnedEntity setOwnedEntity1 = new SetOwnedEntity( 6, "Example Data 1" );
Set<SetOwningEntity> owning = new HashSet<SetOwningEntity>();
Set<SetOwnedEntity> owned = new HashSet<SetOwnedEntity>();
owning.add( setOwningEntity1 );
owned.add( setOwnedEntity1 );
setOwningEntity1.setReferences( owned );
setOwnedEntity1.setReferencing( owning );
// Revision 6
em.getTransaction().begin();
em.persist( setOwnedEntity1 );
em.persist( setOwningEntity1 );
em.getTransaction().commit();
// Revision 7 - removing both object in the same revision
em.getTransaction().begin();
setOwnedEntity1 = em.find( SetOwnedEntity.class, 6 );
setOwningEntity1 = em.find( SetOwningEntity.class, 5 );
em.remove( setOwningEntity1 );
em.remove( setOwnedEntity1 );
em.getTransaction().commit();
SetOwningEntity setOwningEntity2 = new SetOwningEntity( 7, "Demo Data 2" );
SetOwnedEntity setOwnedEntity2 = new SetOwnedEntity( 8, "Example Data 2" );
owning = new HashSet<SetOwningEntity>();
owned = new HashSet<SetOwnedEntity>();
owning.add( setOwningEntity2 );
owned.add( setOwnedEntity2 );
setOwningEntity2.setReferences( owned );
setOwnedEntity2.setReferencing( owning );
// Revision 8
em.getTransaction().begin();
em.persist( setOwnedEntity2 );
em.persist( setOwningEntity2 );
em.getTransaction().commit();
// Revision 9 - removing first object
em.getTransaction().begin();
setOwningEntity2 = em.find( SetOwningEntity.class, 7 );
em.remove( setOwningEntity2 );
em.getTransaction().commit();
// Revision 10 - removing second object
em.getTransaction().begin();
setOwnedEntity2 = em.find( SetOwnedEntity.class, 8 );
em.remove( setOwnedEntity2 );
em.getTransaction().commit();
StringSetEntity stringSetEntity = new StringSetEntity();
stringSetEntity.getStrings().add( "string 1" );
stringSetEntity.getStrings().add( "string 2" );
// Revision 11
em.getTransaction().begin();
em.persist( stringSetEntity );
em.getTransaction().commit();
stringSetId = stringSetEntity.getId();
// Revision 12 - removing element collection
em.getTransaction().begin();
stringSetEntity = em.find( StringSetEntity.class, stringSetEntity.getId() );
em.remove( stringSetEntity );
em.getTransaction().commit();
// Revision 13
em.getTransaction().begin();
unversionedEntity1 = new UnversionedStrTestEntity( "string 1" );
unversionedEntity2 = new UnversionedStrTestEntity( "string 2" );
M2MIndexedListTargetNotAuditedEntity relationNotAuditedEntity = new M2MIndexedListTargetNotAuditedEntity( 1, "Parent" );
relationNotAuditedEntity.getReferences().add( unversionedEntity1 );
relationNotAuditedEntity.getReferences().add( unversionedEntity2 );
em.persist( unversionedEntity1 );
em.persist( unversionedEntity2 );
em.persist( relationNotAuditedEntity );
em.getTransaction().commit();
// Revision 14 - removing entity with unversioned relation
em.getTransaction().begin();
relationNotAuditedEntity = em.find( M2MIndexedListTargetNotAuditedEntity.class, relationNotAuditedEntity.getId() );
em.remove( relationNotAuditedEntity );
em.getTransaction().commit();
stringEntity1 = new StrTestPrivSeqEntity( "Value 1" );
stringEntity2 = new StrTestPrivSeqEntity( "Value 2" );
intEntity1 = new IntTestPrivSeqEntity( 1 );
intEntity2 = new IntTestPrivSeqEntity( 2 );
TernaryMapEntity mapEntity = new TernaryMapEntity();
mapEntity.getMap().put( intEntity1, stringEntity1 );
mapEntity.getMap().put( intEntity2, stringEntity2 );
// Revision 15
em.getTransaction().begin();
em.persist( stringEntity1 );
em.persist( stringEntity2 );
em.persist( intEntity1 );
em.persist( intEntity2 );
em.persist( mapEntity );
em.getTransaction().commit();
ternaryMapId = mapEntity.getId();
// Revision 16 - removing ternary map
em.getTransaction().begin();
mapEntity = em.find( TernaryMapEntity.class, mapEntity.getId() );
em.remove( mapEntity );
em.getTransaction().commit();
em.close();
}
@Test
public void testTernaryMap() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( TernaryMapEntity.class, false, true )
.add( AuditEntity.id().eq( ternaryMapId ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 16, getRevisionNumber( objArray[1] ) );
TernaryMapEntity mapEntity = (TernaryMapEntity) objArray[0];
Assert.assertEquals( TestTools.makeMap( intEntity1, stringEntity1, intEntity2, stringEntity2 ), mapEntity.getMap() );
}
@Test
public void testUnversionedRelation() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( M2MIndexedListTargetNotAuditedEntity.class, false, true )
.add( AuditEntity.id().eq( 1 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 14, getRevisionNumber( objArray[1] ) );
M2MIndexedListTargetNotAuditedEntity relationNotAuditedEntity = (M2MIndexedListTargetNotAuditedEntity) objArray[0];
Assert.assertTrue(
TestTools.checkList(
relationNotAuditedEntity.getReferences(),
unversionedEntity1, unversionedEntity2
)
);
}
@Test
public void testElementCollection() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( StringSetEntity.class, false, true )
.add( AuditEntity.id().eq( stringSetId ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 12, getRevisionNumber( objArray[1] ) );
StringSetEntity stringSetEntity = (StringSetEntity) objArray[0];
Assert.assertEquals( TestTools.makeSet( "string 1", "string 2" ), stringSetEntity.getStrings() );
}
// One to many tests.
@Test
public void testReferencedOneToManySameRevision() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetRefIngEntity.class, false, true )
.add( AuditEntity.id().eq( 2 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 2, getRevisionNumber( objArray[1] ) );
SetRefIngEntity refIngEntity = (SetRefIngEntity) objArray[0];
Assert.assertEquals( "Example Data 1", refIngEntity.getData() );
Hibernate.initialize( refIngEntity.getReference() );
Assert.assertEquals( "Demo Data 1", refIngEntity.getReference().getData() );
}
@Test
public void testReferringOneToManySameRevision() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetRefEdEntity.class, false, true )
.add( AuditEntity.id().eq( 1 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 2, getRevisionNumber( objArray[1] ) );
SetRefEdEntity refEdEntity = (SetRefEdEntity) objArray[0];
Assert.assertEquals( "Demo Data 1", refEdEntity.getData() );
Hibernate.initialize( refEdEntity.getReffering() );
Assert.assertEquals(
TestTools.makeSet( new SetRefIngEntity( 2, "Example Data 1" ) ),
refEdEntity.getReffering()
);
}
@Test
public void testReferencedOneToManyDifferentRevisions() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetRefIngEntity.class, false, true )
.add( AuditEntity.id().eq( 4 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 4, getRevisionNumber( objArray[1] ) );
SetRefIngEntity refIngEntity = (SetRefIngEntity) objArray[0];
Assert.assertEquals( "Example Data 2", refIngEntity.getData() );
Hibernate.initialize( refIngEntity.getReference() );
Assert.assertEquals( "Demo Data 2", refIngEntity.getReference().getData() );
}
@Test
public void testReferringOneToManyDifferentRevisions() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetRefEdEntity.class, false, true )
.add( AuditEntity.id().eq( 3 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 5, getRevisionNumber( objArray[1] ) );
SetRefEdEntity refEdEntity = (SetRefEdEntity) objArray[0];
Assert.assertEquals( "Demo Data 2", refEdEntity.getData() );
Hibernate.initialize( refEdEntity.getReffering() );
Assert.assertTrue( refEdEntity.getReffering().isEmpty() );
// After commit in revision four, child entity has been removed.
queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetRefEdEntity.class, false, true )
.add( AuditEntity.id().eq( 3 ) )
.add( AuditEntity.revisionNumber().eq( 4 ) )
.getResultList();
objArray = (Object[]) queryResult.get( 0 );
refEdEntity = (SetRefEdEntity) objArray[0];
Assert.assertEquals( "Demo Data 2", refEdEntity.getData() );
Hibernate.initialize( refEdEntity.getReffering() );
Assert.assertTrue( refEdEntity.getReffering().isEmpty() );
}
// Many to many tests.
@Test
public void testOwnedManyToManySameRevision() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetOwningEntity.class, false, true )
.add( AuditEntity.id().eq( 5 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 7, getRevisionNumber( objArray[1] ) );
SetOwningEntity setOwningEntity = (SetOwningEntity) objArray[0];
Assert.assertEquals( "Demo Data 1", setOwningEntity.getData() );
Hibernate.initialize( setOwningEntity.getReferences() );
Assert.assertEquals(
TestTools.makeSet( new SetOwnedEntity( 6, "Example Data 1" ) ),
setOwningEntity.getReferences()
);
}
@Test
public void testOwningManyToManySameRevision() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetOwnedEntity.class, false, true )
.add( AuditEntity.id().eq( 6 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 7, getRevisionNumber( objArray[1] ) );
SetOwnedEntity setOwnedEntity = (SetOwnedEntity) objArray[0];
Assert.assertEquals( "Example Data 1", setOwnedEntity.getData() );
Hibernate.initialize( setOwnedEntity.getReferencing() );
Assert.assertEquals(
TestTools.makeSet( new SetOwningEntity( 5, "Demo Data 1" ) ),
setOwnedEntity.getReferencing()
);
}
@Test
public void testOwnedManyToManyDifferentRevisions() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetOwningEntity.class, false, true )
.add( AuditEntity.id().eq( 7 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 9, getRevisionNumber( objArray[1] ) );
SetOwningEntity setOwningEntity = (SetOwningEntity) objArray[0];
Assert.assertEquals( "Demo Data 2", setOwningEntity.getData() );
Hibernate.initialize( setOwningEntity.getReferences() );
Assert.assertEquals(
TestTools.makeSet( new SetOwnedEntity( 8, "Example Data 2" ) ),
setOwningEntity.getReferences()
);
}
@Test
public void testOwningManyToManyDifferentRevisions() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetOwnedEntity.class, false, true )
.add( AuditEntity.id().eq( 8 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 10, getRevisionNumber( objArray[1] ) );
SetOwnedEntity setOwnedEntity = (SetOwnedEntity) objArray[0];
Assert.assertEquals( "Example Data 2", setOwnedEntity.getData() );
Hibernate.initialize( setOwnedEntity.getReferencing() );
Assert.assertTrue( setOwnedEntity.getReferencing().isEmpty() );
// After commit in revision nine, related entity has been removed.
queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetOwnedEntity.class, false, true )
.add( AuditEntity.id().eq( 8 ) )
.add( AuditEntity.revisionNumber().eq( 9 ) )
.getResultList();
objArray = (Object[]) queryResult.get( 0 );
setOwnedEntity = (SetOwnedEntity) objArray[0];
Assert.assertEquals( "Example Data 2", setOwnedEntity.getData() );
Hibernate.initialize( setOwnedEntity.getReferencing() );
Assert.assertTrue( setOwnedEntity.getReferencing().isEmpty() );
}
private Number getRevisionNumber(Object revisionEntity) {
return ( (SequenceIdRevisionEntity) revisionEntity ).getId();
}
}