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

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

View File

@ -200,14 +200,22 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
protected abstract Initializor<T> getInitializor(AuditConfiguration verCfg, protected abstract Initializor<T> getInitializor(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader, Object primaryKey, AuditReaderImplementor versionsReader, Object primaryKey,
Number revision); Number revision, boolean removed);
public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey, public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
AuditReaderImplementor versionsReader, Number revision) { AuditReaderImplementor versionsReader, Number revision) {
Setter setter = ReflectionTools.getSetter(obj.getClass(), Setter setter = ReflectionTools.getSetter(obj.getClass(), commonCollectionMapperData.getCollectionReferencingPropertyData());
commonCollectionMapperData.getCollectionReferencingPropertyData());
try { 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) { } catch (InstantiationException e) {
throw new AuditException(e); throw new AuditException(e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,18 +23,12 @@
*/ */
package org.hibernate.envers.entities.mapper.relation.query; 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.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData; import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData; 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.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder; 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.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS; 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. * Selects data from a relation middle-table only.
*
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/ */
public final class OneEntityQueryGenerator extends AbstractRelationQueryGenerator { public final class OneEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString; private final String queryString;
private final String queryRemovedString;
public OneEntityQueryGenerator(AuditEntitiesConfiguration verEntCfg, public OneEntityQueryGenerator(AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy,
AuditStrategy auditStrategy, String versionsMiddleEntityName, MiddleIdData referencingIdData,
String versionsMiddleEntityName, boolean revisionTypeInId, MiddleComponentData... componentData) {
MiddleIdData referencingIdData,
boolean revisionTypeInId,
MiddleComponentData... componentDatas) {
super( verEntCfg, referencingIdData, revisionTypeInId ); super( verEntCfg, referencingIdData, revisionTypeInId );
/* /*
* The query that we need to create: * The valid query that we need to create:
* SELECT ee FROM middleEntity ee WHERE * SELECT ee FROM middleEntity ee WHERE
* (only entities referenced by the association; id_ref_ing = id of the referencing entity) * (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.originalId.id_ref_ing = :id_ref_ing AND * ee.originalId.id_ref_ing = :id_ref_ing AND
* *
* (the association at revision :revision) * (the association at revision :revision)
* --> for DefaultAuditStrategy: * --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2 * ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) * WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
* *
* --> for ValidityAuditStrategy: * --> for ValidityAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null) * ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
* *
* AND * AND
* *
* (only non-deleted entities and associations) * (only non-deleted entities and associations)
* ee.revision_type != DEL * ee.revision_type != DEL
*/ */
String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final QueryBuilder commonPart = commonQueryPart( versionsMiddleEntityName );
String originalIdPropertyName = verEntCfg.getOriginalIdPropName(); 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 queryString = queryToString( validQuery );
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS); queryRemovedString = queryToString( removedQuery );
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; /**
* 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;
}
// (with ee association at revision :revision) /**
// --> based on auditStrategy (see above) * Creates query restrictions used to retrieve only actual data.
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath, */
verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName, private void createValidDataRestrictions(AuditStrategy auditStrategy, String versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas); 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 );
}
// 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.
*/
StringBuilder sb = new StringBuilder(); private void createValidAndRemovedDataRestrictions(AuditStrategy auditStrategy, String versionsMiddleEntityName,
qb.build(sb, Collections.<String, Object>emptyMap()); QueryBuilder remQb, MiddleComponentData... componentData) {
queryString = sb.toString(); 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 @Override
protected String getQueryString() { protected String getQueryString() {
return queryString; return queryString;
} }
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData; import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData; import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder; import org.hibernate.envers.tools.query.QueryBuilder;
/** /**
@ -59,6 +60,7 @@ public interface AuditStrategy {
* *
* @param globalCfg the {@link GlobalConfiguration} * @param globalCfg the {@link GlobalConfiguration}
* @param rootQueryBuilder the {@link QueryBuilder} that will be updated * @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 revisionProperty property of the revision column
* @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy}) * @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy})
* @param addAlias {@code boolean} indicator if a left alias is needed * @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 originalIdPropertyName name of the id property (only used for {@link ValidityAuditStrategy})
* @param alias1 an alias used for subquery (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 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 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 * Update the rootQueryBuilder with an extra WHERE clause to restrict the revision for a middle-entity
@ -85,6 +88,7 @@ public interface AuditStrategy {
* </ul> * </ul>
* *
* @param rootQueryBuilder the {@link QueryBuilder} that will be updated * @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 revisionProperty property of the revision column
* @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy}) * @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy})
* @param addAlias {@code boolean} indicator if a left alias is needed * @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 revisionPropertyPath path of the revision property (only used for {@link ValidityAuditStrategy})
* @param originalIdPropertyName name of the id 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 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 * @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 revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData,
String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath, String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath,
String originalIdPropertyName, String alias1, MiddleComponentData... componentDatas); String originalIdPropertyName, String alias1, boolean inclusive, MiddleComponentData... componentDatas);
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -79,6 +79,28 @@ public class Parameters {
localQueryParamValues = new HashMap<String, Object>(); 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() { private String generateQueryParam() {
return "_p" + queryParamCounter.getAndIncrease(); return "_p" + queryParamCounter.getAndIncrease();
} }

View File

@ -92,6 +92,23 @@ public class QueryBuilder {
addFrom(entityName, alias); 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. * Add an entity from which to select.
* @param entityName Name of the entity from which to select. * @param entityName Name of the entity from which to select.

View File

@ -1,83 +1,444 @@
package org.hibernate.envers.test.integration.proxy; package org.hibernate.envers.test.integration.proxy;
import org.hibernate.Hibernate; import java.util.HashSet;
import org.hibernate.envers.RevisionType; import java.util.List;
import org.hibernate.envers.query.AuditEntity; import java.util.Map;
import org.hibernate.envers.query.AuditQuery; import java.util.Set;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; import javax.persistence.EntityManager;
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 org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.util.List; import org.hibernate.Hibernate;
import java.util.Map; import org.hibernate.envers.RevisionType;
import org.hibernate.envers.enhanced.SequenceIdRevisionEntity;
import javax.persistence.EntityManager; 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") @TestForIssue(jiraKey = "HHH-5845")
public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase { public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase {
@Override private Integer stringSetId = null;
@SuppressWarnings("unchecked") private Integer ternaryMapId = null;
protected void addConfigOptions(Map options) { private UnversionedStrTestEntity unversionedEntity1 = null;
options.put("org.hibernate.envers.store_data_at_delete", "true"); private UnversionedStrTestEntity unversionedEntity2 = null;
} private StrTestPrivSeqEntity stringEntity1 = null;
private StrTestPrivSeqEntity stringEntity2 = null;
private IntTestPrivSeqEntity intEntity1 = null;
private IntTestPrivSeqEntity intEntity2 = null;
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected void addConfigOptions(Map options) {
return new Class<?>[] { SetRefEdEntity.class, SetRefIngEntity.class }; super.addConfigOptions( options );
} options.put( "org.hibernate.envers.store_data_at_delete", "true" );
}
@Test @Override
@Priority(10) protected Class<?>[] getAnnotatedClasses() {
public void initData() { return new Class<?>[] {
EntityManager em = getEntityManager(); 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"); @Test
SetRefIngEntity refIngEntity = new SetRefIngEntity(2, "Example Data", refEdEntity); @Priority(10)
public void initData() {
EntityManager em = getEntityManager();
em.getTransaction().begin(); SetRefEdEntity refEdEntity1 = new SetRefEdEntity( 1, "Demo Data 1" );
em.persist(refEdEntity); SetRefIngEntity refIngEntity1 = new SetRefIngEntity( 2, "Example Data 1", refEdEntity1 );
em.persist(refIngEntity);
em.getTransaction().commit();
em.getTransaction().begin(); // Revision 1
refIngEntity = em.find(SetRefIngEntity.class, 2); em.getTransaction().begin();
em.remove(refIngEntity); em.persist( refEdEntity1 );
em.remove(refEdEntity); em.persist( refIngEntity1 );
em.getTransaction().commit(); em.getTransaction().commit();
}
@Test // Revision 2 - removing both object in the same revision
public void testFindDeletedReference() { em.getTransaction().begin();
AuditQuery query = getAuditReader().createQuery().forRevisionsOfEntity(SetRefIngEntity.class, false, true) refEdEntity1 = em.find( SetRefEdEntity.class, 1 );
.add(AuditEntity.revisionType().eq(RevisionType.DEL)); refIngEntity1 = em.find( SetRefIngEntity.class, 2 );
List queryResult = (List) query.getResultList(); em.remove( refIngEntity1 );
em.remove( refEdEntity1 );
em.getTransaction().commit();
Object[] objArray = (Object[]) queryResult.get(0); SetRefEdEntity refEdEntity2 = new SetRefEdEntity( 3, "Demo Data 2" );
SetRefIngEntity refIngEntity = (SetRefIngEntity) objArray[0]; SetRefIngEntity refIngEntity2 = new SetRefIngEntity( 4, "Example Data 2", refEdEntity2 );
Assert.assertEquals("Example Data", refIngEntity.getData());
Hibernate.initialize(refIngEntity.getReference()); // Revision 3
Assert.assertEquals("Demo Data", refIngEntity.getReference().getData()); 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 // Revision 4 - removing child object
@Test em.getTransaction().begin();
public void testFindDeletedReferring() { refIngEntity2 = em.find( SetRefIngEntity.class, 4 );
AuditQuery query = getAuditReader().createQuery().forRevisionsOfEntity(SetRefEdEntity.class, false, true) em.remove( refIngEntity2 );
.add(AuditEntity.revisionType().eq(RevisionType.DEL)); em.getTransaction().commit();
List queryResult = (List) query.getResultList();
Object[] objArray = (Object[]) queryResult.get(0); // Revision 5 - removing parent object
SetRefEdEntity refEdEntity = (SetRefEdEntity) objArray[0]; em.getTransaction().begin();
Assert.assertEquals("Demo Data", refEdEntity.getData()); refEdEntity2 = em.find( SetRefEdEntity.class, 3 );
em.remove( refEdEntity2 );
em.getTransaction().commit();
Hibernate.initialize(refEdEntity.getReffering()); SetOwningEntity setOwningEntity1 = new SetOwningEntity( 5, "Demo Data 1" );
Assert.assertEquals(TestTools.makeSet(new SetRefIngEntity(2, "Example Data")), refEdEntity.getReffering()); 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();
}
} }