HHH-5845 - Lazy loading of audited entites with revision type DEL
This commit is contained in:
parent
2daa528cd1
commit
76fe91cf4a
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"})
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -36,6 +36,10 @@ public class MutableInteger {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
public MutableInteger deepCopy() {
|
||||
return new MutableInteger( value );
|
||||
}
|
||||
|
||||
public int getAndIncrease() {
|
||||
return value++;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue