HHH-5372:

applying patch by Matthew B. Jones and Erik-Berndt Scheper
Using the revend column in queries that retrieve historical data, which is much faster then doing the subselect.

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@20315 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Adam Warski 2010-09-07 13:52:38 +00:00
parent 28dae5c9f3
commit 9c919ee9dc
16 changed files with 387 additions and 200 deletions

View File

@ -83,7 +83,7 @@ public class AuditConfiguration {
public AuditConfiguration(Configuration cfg) {
Properties properties = cfg.getProperties();
ReflectionManager reflectionManager = ((AnnotationConfiguration) cfg).getReflectionManager();
ReflectionManager reflectionManager = cfg.getReflectionManager();
RevisionInfoConfiguration revInfoCfg = new RevisionInfoConfiguration();
RevisionInfoConfigurationResult revInfoCfgResult = revInfoCfg.configure(cfg, reflectionManager);
auditEntCfg = new AuditEntitiesConfiguration(properties, revInfoCfgResult.getRevisionInfoEntityName());
@ -99,7 +99,7 @@ public class AuditConfiguration {
revisionInfoQueryCreator = revInfoCfgResult.getRevisionInfoQueryCreator();
revisionInfoNumberReader = revInfoCfgResult.getRevisionInfoNumberReader();
entCfg = new EntitiesConfigurator().configure(cfg, reflectionManager, globalCfg, auditEntCfg,
entCfg = new EntitiesConfigurator().configure(cfg, reflectionManager, globalCfg, auditEntCfg, auditStrategy,
revInfoCfgResult.getRevisionInfoXmlMapping(), revInfoCfgResult.getRevisionInfoRelationMapping());
}

View File

@ -43,6 +43,7 @@ import org.hibernate.envers.configuration.metadata.reader.ClassAuditingData;
import org.hibernate.envers.configuration.metadata.AuditMetadataGenerator;
import org.hibernate.envers.configuration.metadata.AuditEntityNameRegister;
import org.hibernate.envers.entities.EntitiesConfigurations;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.StringTools;
import org.hibernate.envers.tools.graph.GraphTopologicalSort;
@ -57,6 +58,7 @@ import org.hibernate.mapping.PersistentClass;
public class EntitiesConfigurator {
public EntitiesConfigurations configure(Configuration cfg, ReflectionManager reflectionManager,
GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
Document revisionInfoXmlMapping, Element revisionInfoRelationMapping) {
// Creating a name register to capture all audit entity names created.
AuditEntityNameRegister auditEntityNameRegister = new AuditEntityNameRegister();
@ -83,7 +85,7 @@ public class EntitiesConfigurator {
// Now that all information is read we can update the calculated fields.
classesAuditingData.updateCalculatedFields();
AuditMetadataGenerator auditMetaGen = new AuditMetadataGenerator(cfg, globalCfg, verEntCfg,
AuditMetadataGenerator auditMetaGen = new AuditMetadataGenerator(cfg, globalCfg, verEntCfg, auditStrategy,
revisionInfoRelationMapping, auditEntityNameRegister);
// First pass

View File

@ -28,6 +28,7 @@ import java.util.Iterator;
import java.util.Map;
import org.dom4j.Element;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.metadata.reader.ClassAuditingData;
@ -38,6 +39,7 @@ import org.hibernate.envers.entities.mapper.CompositeMapperBuilder;
import org.hibernate.envers.entities.mapper.ExtendedPropertyMapper;
import org.hibernate.envers.entities.mapper.MultiPropertyMapper;
import org.hibernate.envers.entities.mapper.SubclassPropertyMapper;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.strategy.ValidTimeAuditStrategy;
import org.hibernate.envers.tools.StringTools;
import org.hibernate.envers.tools.Triple;
@ -63,6 +65,7 @@ public final class AuditMetadataGenerator {
private final Configuration cfg;
private final GlobalConfiguration globalCfg;
private final AuditEntitiesConfiguration verEntCfg;
private final AuditStrategy auditStrategy;
private final Element revisionInfoRelationMapping;
/*
@ -86,11 +89,13 @@ public final class AuditMetadataGenerator {
public AuditMetadataGenerator(Configuration cfg, GlobalConfiguration globalCfg,
AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
Element revisionInfoRelationMapping,
AuditEntityNameRegister auditEntityNameRegister) {
this.cfg = cfg;
this.globalCfg = globalCfg;
this.verEntCfg = verEntCfg;
this.auditStrategy = auditStrategy;
this.revisionInfoRelationMapping = revisionInfoRelationMapping;
this.basicMetadataGenerator = new BasicMetadataGenerator();
@ -490,6 +495,10 @@ public final class AuditMetadataGenerator {
return verEntCfg;
}
AuditStrategy getAuditStrategy() {
return auditStrategy;
}
AuditEntityNameRegister getAuditEntityNameRegister() {
return auditEntityNameRegister;
}

View File

@ -32,9 +32,11 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.persistence.JoinColumn;
import org.dom4j.Element;
import org.hibernate.MappingException;
import org.hibernate.envers.ModificationStore;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData;
@ -45,8 +47,19 @@ import org.hibernate.envers.entities.mapper.CompositeMapperBuilder;
import org.hibernate.envers.entities.mapper.PropertyMapper;
import org.hibernate.envers.entities.mapper.SinglePropertyMapper;
import org.hibernate.envers.entities.mapper.id.IdMapper;
import org.hibernate.envers.entities.mapper.relation.*;
import org.hibernate.envers.entities.mapper.relation.component.*;
import org.hibernate.envers.entities.mapper.relation.BasicCollectionMapper;
import org.hibernate.envers.entities.mapper.relation.CommonCollectionMapperData;
import org.hibernate.envers.entities.mapper.relation.ListCollectionMapper;
import org.hibernate.envers.entities.mapper.relation.MapCollectionMapper;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.entities.mapper.relation.ToOneIdMapper;
import org.hibernate.envers.entities.mapper.relation.component.MiddleDummyComponentMapper;
import org.hibernate.envers.entities.mapper.relation.component.MiddleMapKeyIdComponentMapper;
import org.hibernate.envers.entities.mapper.relation.component.MiddleMapKeyPropertyComponentMapper;
import org.hibernate.envers.entities.mapper.relation.component.MiddleRelatedComponentMapper;
import org.hibernate.envers.entities.mapper.relation.component.MiddleSimpleComponentMapper;
import org.hibernate.envers.entities.mapper.relation.component.MiddleStraightComponentMapper;
import org.hibernate.envers.entities.mapper.relation.lazy.proxy.ListProxy;
import org.hibernate.envers.entities.mapper.relation.lazy.proxy.MapProxy;
import org.hibernate.envers.entities.mapper.relation.lazy.proxy.SetProxy;
@ -54,11 +67,9 @@ import org.hibernate.envers.entities.mapper.relation.lazy.proxy.SortedMapProxy;
import org.hibernate.envers.entities.mapper.relation.lazy.proxy.SortedSetProxy;
import org.hibernate.envers.entities.mapper.relation.query.OneAuditEntityQueryGenerator;
import org.hibernate.envers.entities.mapper.relation.query.RelationQueryGenerator;
import org.hibernate.envers.tools.MappingTools;
import org.hibernate.envers.tools.StringTools;
import org.hibernate.envers.tools.Tools;
import org.hibernate.envers.tools.MappingTools;
import org.hibernate.MappingException;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.OneToMany;
@ -184,8 +195,8 @@ public final class CollectionMetadataGenerator {
// Generating the query generator - it should read directly from the related entity.
RelationQueryGenerator queryGenerator = new OneAuditEntityQueryGenerator(mainGenerator.getGlobalCfg(),
mainGenerator.getVerEntCfg(), referencingIdData, referencedEntityName,
referencedIdMapping.getIdMapper());
mainGenerator.getVerEntCfg(), mainGenerator.getAuditStrategy(),
referencingIdData, referencedEntityName, referencedIdData);
// Creating common mapper data.
CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData(
@ -333,7 +344,8 @@ public final class CollectionMetadataGenerator {
// references some entities (either from the element or index). At the end, this will be used to build
// a query generator to read the raw data collection from the middle table.
QueryGeneratorBuilder queryGeneratorBuilder = new QueryGeneratorBuilder(mainGenerator.getGlobalCfg(),
mainGenerator.getVerEntCfg(), referencingIdData, auditMiddleEntityName);
mainGenerator.getVerEntCfg(), mainGenerator.getAuditStrategy(), referencingIdData,
auditMiddleEntityName);
// Adding the XML mapping for the referencing entity, if the relation isn't inverse.
if (middleEntityXml != null) {

View File

@ -26,12 +26,17 @@ package org.hibernate.envers.configuration.metadata;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.MappingException;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.entities.mapper.relation.query.*;
import org.hibernate.MappingException;
import org.hibernate.envers.entities.mapper.relation.query.OneEntityQueryGenerator;
import org.hibernate.envers.entities.mapper.relation.query.RelationQueryGenerator;
import org.hibernate.envers.entities.mapper.relation.query.ThreeEntityQueryGenerator;
import org.hibernate.envers.entities.mapper.relation.query.TwoEntityOneAuditedQueryGenerator;
import org.hibernate.envers.entities.mapper.relation.query.TwoEntityQueryGenerator;
import org.hibernate.envers.strategy.AuditStrategy;
/**
* Builds query generators, for reading collection middle tables, along with any related entities.
@ -41,14 +46,17 @@ import org.hibernate.MappingException;
public final class QueryGeneratorBuilder {
private final GlobalConfiguration globalCfg;
private final AuditEntitiesConfiguration verEntCfg;
private final AuditStrategy auditStrategy;
private final MiddleIdData referencingIdData;
private final String auditMiddleEntityName;
private final List<MiddleIdData> idDatas;
QueryGeneratorBuilder(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
MiddleIdData referencingIdData, String auditMiddleEntityName) {
this.globalCfg = globalCfg;
this.verEntCfg = verEntCfg;
this.auditStrategy = auditStrategy;
this.referencingIdData = referencingIdData;
this.auditMiddleEntityName = auditMiddleEntityName;
@ -61,14 +69,14 @@ public final class QueryGeneratorBuilder {
RelationQueryGenerator build(MiddleComponentData... componentDatas) {
if (idDatas.size() == 0) {
return new OneEntityQueryGenerator(verEntCfg, auditMiddleEntityName, referencingIdData,
return new OneEntityQueryGenerator(verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData,
componentDatas);
} else if (idDatas.size() == 1) {
if (idDatas.get(0).isAudited()) {
return new TwoEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData,
return new TwoEntityQueryGenerator(globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData,
idDatas.get(0), componentDatas);
} else {
return new TwoEntityOneAuditedQueryGenerator(verEntCfg, auditMiddleEntityName, referencingIdData,
return new TwoEntityOneAuditedQueryGenerator(verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData,
idDatas.get(0), componentDatas);
}
} else if (idDatas.size() == 2) {
@ -77,7 +85,7 @@ public final class QueryGeneratorBuilder {
throw new MappingException("Ternary relations using @Audited(targetAuditMode = NOT_AUDITED) are not supported.");
}
return new ThreeEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData,
return new ThreeEntityQueryGenerator(globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData,
idDatas.get(0), idDatas.get(1), componentDatas);
} else {
throw new IllegalStateException("Illegal number of related entities.");

View File

@ -25,18 +25,17 @@ 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.GlobalConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.id.IdMapper;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.Query;
/**
* Selects data from an audit entity.
* @author Adam Warski (adam at warski dot org)
@ -46,8 +45,9 @@ public final class OneAuditEntityQueryGenerator implements RelationQueryGenerato
private final MiddleIdData referencingIdData;
public OneAuditEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg,
MiddleIdData referencingIdData, String referencedEntityName,
IdMapper referencedIdMapper) {
AuditStrategy auditStrategy,
MiddleIdData referencingIdData,
String referencedEntityName, MiddleIdData referencedIdData) {
this.referencingIdData = referencingIdData;
/*
@ -57,8 +57,14 @@ public final class OneAuditEntityQueryGenerator implements RelationQueryGenerato
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* e.id_ref_ing = :id_ref_ing AND
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id = e.id) AND
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidTimeAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
* (only non-deleted entities)
* e.revision_type != DEL
*/
@ -75,19 +81,11 @@ public final class OneAuditEntityQueryGenerator implements RelationQueryGenerato
// e.id_ref_ed = :id_ref_ed
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, null, true);
// SELECT max(e.revision) FROM versionsReferencedEntity e2
QueryBuilder maxERevQb = qb.newSubQueryBuilder(versionsReferencedEntityName, "e2");
maxERevQb.addProjection("max", revisionPropertyPath, false);
// WHERE
Parameters maxERevQbParameters = maxERevQb.getRootParameters();
// e2.revision <= :revision
maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
// e2.id = e.id
referencedIdMapper.addIdsEqualToQuery(maxERevQbParameters,
"e." + originalIdPropertyName, "e2." + originalIdPropertyName);
// e.revision = (SELECT max(...) ...)
rootParameters.addWhere(revisionPropertyPath, false, globalCfg.getCorrelatedSubqueryOperator(), maxERevQb);
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(globalCfg, qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencedIdData,
revisionPropertyPath, originalIdPropertyName, "e", "e2");
// e.revision_type != DEL
rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), false, "!=", "delrevisiontype");

View File

@ -25,17 +25,17 @@ package org.hibernate.envers.entities.mapper.relation.query;
import java.util.Collections;
import org.hibernate.Query;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.Query;
/**
* Selects data from a relation middle-table only.
* @author Adam Warski (adam at warski dot org)
@ -45,6 +45,7 @@ public final class OneEntityQueryGenerator implements RelationQueryGenerator {
private final MiddleIdData referencingIdData;
public OneEntityQueryGenerator(AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleComponentData... componentDatas) {
@ -55,9 +56,17 @@ public final class OneEntityQueryGenerator implements RelationQueryGenerator {
* SELECT new list(ee) FROM middleEntity ee WHERE
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.originalId.id_ref_ing = :id_ref_ing AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) AND
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidTimeAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* AND
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL
*/
@ -71,22 +80,15 @@ public final class OneEntityQueryGenerator implements RelationQueryGenerator {
Parameters rootParameters = qb.getRootParameters();
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
// SELECT max(ee2.revision) FROM middleEntity ee2
QueryBuilder maxRevQb = qb.newSubQueryBuilder(versionsMiddleEntityName, "ee2");
maxRevQb.addProjection("max", revisionPropertyPath, false);
// WHERE
Parameters maxRevQbParameters = maxRevQb.getRootParameters();
// ee2.revision <= :revision
maxRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
// ee2.originalId.* = ee.originalId.*
String eeOriginalIdPropertyPath = "ee." + originalIdPropertyName;
String ee2OriginalIdPropertyPath = "ee2." + originalIdPropertyName;
referencingIdData.getPrefixedMapper().addIdsEqualToQuery(maxRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
for (MiddleComponentData componentData : componentDatas) {
componentData.getComponentMapper().addMiddleEqualToQuery(maxRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
}
// ee.revision = (SELECT max(...) ...)
rootParameters.addWhere(revisionPropertyPath, "=", maxRevQb);
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true,referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas);
// ee.revision_type != DEL
rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", "delrevisiontype");

View File

@ -1,75 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.entities.mapper.relation.query;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
/**
* @author Adam Warski (adam at warski dot org)
*/
public class QueryGeneratorTools {
public static void addEntityAtRevision(GlobalConfiguration globalCfg, QueryBuilder qb, Parameters rootParameters,
MiddleIdData idData, String revisionPropertyPath, String originalIdPropertyName,
String alias1, String alias2) {
// SELECT max(e.revision) FROM versionsReferencedEntity e2
QueryBuilder maxERevQb = qb.newSubQueryBuilder(idData.getAuditEntityName(), alias2);
maxERevQb.addProjection("max", revisionPropertyPath, false);
// WHERE
Parameters maxERevQbParameters = maxERevQb.getRootParameters();
// e2.revision <= :revision
maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
// e2.id_ref_ed = e.id_ref_ed
idData.getOriginalMapper().addIdsEqualToQuery(maxERevQbParameters,
alias1 + "." + originalIdPropertyName, alias2 +"." + originalIdPropertyName);
// e.revision = (SELECT max(...) ...)
rootParameters.addWhere("e." + revisionPropertyPath, false, globalCfg.getCorrelatedSubqueryOperator(), maxERevQb);
}
public static void addAssociationAtRevision(QueryBuilder qb, Parameters rootParameters,
MiddleIdData referencingIdData, String versionsMiddleEntityName,
String eeOriginalIdPropertyPath, String revisionPropertyPath,
String originalIdPropertyName, MiddleComponentData... componentDatas) {
// SELECT max(ee2.revision) FROM middleEntity ee2
QueryBuilder maxEeRevQb = qb.newSubQueryBuilder(versionsMiddleEntityName, "ee2");
maxEeRevQb.addProjection("max", revisionPropertyPath, false);
// WHERE
Parameters maxEeRevQbParameters = maxEeRevQb.getRootParameters();
// ee2.revision <= :revision
maxEeRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
// ee2.originalId.* = ee.originalId.*
String ee2OriginalIdPropertyPath = "ee2." + originalIdPropertyName;
referencingIdData.getPrefixedMapper().addIdsEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
for (MiddleComponentData componentData : componentDatas) {
componentData.getComponentMapper().addMiddleEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
}
// ee.revision = (SELECT max(...) ...)
rootParameters.addWhere(revisionPropertyPath, "=", maxEeRevQb);
}
}

View File

@ -25,18 +25,18 @@ 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.GlobalConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.Query;
/**
* Selects data from a relation middle-table and a two related versions entity.
* @author Adam Warski (adam at warski dot org)
@ -47,6 +47,7 @@ public final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
public ThreeEntityQueryGenerator(GlobalConfiguration globalCfg,
AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
@ -65,14 +66,48 @@ public final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id_ref_ed = e.id_ref_ed) AND
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidTimeAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
*
* (selecting f entities at revision :revision)
* --> for DefaultAuditStrategy:
* f.revision = (SELECT max(f2.revision) FROM versionsIndexEntity f2
* WHERE f2.revision <= :revision AND f2.id_ref_ed = f.id_ref_ed) AND
* WHERE f2.revision <= :revision AND f2.id_ref_ed = f.id_ref_ed)
*
* --> for ValidTimeAuditStrategy:
* f.revision <= :revision and (f.endRevision > :revision or f.endRevision is null)
*
* AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) AND
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidTimeAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
and (
strtestent1_.REVEND>?
or strtestent1_.REVEND is null
)
and (
strtestent1_.REVEND>?
or strtestent1_.REVEND is null
)
and (
ternarymap0_.REVEND>?
or ternarymap0_.REVEND is null
)
*
*
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL AND
* e.revision_type != DEL AND
@ -80,7 +115,6 @@ public final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
*/
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
String eeOriginalIdPropertyPath = "ee." + originalIdPropertyName;
// SELECT new list(ee) FROM middleEntity ee
@ -99,16 +133,22 @@ public final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
// e.revision = (SELECT max(...) ...)
QueryGeneratorTools.addEntityAtRevision(globalCfg, qb, rootParameters, referencedIdData, revisionPropertyPath,
originalIdPropertyName, "e", "e2");
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(globalCfg, qb, "e." + revisionPropertyPath,
"e." + verEntCfg.getRevisionEndFieldName(), false,
referencedIdData, revisionPropertyPath, originalIdPropertyName, "e", "e2");
// f.revision = (SELECT max(...) ...)
QueryGeneratorTools.addEntityAtRevision(globalCfg, qb, rootParameters, indexIdData, revisionPropertyPath,
originalIdPropertyName, "f", "f2");
// (selecting f entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(globalCfg, qb, "e." + revisionPropertyPath,
"e." + verEntCfg.getRevisionEndFieldName(), false,
referencedIdData, revisionPropertyPath, originalIdPropertyName, "f", "f2");
// ee.revision = (SELECT max(...) ...)
QueryGeneratorTools.addAssociationAtRevision(qb, rootParameters, referencingIdData, versionsMiddleEntityName,
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas);
// ee.revision_type != DEL

View File

@ -26,11 +26,14 @@ package org.hibernate.envers.entities.mapper.relation.query;
import java.util.Collections;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
@ -44,8 +47,7 @@ public final class TwoEntityOneAuditedQueryGenerator implements RelationQueryGen
private final String queryString;
private final MiddleIdData referencingIdData;
public TwoEntityOneAuditedQueryGenerator(
AuditEntitiesConfiguration verEntCfg,
public TwoEntityOneAuditedQueryGenerator(AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
@ -60,9 +62,17 @@ public final class TwoEntityOneAuditedQueryGenerator implements RelationQueryGen
* ee.id_ref_ed = e.id_ref_ed AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) AND
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidTimeAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* AND
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL
*/
@ -83,8 +93,10 @@ public final class TwoEntityOneAuditedQueryGenerator implements RelationQueryGen
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
// ee.revision = (SELECT max(...) ...)
QueryGeneratorTools.addAssociationAtRevision(qb, rootParameters, referencingIdData, versionsMiddleEntityName,
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true,referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas);
// ee.revision_type != DEL

View File

@ -25,18 +25,18 @@ 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.GlobalConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.Query;
/**
* Selects data from a relation middle-table and a related versions entity.
* @author Adam Warski (adam at warski dot org)
@ -47,6 +47,7 @@ public final class TwoEntityQueryGenerator implements RelationQueryGenerator {
public TwoEntityQueryGenerator(GlobalConfiguration globalCfg,
AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
@ -61,12 +62,25 @@ public final class TwoEntityQueryGenerator implements RelationQueryGenerator {
* ee.id_ref_ed = e.id_ref_ed AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
*
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id_ref_ed = e.id_ref_ed) AND
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidTimeAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
*
* (the association at revision :revision)
* --> for DefaultAuditStrategy:
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) AND
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*)
*
* --> for ValidTimeAuditStrategy:
* ee.revision <= :revision and (ee.endRevision > :revision or ee.endRevision is null)
*
* (only non-deleted entities and associations)
* ee.revision_type != DEL AND
* e.revision_type != DEL
@ -88,12 +102,16 @@ public final class TwoEntityQueryGenerator implements RelationQueryGenerator {
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
// e.revision = (SELECT max(...) ...)
QueryGeneratorTools.addEntityAtRevision(globalCfg, qb, rootParameters, referencedIdData, revisionPropertyPath,
originalIdPropertyName, "e", "e2");
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction(globalCfg, qb, "e." + revisionPropertyPath,
"e." + verEntCfg.getRevisionEndFieldName(), false,
referencedIdData, revisionPropertyPath, originalIdPropertyName, "e", "e2");
// ee.revision = (SELECT max(...) ...)
QueryGeneratorTools.addAssociationAtRevision(qb, rootParameters, referencingIdData, versionsMiddleEntityName,
// (with ee association at revision :revision)
// --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas);
// ee.revision_type != DEL

View File

@ -86,10 +86,14 @@ public abstract class AbstractAuditQuery implements AuditQuery {
qb = new QueryBuilder(versionsEntityName, "e");
}
protected List buildAndExecuteQuery() {
protected Query buildQuery() {
Query query = qb.toQuery(versionsReader.getSession());
setQueryProperties(query);
return query;
}
protected List buildAndExecuteQuery() {
Query query = buildQuery();
return query.list();
}

View File

@ -24,14 +24,16 @@
package org.hibernate.envers.query.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.query.criteria.AuditCriterion;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.tools.query.QueryBuilder;
/**
* @author Adam Warski (adam at warski dot org)
@ -56,39 +58,50 @@ public class EntitiesAtRevisionQuery extends AbstractAuditQuery {
@SuppressWarnings({"unchecked"})
public List list() {
/*
The query that should be executed in the versions table:
SELECT e FROM ent_ver e WHERE
(all specified conditions, transformed, on the "e" entity) AND
e.revision_type != DEL AND
e.revision = (SELECT max(e2.revision) FROM ent_ver e2 WHERE
e2.revision <= :revision AND e2.originalId.id = e.originalId.id)
* The query that we need to create:
* SELECT new list(e) FROM versionsReferencedEntity e
* WHERE
* (all specified conditions, transformed, on the "e" entity) AND
* (selecting e entities at revision :revision)
* --> for DefaultAuditStrategy:
* e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
* WHERE e2.revision <= :revision AND e2.id = e.id)
*
* --> for ValidTimeAuditStrategy:
* e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)
*
* AND
* (only non-deleted entities)
* e.revision_type != DEL
*/
QueryBuilder maxRevQb = qb.newSubQueryBuilder(versionsEntityName, "e2");
AuditEntitiesConfiguration verEntCfg = verCfg.getAuditEntCfg();
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
// SELECT max(e2.revision)
maxRevQb.addProjection("max", revisionPropertyPath, false);
// e2.revision <= :revision
maxRevQb.getRootParameters().addWhereWithParam(revisionPropertyPath, "<=", revision);
// e2.id = e.id
verCfg.getEntCfg().get(entityName).getIdMapper().addIdsEqualToQuery(maxRevQb.getRootParameters(),
"e." + originalIdPropertyName, "e2." + originalIdPropertyName);
MiddleIdData referencedIdData = new MiddleIdData(verEntCfg, verCfg.getEntCfg().get(entityName).getIdMappingData(),
null, entityName, verCfg.getEntCfg().isVersioned(entityName));
// e.revision_type != DEL AND
// (selecting e entities at revision :revision)
// --> based on auditStrategy (see above)
verCfg.getAuditStrategy().addEntityAtRevisionRestriction(verCfg.getGlobalCfg(), qb, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencedIdData,
revisionPropertyPath, originalIdPropertyName, "e", "e2");
// e.revision_type != DEL
qb.getRootParameters().addWhereWithParam(verEntCfg.getRevisionTypePropName(), "<>", RevisionType.DEL);
// e.revision = (SELECT max(...) ...)
qb.getRootParameters().addWhere(revisionPropertyPath, verCfg.getGlobalCfg().getCorrelatedSubqueryOperator(), maxRevQb);
// all specified conditions
for (AuditCriterion criterion : criterions) {
criterion.addToQuery(verCfg, entityName, qb, qb.getRootParameters());
}
List queryResult = buildAndExecuteQuery();
Query query = buildQuery();
// add named parameter (only used for ValidAuditTimeStrategy)
List<String> params = Arrays.asList(query.getNamedParameters());
if (params.contains("revision")) {
query.setParameter("revision", revision);
}
List queryResult = query.list();
if (hasProjection) {
return queryResult;

View File

@ -1,10 +1,14 @@
package org.hibernate.envers.strategy;
import java.io.Serializable;
import org.hibernate.Session;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
import java.io.Serializable;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.tools.query.QueryBuilder;
/**
* Behaviours of different audit strategy for populating audit data.
@ -36,4 +40,61 @@ public interface AuditStrategy {
*/
void performCollectionChange(Session session, AuditConfiguration auditCfg,
PersistentCollectionChangeData persistentCollectionChangeData, Object revision);
/**
* Update the rootQueryBuilder with an extra WHERE clause to restrict the revision for a two-entity relation.
* This WHERE clause depends on the AuditStrategy, as follows:
* <ul>
* <li>For {@link DefaultAuditStrategy} a subquery is created:
* <p><code>e.revision = (SELECT max(...) ...)</code></p>
* </li>
* <li>for {@link ValidTimeAuditStrategy} the revision-end column is used:
* <p><code>e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)</code></p>
* </li>
* </ul>
*
* @param globalCfg the {@link GlobalConfiguration}
* @param rootQueryBuilder the {@link QueryBuilder} that will be updated
* @param revisionProperty property of the revision column
* @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidTimeAuditStrategy})
* @param addAlias {@code boolean} indicator if a left alias is needed
* @param idData id-information for the two-entity relation (only used for {@link DefaultAuditStrategy})
* @param revisionPropertyPath path of the revision property (only used for {@link ValidTimeAuditStrategy})
* @param originalIdPropertyName name of the id property (only used for {@link ValidTimeAuditStrategy})
* @param alias1 an alias used for subquery (only used for {@link ValidTimeAuditStrategy})
* @param alias2 an alias used for subquery (only used for {@link ValidTimeAuditStrategy})
*/
void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder,
String revisionProperty, String revisionEndProperty, boolean addAlias, MiddleIdData idData,
String revisionPropertyPath, String originalIdPropertyName, String alias1, String alias2);
/**
* Update the rootQueryBuilder with an extra WHERE clause to restrict the revision for a middle-entity
* association. This WHERE clause depends on the AuditStrategy, as follows:
* <ul>
* <li>For {@link DefaultAuditStrategy} a subquery is created:
* <p><code>e.revision = (SELECT max(...) ...)</code></p>
* </li>
* <li>for {@link ValidTimeAuditStrategy} the revision-end column is used:
* <p><code>e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)</code></p>
* </li>
* </ul>
*
* @param rootQueryBuilder the {@link QueryBuilder} that will be updated
* @param revisionProperty property of the revision column
* @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidTimeAuditStrategy})
* @param addAlias {@code boolean} indicator if a left alias is needed
* @param referencingIdData id-information for the middle-entity association (only used for {@link DefaultAuditStrategy})
* @param versionsMiddleEntityName name of the middle-entity
* @param eeOriginalIdPropertyPath name of the id property (only used for {@link ValidTimeAuditStrategy})
* @param revisionPropertyPath path of the revision property (only used for {@link ValidTimeAuditStrategy})
* @param originalIdPropertyName name of the id property (only used for {@link ValidTimeAuditStrategy})
* @param componentDatas information about the middle-entity relation
*/
void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty,
String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData,
String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath,
String originalIdPropertyName, MiddleComponentData... componentDatas);
}

View File

@ -1,10 +1,15 @@
package org.hibernate.envers.strategy;
import java.io.Serializable;
import org.hibernate.Session;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
import java.io.Serializable;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
/**
* Default strategy is to simply persist the audit data.
@ -22,4 +27,52 @@ public class DefaultAuditStrategy implements AuditStrategy {
PersistentCollectionChangeData persistentCollectionChangeData, Object revision) {
session.save(persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData());
}
public void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder, String revisionProperty,
String revisionEndProperty, boolean addAlias, MiddleIdData idData, String revisionPropertyPath,
String originalIdPropertyName, String alias1, String alias2) {
Parameters rootParameters = rootQueryBuilder.getRootParameters();
// create a subquery builder
// SELECT max(e.revision) FROM versionsReferencedEntity e2
QueryBuilder maxERevQb = rootQueryBuilder.newSubQueryBuilder(idData.getAuditEntityName(), alias2);
maxERevQb.addProjection("max", revisionPropertyPath, false);
// WHERE
Parameters maxERevQbParameters = maxERevQb.getRootParameters();
// e2.revision <= :revision
maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
// e2.id_ref_ed = e.id_ref_ed
idData.getOriginalMapper().addIdsEqualToQuery(maxERevQbParameters,
alias1 + "." + originalIdPropertyName, alias2 +"." + originalIdPropertyName);
// add subquery to rootParameters
String subqueryOperator = globalCfg.getCorrelatedSubqueryOperator();
rootParameters.addWhere(revisionProperty, addAlias, subqueryOperator, maxERevQb);
}
public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty,
String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData, String versionsMiddleEntityName,
String eeOriginalIdPropertyPath, String revisionPropertyPath,
String originalIdPropertyName, MiddleComponentData... componentDatas) {
Parameters rootParameters = rootQueryBuilder.getRootParameters();
// SELECT max(ee2.revision) FROM middleEntity ee2
QueryBuilder maxEeRevQb = rootQueryBuilder.newSubQueryBuilder(versionsMiddleEntityName, "ee2");
maxEeRevQb.addProjection("max", revisionPropertyPath, false);
// WHERE
Parameters maxEeRevQbParameters = maxEeRevQb.getRootParameters();
// ee2.revision <= :revision
maxEeRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
// ee2.originalId.* = ee.originalId.*
String ee2OriginalIdPropertyPath = "ee2." + originalIdPropertyName;
referencingIdData.getPrefixedMapper().addIdsEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
for (MiddleComponentData componentData : componentDatas) {
componentData.getComponentMapper().addMiddleEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
}
// add subquery to rootParameters
rootParameters.addWhere(revisionProperty, addAlias, "=", maxEeRevQb);
}
}

View File

@ -8,8 +8,12 @@ import org.hibernate.Session;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
import org.hibernate.envers.entities.mapper.id.IdMapper;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
/**
@ -75,6 +79,32 @@ public class ValidTimeAuditStrategy implements AuditStrategy {
session.save(persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData());
}
public void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder,
String revisionProperty,String revisionEndProperty, boolean addAlias,
MiddleIdData idData, String revisionPropertyPath, String originalIdPropertyName,
String alias1, String alias2) {
Parameters rootParameters = rootQueryBuilder.getRootParameters();
addRevisionRestriction(rootParameters, revisionProperty, revisionEndProperty, addAlias);
}
public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty,
String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData,
String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath,
String originalIdPropertyName, MiddleComponentData... componentDatas) {
Parameters rootParameters = rootQueryBuilder.getRootParameters();
addRevisionRestriction(rootParameters, revisionProperty, revisionEndProperty, addAlias);
}
private void addRevisionRestriction(Parameters rootParameters,
String revisionProperty, String revisionEndProperty, boolean addAlias) {
// e.revision <= _revision and (e.endRevision > _revision or e.endRevision is null)
Parameters subParm = rootParameters.addSubParameters("or");
rootParameters.addWhereWithNamedParam(revisionProperty, addAlias, "<=", "revision");
subParm.addWhereWithNamedParam(revisionEndProperty + ".id", addAlias, ">", "revision");
subParm.addWhere(revisionEndProperty, addAlias, "is", "null", false);
}
@SuppressWarnings({"unchecked"})
private RevisionType getRevisionType(AuditConfiguration auditCfg, Object data) {
return (RevisionType) ((Map<String, Object>) data).get(auditCfg.getAuditEntCfg().getRevisionTypePropName());