HHH-12992 - Fix proper support for order-by annotation on audited entity associations.

This commit is contained in:
Chris Cranford 2018-10-04 11:13:18 -04:00
parent 334e064272
commit eff27db90a
13 changed files with 498 additions and 329 deletions

View File

@ -64,6 +64,7 @@
import org.hibernate.envers.internal.tools.ReflectionTools; import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.envers.internal.tools.StringTools; import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.envers.internal.tools.Tools; import org.hibernate.envers.internal.tools.Tools;
import org.hibernate.mapping.Bag;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.IndexedCollection; import org.hibernate.mapping.IndexedCollection;
@ -237,7 +238,8 @@ private void addOneToManyAttached(boolean fakeOneToManyBidirectional) {
referencedIdData, referencedIdData,
isEmbeddableElementType(), isEmbeddableElementType(),
mappedBy, mappedBy,
isMappedByKey( propertyValue, mappedBy ) isMappedByKey( propertyValue, mappedBy ),
propertyValue.getOrderBy()
); );
// Creating common mapper data. // Creating common mapper data.
@ -290,7 +292,15 @@ else if ( indexed ) {
// Checking if there's an index defined. If so, adding a mapper for it. // Checking if there's an index defined. If so, adding a mapper for it.
if ( positionMappedBy != null ) { if ( positionMappedBy != null ) {
final Type indexType = ( (IndexedCollection) propertyValue ).getIndex().getType(); final Type indexType;
if ( IndexedCollection.class.isInstance( propertyValue ) ) {
indexType = ( (IndexedCollection) propertyValue ).getIndex().getType();
}
else {
// todo - do we need to reverse lookup the type anyway?
indexType = null;
}
fakeBidirectionalRelationIndexMapper = new SinglePropertyMapper( fakeBidirectionalRelationIndexMapper = new SinglePropertyMapper(
PropertyData.forProperty( positionMappedBy, indexType ) PropertyData.forProperty( positionMappedBy, indexType )
); );
@ -451,7 +461,10 @@ private void addWithMiddleTable() {
mainGenerator.getAuditStrategy(), mainGenerator.getAuditStrategy(),
referencingIdData, referencingIdData,
auditMiddleEntityName, auditMiddleEntityName,
isRevisionTypeInId() isRevisionTypeInId(),
propertyValue.getOrderBy() == null
? propertyValue.getManyToManyOrdering()
: propertyValue.getOrderBy()
); );
// Adding the XML mapping for the referencing entity, if the relation isn't inverse. // Adding the XML mapping for the referencing entity, if the relation isn't inverse.

View File

@ -36,6 +36,7 @@ public final class QueryGeneratorBuilder {
private final String auditMiddleEntityName; private final String auditMiddleEntityName;
private final List<MiddleIdData> idDatas; private final List<MiddleIdData> idDatas;
private final boolean revisionTypeInId; private final boolean revisionTypeInId;
private final String orderBy;
QueryGeneratorBuilder( QueryGeneratorBuilder(
GlobalConfiguration globalCfg, GlobalConfiguration globalCfg,
@ -43,12 +44,14 @@ public final class QueryGeneratorBuilder {
AuditStrategy auditStrategy, AuditStrategy auditStrategy,
MiddleIdData referencingIdData, MiddleIdData referencingIdData,
String auditMiddleEntityName, String auditMiddleEntityName,
boolean revisionTypeInId) { boolean revisionTypeInId,
String orderBy) {
this.globalCfg = globalCfg; this.globalCfg = globalCfg;
this.verEntCfg = verEntCfg; this.verEntCfg = verEntCfg;
this.auditStrategy = auditStrategy; this.auditStrategy = auditStrategy;
this.referencingIdData = referencingIdData; this.referencingIdData = referencingIdData;
this.auditMiddleEntityName = auditMiddleEntityName; this.auditMiddleEntityName = auditMiddleEntityName;
this.orderBy = orderBy;
this.revisionTypeInId = revisionTypeInId; this.revisionTypeInId = revisionTypeInId;
idDatas = new ArrayList<>(); idDatas = new ArrayList<>();
@ -61,7 +64,7 @@ void addRelation(MiddleIdData idData) {
RelationQueryGenerator build(MiddleComponentData... componentDatas) { RelationQueryGenerator build(MiddleComponentData... componentDatas) {
if ( idDatas.size() == 0 ) { if ( idDatas.size() == 0 ) {
return new OneEntityQueryGenerator( return new OneEntityQueryGenerator(
verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData, globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData,
revisionTypeInId, componentDatas revisionTypeInId, componentDatas
); );
} }
@ -69,13 +72,13 @@ else if ( idDatas.size() == 1 ) {
if ( idDatas.get( 0 ).isAudited() ) { if ( idDatas.get( 0 ).isAudited() ) {
return new TwoEntityQueryGenerator( return new TwoEntityQueryGenerator(
globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData, globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData,
idDatas.get( 0 ), revisionTypeInId, componentDatas idDatas.get( 0 ), revisionTypeInId, orderBy, componentDatas
); );
} }
else { else {
return new TwoEntityOneAuditedQueryGenerator( return new TwoEntityOneAuditedQueryGenerator(
verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData, globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData,
idDatas.get( 0 ), revisionTypeInId, componentDatas idDatas.get( 0 ), revisionTypeInId, orderBy, componentDatas
); );
} }
} }
@ -86,10 +89,9 @@ else if ( idDatas.size() == 2 ) {
"Ternary relations using @Audited(targetAuditMode = NOT_AUDITED) are not supported." "Ternary relations using @Audited(targetAuditMode = NOT_AUDITED) are not supported."
); );
} }
return new ThreeEntityQueryGenerator( return new ThreeEntityQueryGenerator(
globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData, globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData,
idDatas.get( 0 ), idDatas.get( 1 ), revisionTypeInId, componentDatas idDatas.get( 0 ), idDatas.get( 1 ), revisionTypeInId, orderBy, componentDatas
); );
} }
else { else {

View File

@ -8,6 +8,7 @@
import java.util.List; import java.util.List;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.boot.internal.EnversService;
import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.entities.EntityInstantiator;
import org.hibernate.envers.internal.entities.mapper.relation.query.RelationQueryGenerator; import org.hibernate.envers.internal.entities.mapper.relation.query.RelationQueryGenerator;
@ -46,7 +47,8 @@ public AbstractCollectionInitializor(
@Override @Override
public T initialize() { public T initialize() {
final List<?> collectionContent = queryGenerator.getQuery( versionsReader, primaryKey, revision, removed ).list(); final SharedSessionContractImplementor session = versionsReader.getSessionImplementor();
final List<?> collectionContent = queryGenerator.getQuery( session, primaryKey, revision, removed ).list();
final T collection = initializeCollection( collectionContent.size() ); final T collection = initializeCollection( collectionContent.size() );

View File

@ -9,12 +9,17 @@
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.envers.RevisionType; import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
import org.hibernate.envers.internal.entities.mapper.id.QueryParameterData; import org.hibernate.envers.internal.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.internal.reader.AuditReaderImplementor; import org.hibernate.envers.internal.tools.query.Parameters;
import org.hibernate.envers.internal.tools.query.QueryBuilder; import org.hibernate.envers.internal.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.query.Query; import org.hibernate.query.Query;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
@ -27,57 +32,127 @@
* @author Chris Cranford * @author Chris Cranford
*/ */
public abstract class AbstractRelationQueryGenerator implements RelationQueryGenerator { public abstract class AbstractRelationQueryGenerator implements RelationQueryGenerator {
protected final GlobalConfiguration globalCfg;
protected final AuditEntitiesConfiguration verEntCfg; protected final AuditEntitiesConfiguration verEntCfg;
protected final AuditStrategy auditStrategy;
protected final MiddleIdData referencingIdData; protected final MiddleIdData referencingIdData;
protected final boolean revisionTypeInId; protected final boolean revisionTypeInId;
protected final String entityName;
protected final String orderBy;
private String queryString;
private String queryRemovedString;
protected AbstractRelationQueryGenerator( protected AbstractRelationQueryGenerator(
GlobalConfiguration globalCfg,
AuditEntitiesConfiguration verEntCfg, AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
String entityName,
MiddleIdData referencingIdData, MiddleIdData referencingIdData,
boolean revisionTypeInId) { boolean revisionTypeInId,
String orderBy) {
this.globalCfg = globalCfg;
this.verEntCfg = verEntCfg; this.verEntCfg = verEntCfg;
this.entityName = entityName;
this.auditStrategy = auditStrategy;
this.referencingIdData = referencingIdData; this.referencingIdData = referencingIdData;
this.revisionTypeInId = revisionTypeInId; this.revisionTypeInId = revisionTypeInId;
this.orderBy = orderBy;
} }
/**
* @return Query used to retrieve state of audited entity valid at a given revision.
*/
protected abstract String getQueryString();
/**
* @return Query executed to retrieve state of audited entity valid at previous revision
* or removed during exactly specified revision number. Used only when traversing deleted
* entities graph.
*/
protected abstract String getQueryRemovedString();
@Override @Override
public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision, boolean removed) { public Query getQuery(SharedSessionContractImplementor session, Object primaryKey, Number revision, boolean removed) {
final Query query = versionsReader.getSession().createQuery( removed ? getQueryRemovedString() : getQueryString() ); final String queryString = getQueryString( session.getFactory(), removed );
final Query query = session.createQuery( queryString );
query.setParameter( DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL ); query.setParameter( DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL );
query.setParameter( REVISION_PARAMETER, revision ); query.setParameter( REVISION_PARAMETER, revision );
for ( QueryParameterData paramData : referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(
primaryKey final IdMapper prefixIdMapper = referencingIdData.getPrefixedMapper();
) ) { for ( QueryParameterData paramData : prefixIdMapper.mapToQueryParametersFromId( primaryKey ) ) {
paramData.setParameterValue( query ); paramData.setParameterValue( query );
} }
return query; return query;
} }
protected String queryToString(QueryBuilder query) { /**
* Build the common aspects of a {@link QueryBuilder} used by both query and query-remove strings.
*
* @param sessionFactory The session factory.
* @return The constructed query builder instance.
*/
protected abstract QueryBuilder buildQueryBuilderCommon(SessionFactoryImplementor sessionFactory);
/**
* Apply predicates used to fetch actual data.
*
* @param qb The query builder instance to apply predicates against.
* @param parameters The root query parameters
* @param inclusive Whether its inclusive or not.
*/
protected abstract void applyValidPredicates(QueryBuilder qb, Parameters parameters, boolean inclusive);
/**
* Apply predicates to fetch data and deletions that took place during the same revision.
*
* @param qb The query builder instance to apply predicates against.
*/
protected abstract void applyValidAndRemovePredicates(QueryBuilder qb);
protected String getRevisionTypePath() {
if ( revisionTypeInId ) {
return verEntCfg.getOriginalIdPropName() + "." + verEntCfg.getRevisionTypePropName();
}
return verEntCfg.getRevisionTypePropName();
}
/**
* Get the query to be used.
*
* If {@code removed} is specified as {@code true}, the removal query will be returned.
* If {@code removed} is specified as {@code false}, the non-removal query will be returned.
*
* This method internally will cache the built queries so that subsequent calls will not
* require the rebuilding of the queries.
*
* @param sessionFactory The session factory.
* @param removed Whether to return the removal query or non-removal query.
* @return The query string to be used.
*/
private String getQueryString(SessionFactoryImplementor sessionFactory, boolean removed) {
if ( removed ) {
if ( queryRemovedString == null ) {
queryRemovedString = buildQueryRemoveString( sessionFactory );
}
return queryRemovedString;
}
if ( queryString == null ) {
queryString = buildQueryString( sessionFactory );
}
return queryString;
}
private String buildQueryString(SessionFactoryImplementor sessionFactory) {
final QueryBuilder builder = buildQueryBuilderCommon( sessionFactory );
applyValidPredicates( builder, builder.getRootParameters(), true );
return queryToString( builder );
}
private String buildQueryRemoveString(SessionFactoryImplementor sessionFactory) {
final QueryBuilder builder = buildQueryBuilderCommon( sessionFactory );
applyValidAndRemovePredicates( builder );
return queryToString( builder );
}
private String queryToString(QueryBuilder query) {
return queryToString( query, Collections.emptyMap() ); return queryToString( query, Collections.emptyMap() );
} }
protected String queryToString(QueryBuilder query, Map<String, Object> queryParamValues) { private String queryToString(QueryBuilder query, Map<String, Object> queryParamValues) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
query.build( sb, queryParamValues ); query.build( sb, queryParamValues );
return sb.toString(); return sb.toString();
} }
protected String getRevisionTypePath() {
return revisionTypeInId
? verEntCfg.getOriginalIdPropName() + "." + verEntCfg.getRevisionTypePropName()
: verEntCfg.getRevisionTypePropName();
}
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.envers.internal.entities.mapper.relation.query; package org.hibernate.envers.internal.entities.mapper.relation.query;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.GlobalConfiguration; import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.internal.entities.mapper.id.AbstractCompositeIdMapper; import org.hibernate.envers.internal.entities.mapper.id.AbstractCompositeIdMapper;
@ -14,6 +15,7 @@
import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.envers.internal.tools.query.Parameters;
import org.hibernate.envers.internal.tools.query.QueryBuilder; import org.hibernate.envers.internal.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy; import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.internal.util.StringHelper;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.REFERENCED_ENTITY_ALIAS; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.REFERENCED_ENTITY_ALIAS;
@ -28,10 +30,9 @@
* @author Chris Cranford * @author Chris Cranford
*/ */
public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGenerator { public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString;
private final String queryRemovedString;
private final String mappedBy; private final String mappedBy;
private final boolean multipleIdMapperKey; private final boolean multipleIdMapperKey;
private final MiddleIdData referencedIdData;
public OneAuditEntityQueryGenerator( public OneAuditEntityQueryGenerator(
GlobalConfiguration globalCfg, GlobalConfiguration globalCfg,
@ -42,10 +43,20 @@ public OneAuditEntityQueryGenerator(
MiddleIdData referencedIdData, MiddleIdData referencedIdData,
boolean revisionTypeInId, boolean revisionTypeInId,
String mappedBy, String mappedBy,
boolean mappedByKey) { boolean mappedByKey,
super( verEntCfg, referencingIdData, revisionTypeInId ); String orderBy) {
super(
globalCfg,
verEntCfg,
auditStrategy,
verEntCfg.getAuditEntityName( referencedEntityName ),
referencingIdData,
revisionTypeInId,
orderBy
);
this.mappedBy = mappedBy; this.mappedBy = mappedBy;
this.referencedIdData = referencedIdData;
// HHH-11770 We use AbstractCompositeIdMapper here to handle EmbeddedIdMapper and MultipleIdMappper support // HHH-11770 We use AbstractCompositeIdMapper here to handle EmbeddedIdMapper and MultipleIdMappper support
// so that OneAuditEntityQueryGenerator supports mappings to both @IdClass and @EmbeddedId components. // so that OneAuditEntityQueryGenerator supports mappings to both @IdClass and @EmbeddedId components.
@ -74,24 +85,12 @@ public OneAuditEntityQueryGenerator(
* (only non-deleted entities) * (only non-deleted entities)
* e.revision_type != DEL * e.revision_type != DEL
*/ */
final QueryBuilder commonPart = commonQueryPart( verEntCfg.getAuditEntityName( referencedEntityName ) );
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
globalCfg, auditStrategy, referencedIdData, validQuery, validQuery.getRootParameters()
);
createValidAndRemovedDataRestrictions( globalCfg, auditStrategy, referencedIdData, removedQuery );
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
} }
/** @Override
* Compute common part for both queries. protected QueryBuilder buildQueryBuilderCommon(SessionFactoryImplementor sessionFactory) {
*/
private QueryBuilder commonQueryPart(String versionsReferencedEntityName) {
// SELECT e FROM versionsEntity e // SELECT e FROM versionsEntity e
final QueryBuilder qb = new QueryBuilder( versionsReferencedEntityName, REFERENCED_ENTITY_ALIAS ); final QueryBuilder qb = new QueryBuilder( entityName, REFERENCED_ENTITY_ALIAS, sessionFactory );
qb.addProjection( null, REFERENCED_ENTITY_ALIAS, null, false ); qb.addProjection( null, REFERENCED_ENTITY_ALIAS, null, false );
// WHERE // WHERE
if ( multipleIdMapperKey ) { if ( multipleIdMapperKey ) {
@ -105,57 +104,53 @@ private QueryBuilder commonQueryPart(String versionsReferencedEntityName) {
// e.id_ref_ed = :id_ref_ed // e.id_ref_ed = :id_ref_ed
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( qb.getRootParameters(), null, true ); referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( qb.getRootParameters(), null, true );
} }
// ORDER BY
if ( !StringHelper.isEmpty( orderBy ) ) {
qb.addOrderFragment( REFERENCED_ENTITY_ALIAS, orderBy );
}
return qb; return qb;
} }
/** @Override
* Creates query restrictions used to retrieve only actual data. protected void applyValidPredicates(QueryBuilder qb, Parameters rootParameters, boolean inclusive) {
*/
private void createValidDataRestrictions(
GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, QueryBuilder qb, Parameters rootParameters) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
// (selecting e entities at revision :revision) // (selecting e entities at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)
auditStrategy.addEntityAtRevisionRestriction( auditStrategy.addEntityAtRevisionRestriction(
globalCfg, qb, rootParameters, revisionPropertyPath, globalCfg,
verEntCfg.getRevisionEndFieldName(), true, referencedIdData, revisionPropertyPath, qb,
verEntCfg.getOriginalIdPropName(), REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, rootParameters,
revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(),
true,
referencedIdData,
revisionPropertyPath,
verEntCfg.getOriginalIdPropName(),
REFERENCED_ENTITY_ALIAS,
REFERENCED_ENTITY_ALIAS_DEF_AUD_STR,
true true
); );
// e.revision_type != DEL // e.revision_type != DEL
rootParameters.addWhereWithNamedParam( getRevisionTypePath(), false, "!=", DEL_REVISION_TYPE_PARAMETER ); rootParameters.addWhereWithNamedParam( getRevisionTypePath(), false, "!=", DEL_REVISION_TYPE_PARAMETER );
} }
/** @Override
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision. protected void applyValidAndRemovePredicates(QueryBuilder remQb) {
*/
private void createValidAndRemovedDataRestrictions(
GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, QueryBuilder remQb) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" ); final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
// Restrictions to match all valid rows. // Restrictions to match all valid rows.
final Parameters valid = disjoint.addSubParameters( "and" ); final Parameters valid = disjoint.addSubParameters( "and" );
// Restrictions to match all rows deleted at exactly given revision. // Restrictions to match all rows deleted at exactly given revision.
final Parameters removed = disjoint.addSubParameters( "and" ); final Parameters removed = disjoint.addSubParameters( "and" );
// Excluding current revision, because we need to match data valid at the previous one. // Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions( globalCfg, auditStrategy, referencedIdData, remQb, valid ); applyValidPredicates( remQb, valid, false );
// e.revision = :revision // e.revision = :revision
removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), false, "=", REVISION_PARAMETER ); removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), false, "=", REVISION_PARAMETER );
// e.revision_type = DEL // e.revision_type = DEL
removed.addWhereWithNamedParam( getRevisionTypePath(), false, "=", DEL_REVISION_TYPE_PARAMETER ); removed.addWhereWithNamedParam( getRevisionTypePath(), false, "=", DEL_REVISION_TYPE_PARAMETER );
} }
@Override
protected String getQueryString() {
return queryString;
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
private IdMapper getMultipleIdPrefixedMapper() { private IdMapper getMultipleIdPrefixedMapper() {
final String prefix = verEntCfg.getOriginalIdPropName() + "." + mappedBy + "."; final String prefix = verEntCfg.getOriginalIdPropName() + "." + mappedBy + ".";
return referencingIdData.getOriginalMapper().prefixMappedProperties( prefix ); return referencingIdData.getOriginalMapper().prefixMappedProperties( prefix );

View File

@ -6,7 +6,9 @@
*/ */
package org.hibernate.envers.internal.entities.mapper.relation.query; package org.hibernate.envers.internal.entities.mapper.relation.query;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.envers.internal.tools.query.Parameters;
@ -22,16 +24,30 @@
* *
* @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) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @author Chris Cranford
*/ */
public final class OneEntityQueryGenerator extends AbstractRelationQueryGenerator { public final class OneEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString; private final MiddleComponentData[] componentDatas;
private final String queryRemovedString;
public OneEntityQueryGenerator( public OneEntityQueryGenerator(
AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy, GlobalConfiguration globalCfg,
String versionsMiddleEntityName, MiddleIdData referencingIdData, AuditEntitiesConfiguration verEntCfg,
boolean revisionTypeInId, MiddleComponentData... componentData) { AuditStrategy auditStrategy,
super( verEntCfg, referencingIdData, revisionTypeInId ); String versionsMiddleEntityName,
MiddleIdData referencingIdData,
boolean revisionTypeInId,
MiddleComponentData... componentData) {
super(
globalCfg,
verEntCfg,
auditStrategy,
versionsMiddleEntityName,
referencingIdData,
revisionTypeInId,
null
);
this.componentDatas = componentData;
/* /*
* The valid query that we need to create: * The valid query that we need to create:
@ -52,24 +68,12 @@ public OneEntityQueryGenerator(
* (only non-deleted entities and associations) * (only non-deleted entities and associations)
* ee.revision_type != DEL * ee.revision_type != DEL
*/ */
final QueryBuilder commonPart = commonQueryPart( versionsMiddleEntityName );
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
auditStrategy, versionsMiddleEntityName, validQuery, validQuery.getRootParameters(), true, componentData
);
createValidAndRemovedDataRestrictions( auditStrategy, versionsMiddleEntityName, removedQuery, componentData );
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
} }
/** @Override
* Compute common part for both queries. protected QueryBuilder buildQueryBuilderCommon(SessionFactoryImplementor sessionFactory) {
*/
private QueryBuilder commonQueryPart(String versionsMiddleEntityName) {
// SELECT ee FROM middleEntity ee // SELECT ee FROM middleEntity ee
final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS ); final QueryBuilder qb = new QueryBuilder( entityName, MIDDLE_ENTITY_ALIAS, sessionFactory );
qb.addProjection( null, MIDDLE_ENTITY_ALIAS, null, false ); qb.addProjection( null, MIDDLE_ENTITY_ALIAS, null, false );
// WHERE // WHERE
// ee.originalId.id_ref_ing = :id_ref_ing // ee.originalId.id_ref_ing = :id_ref_ing
@ -78,56 +82,55 @@ private QueryBuilder commonQueryPart(String versionsMiddleEntityName) {
verEntCfg.getOriginalIdPropName(), verEntCfg.getOriginalIdPropName(),
true true
); );
// NOTE:
// No `orderBy` fragment is specified because this generator is used for
// embeddables and enumerations where either a Set-based container will
// force the SETORDINAL property to give us a unique primary key tuple
// or an @IndexColumn/@OrderColumn must be specified that takes priority
// over an @OrderBy fragment.
return qb; return qb;
} }
/** @Override
* Creates query restrictions used to retrieve only actual data. protected void applyValidPredicates(QueryBuilder qb, Parameters rootParameters, boolean inclusive) {
*/
private void createValidDataRestrictions(
AuditStrategy auditStrategy, String versionsMiddleEntityName,
QueryBuilder qb, Parameters rootParameters, boolean inclusive,
MiddleComponentData... componentData) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String originalIdPropertyName = verEntCfg.getOriginalIdPropName(); final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// (with ee association at revision :revision) // (with ee association at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction( auditStrategy.addAssociationAtRevisionRestriction(
qb, rootParameters, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true, qb,
referencingIdData, versionsMiddleEntityName, eeOriginalIdPropertyPath, revisionPropertyPath, rootParameters,
originalIdPropertyName, MIDDLE_ENTITY_ALIAS, inclusive, componentData revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(),
true,
referencingIdData,
entityName,
eeOriginalIdPropertyPath,
revisionPropertyPath,
originalIdPropertyName,
MIDDLE_ENTITY_ALIAS,
inclusive,
componentDatas
); );
// ee.revision_type != DEL // ee.revision_type != DEL
rootParameters.addWhereWithNamedParam( getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER ); rootParameters.addWhereWithNamedParam( getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER );
} }
/** @Override
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision. protected void applyValidAndRemovePredicates(QueryBuilder remQb) {
*/
private void createValidAndRemovedDataRestrictions(
AuditStrategy auditStrategy, String versionsMiddleEntityName,
QueryBuilder remQb, MiddleComponentData... componentData) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" ); final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
// Restrictions to match all valid rows. // Restrictions to match all valid rows.
final Parameters valid = disjoint.addSubParameters( "and" ); final Parameters valid = disjoint.addSubParameters( "and" );
// Restrictions to match all rows deleted at exactly given revision. // Restrictions to match all rows deleted at exactly given revision.
final Parameters removed = disjoint.addSubParameters( "and" ); final Parameters removed = disjoint.addSubParameters( "and" );
// Excluding current revision, because we need to match data valid at the previous one. // Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions( auditStrategy, versionsMiddleEntityName, remQb, valid, false, componentData ); applyValidPredicates( remQb, valid, false );
// ee.revision = :revision // ee.revision = :revision
removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), "=", REVISION_PARAMETER ); removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), "=", REVISION_PARAMETER );
// ee.revision_type = DEL // ee.revision_type = DEL
removed.addWhereWithNamedParam( getRevisionTypePath(), "=", DEL_REVISION_TYPE_PARAMETER ); removed.addWhereWithNamedParam( getRevisionTypePath(), "=", DEL_REVISION_TYPE_PARAMETER );
} }
@Override
protected String getQueryString() {
return queryString;
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
} }

View File

@ -6,18 +6,26 @@
*/ */
package org.hibernate.envers.internal.entities.mapper.relation.query; package org.hibernate.envers.internal.entities.mapper.relation.query;
import org.hibernate.envers.internal.reader.AuditReaderImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.Query; import org.hibernate.query.Query;
/** /**
* TODO: cleanup implementations and extract common code * Implementations of this interface provide a method to generate queries on a
* <p/> * relation table (a table used for mapping relations). The query can select,
* Implementations of this interface provide a method to generate queries on a relation table (a table used * apart from selecting the content of the relation table, also data of other
* for mapping relations). The query can select, apart from selecting the content of the relation table, also data of * "related" entities.
* other "related" entities.
* *
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
* @author Chris Cranford
*/ */
public interface RelationQueryGenerator { public interface RelationQueryGenerator {
Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision, boolean removed); /**
* Return the query to fetch the relation.
*
* @param session The session.
* @param primaryKey The primary key of the owning object.
* @param revision The revision to be fetched.
* @param removed Whether to return a query that includes the removed audit rows.
*/
Query getQuery(SharedSessionContractImplementor session, Object primaryKey, Number revision, boolean removed);
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.envers.internal.entities.mapper.relation.query; package org.hibernate.envers.internal.entities.mapper.relation.query;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.GlobalConfiguration; import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData;
@ -13,6 +14,7 @@
import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.envers.internal.tools.query.Parameters;
import org.hibernate.envers.internal.tools.query.QueryBuilder; import org.hibernate.envers.internal.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy; import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.internal.util.StringHelper;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.INDEX_ENTITY_ALIAS; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.INDEX_ENTITY_ALIAS;
@ -27,18 +29,37 @@
* *
* @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) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @author Chris Cranford
*/ */
public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenerator { public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString; private final MiddleIdData referencedIdData;
private final String queryRemovedString; private final MiddleIdData indexIdData;
private final MiddleComponentData[] componentDatas;
public ThreeEntityQueryGenerator( public ThreeEntityQueryGenerator(
GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg, GlobalConfiguration globalCfg,
AuditStrategy auditStrategy, String versionsMiddleEntityName, AuditEntitiesConfiguration verEntCfg,
MiddleIdData referencingIdData, MiddleIdData referencedIdData, AuditStrategy auditStrategy,
MiddleIdData indexIdData, boolean revisionTypeInId, String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
MiddleIdData indexIdData,
boolean revisionTypeInId,
String orderBy,
MiddleComponentData... componentData) { MiddleComponentData... componentData) {
super( verEntCfg, referencingIdData, revisionTypeInId ); super(
globalCfg,
verEntCfg,
auditStrategy,
versionsMiddleEntityName,
referencingIdData,
revisionTypeInId,
orderBy
);
this.referencedIdData = referencedIdData;
this.indexIdData = indexIdData;
this.componentDatas = componentData;
/* /*
* The valid query that we need to create: * The valid query that we need to create:
@ -86,35 +107,14 @@ public ThreeEntityQueryGenerator(
* e.revision_type != DEL AND * e.revision_type != DEL AND
* f.revision_type != DEL * f.revision_type != DEL
*/ */
final QueryBuilder commonPart = commonQueryPart(
referencedIdData,
indexIdData,
versionsMiddleEntityName,
verEntCfg.getOriginalIdPropName()
);
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, validQuery,
validQuery.getRootParameters(), true, indexIdData, componentData
);
createValidAndRemovedDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, removedQuery, indexIdData, componentData
);
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
} }
/** @Override
* Compute common part for both queries. protected QueryBuilder buildQueryBuilderCommon(SessionFactoryImplementor sessionFactory) {
*/ final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
private QueryBuilder commonQueryPart(
MiddleIdData referencedIdData, MiddleIdData indexIdData,
String versionsMiddleEntityName, String originalIdPropertyName) {
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// SELECT new list(ee) FROM middleEntity ee // SELECT new list(ee) FROM middleEntity ee
final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS ); final QueryBuilder qb = new QueryBuilder( entityName, MIDDLE_ENTITY_ALIAS, sessionFactory );
qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS, false ); qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS, false );
qb.addFrom( indexIdData.getAuditEntityName(), INDEX_ENTITY_ALIAS, false ); qb.addFrom( indexIdData.getAuditEntityName(), INDEX_ENTITY_ALIAS, false );
qb.addProjection( qb.addProjection(
@ -135,16 +135,18 @@ private QueryBuilder commonQueryPart(
); );
// ee.originalId.id_ref_ing = :id_ref_ing // ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true ); referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true );
// ORDER BY
// Hibernate applies @OrderBy on map elements, not the key.
// So here we apply it to the referenced entity, not the actual index entity that represents the key.
if ( !StringHelper.isEmpty( orderBy ) ) {
qb.addOrderFragment( REFERENCED_ENTITY_ALIAS, orderBy );
}
return qb; return qb;
} }
/** @Override
* Creates query restrictions used to retrieve only actual data. protected void applyValidPredicates(QueryBuilder qb, Parameters rootParameters, boolean inclusive) {
*/
private void createValidDataRestrictions(
GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, String versionsMiddleEntityName, QueryBuilder qb,
Parameters rootParameters, boolean inclusive, MiddleIdData indexIdData, MiddleComponentData... componentData) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String originalIdPropertyName = verEntCfg.getOriginalIdPropName(); final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
@ -184,9 +186,19 @@ private void createValidDataRestrictions(
// (with ee association at revision :revision) // (with ee association at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction( auditStrategy.addAssociationAtRevisionRestriction(
qb, rootParameters, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true, qb,
referencingIdData, versionsMiddleEntityName, eeOriginalIdPropertyPath, revisionPropertyPath, rootParameters,
originalIdPropertyName, MIDDLE_ENTITY_ALIAS, inclusive, componentData revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(),
true,
referencingIdData,
entityName,
eeOriginalIdPropertyPath,
revisionPropertyPath,
originalIdPropertyName,
MIDDLE_ENTITY_ALIAS,
inclusive,
componentDatas
); );
// ee.revision_type != DEL // ee.revision_type != DEL
rootParameters.addWhereWithNamedParam( revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER ); rootParameters.addWhereWithNamedParam( revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER );
@ -206,13 +218,8 @@ private void createValidDataRestrictions(
); );
} }
/** @Override
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision. protected void applyValidAndRemovePredicates(QueryBuilder remQb) {
*/
private void createValidAndRemovedDataRestrictions(
GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, String versionsMiddleEntityName,
QueryBuilder remQb, MiddleIdData indexIdData, MiddleComponentData... componentData) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" ); final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
// Restrictions to match all valid rows. // Restrictions to match all valid rows.
final Parameters valid = disjoint.addSubParameters( "and" ); final Parameters valid = disjoint.addSubParameters( "and" );
@ -221,9 +228,7 @@ private void createValidAndRemovedDataRestrictions(
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String revisionTypePropName = getRevisionTypePath(); final String revisionTypePropName = getRevisionTypePath();
// Excluding current revision, because we need to match data valid at the previous one. // Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions( applyValidPredicates( remQb, valid, false );
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, remQb, valid, false, indexIdData, componentData
);
// ee.revision = :revision // ee.revision = :revision
removed.addWhereWithNamedParam( revisionPropertyPath, "=", REVISION_PARAMETER ); removed.addWhereWithNamedParam( revisionPropertyPath, "=", REVISION_PARAMETER );
// e.revision = :revision // e.revision = :revision
@ -257,14 +262,4 @@ private void createValidAndRemovedDataRestrictions(
DEL_REVISION_TYPE_PARAMETER DEL_REVISION_TYPE_PARAMETER
); );
} }
@Override
protected String getQueryString() {
return queryString;
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
} }

View File

@ -6,12 +6,15 @@
*/ */
package org.hibernate.envers.internal.entities.mapper.relation.query; package org.hibernate.envers.internal.entities.mapper.relation.query;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.envers.internal.tools.query.Parameters;
import org.hibernate.envers.internal.tools.query.QueryBuilder; import org.hibernate.envers.internal.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy; import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.internal.util.StringHelper;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS;
@ -23,17 +26,34 @@
* *
* @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) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @author Chris Cranford
*/ */
public final class TwoEntityOneAuditedQueryGenerator extends AbstractRelationQueryGenerator { public final class TwoEntityOneAuditedQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString; private final MiddleIdData referencedIdData;
private final String queryRemovedString; private final MiddleComponentData[] componentDatas;
public TwoEntityOneAuditedQueryGenerator( public TwoEntityOneAuditedQueryGenerator(
AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy, GlobalConfiguration globalCfg,
String versionsMiddleEntityName, MiddleIdData referencingIdData, AuditEntitiesConfiguration verEntCfg,
MiddleIdData referencedIdData, boolean revisionTypeInId, AuditStrategy auditStrategy,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
boolean revisionTypeInId,
String orderBy,
MiddleComponentData... componentData) { MiddleComponentData... componentData) {
super( verEntCfg, referencingIdData, revisionTypeInId ); super(
globalCfg,
verEntCfg,
auditStrategy,
versionsMiddleEntityName,
referencingIdData,
revisionTypeInId,
orderBy
);
this.referencedIdData = referencedIdData;
this.componentDatas = componentData;
/* /*
* The valid query that we need to create: * The valid query that we need to create:
@ -57,31 +77,14 @@ public TwoEntityOneAuditedQueryGenerator(
* (only non-deleted entities and associations) * (only non-deleted entities and associations)
* ee.revision_type != DEL * ee.revision_type != DEL
*/ */
final QueryBuilder commonPart = commonQueryPart(
referencedIdData,
versionsMiddleEntityName,
verEntCfg.getOriginalIdPropName()
);
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
auditStrategy, versionsMiddleEntityName, validQuery, validQuery.getRootParameters(), componentData
);
createValidAndRemovedDataRestrictions( auditStrategy, versionsMiddleEntityName, removedQuery, componentData );
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
} }
/** @Override
* Compute common part for both queries. protected QueryBuilder buildQueryBuilderCommon(SessionFactoryImplementor sessionFactory) {
*/ final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
private QueryBuilder commonQueryPart(
MiddleIdData referencedIdData, String versionsMiddleEntityName,
String originalIdPropertyName) {
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// SELECT new list(ee) FROM middleEntity ee // SELECT new list(ee) FROM middleEntity ee
final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS ); final QueryBuilder qb = new QueryBuilder( entityName, MIDDLE_ENTITY_ALIAS, sessionFactory );
qb.addFrom( referencedIdData.getEntityName(), REFERENCED_ENTITY_ALIAS, false ); qb.addFrom( referencedIdData.getEntityName(), REFERENCED_ENTITY_ALIAS, false );
qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, null, false ); qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, null, false );
// WHERE // WHERE
@ -92,54 +95,53 @@ private QueryBuilder commonQueryPart(
); );
// ee.originalId.id_ref_ing = :id_ref_ing // ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true ); referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true );
// ORDER BY
if ( !StringHelper.isEmpty( orderBy ) ) {
qb.addOrderFragment( REFERENCED_ENTITY_ALIAS, orderBy );
}
return qb; return qb;
} }
/** /**
* Creates query restrictions used to retrieve only actual data. * Creates query restrictions used to retrieve only actual data.
*/ */
private void createValidDataRestrictions( @Override
AuditStrategy auditStrategy, String versionsMiddleEntityName, QueryBuilder qb, protected void applyValidPredicates(QueryBuilder qb, Parameters rootParameters, boolean inclusive) {
Parameters rootParameters, MiddleComponentData... componentData) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String originalIdPropertyName = verEntCfg.getOriginalIdPropName(); final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// (with ee association at revision :revision) // (with ee association at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction( auditStrategy.addAssociationAtRevisionRestriction(
qb, rootParameters, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true, qb,
referencingIdData, versionsMiddleEntityName, eeOriginalIdPropertyPath, revisionPropertyPath, rootParameters,
originalIdPropertyName, MIDDLE_ENTITY_ALIAS, true, componentData revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(),
true,
referencingIdData,
entityName,
eeOriginalIdPropertyPath,
revisionPropertyPath,
originalIdPropertyName,
MIDDLE_ENTITY_ALIAS,
true,
componentDatas
); );
// ee.revision_type != DEL // ee.revision_type != DEL
rootParameters.addWhereWithNamedParam( getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER ); rootParameters.addWhereWithNamedParam( getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER );
} }
/** @Override
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision. protected void applyValidAndRemovePredicates(QueryBuilder remQb) {
*/
private void createValidAndRemovedDataRestrictions(
AuditStrategy auditStrategy, String versionsMiddleEntityName,
QueryBuilder remQb, MiddleComponentData... componentData) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" ); final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
// Restrictions to match all valid rows. // Restrictions to match all valid rows.
final Parameters valid = disjoint.addSubParameters( "and" ); final Parameters valid = disjoint.addSubParameters( "and" );
// Restrictions to match all rows deleted at exactly given revision. // Restrictions to match all rows deleted at exactly given revision.
final Parameters removed = disjoint.addSubParameters( "and" ); final Parameters removed = disjoint.addSubParameters( "and" );
createValidDataRestrictions( auditStrategy, versionsMiddleEntityName, remQb, valid, componentData ); applyValidPredicates( remQb, valid, false );
// ee.revision = :revision // ee.revision = :revision
removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), "=", REVISION_PARAMETER ); removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), "=", REVISION_PARAMETER );
// ee.revision_type = DEL // ee.revision_type = DEL
removed.addWhereWithNamedParam( getRevisionTypePath(), "=", DEL_REVISION_TYPE_PARAMETER ); removed.addWhereWithNamedParam( getRevisionTypePath(), "=", DEL_REVISION_TYPE_PARAMETER );
} }
@Override
protected String getQueryString() {
return queryString;
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
} }

View File

@ -6,6 +6,8 @@
*/ */
package org.hibernate.envers.internal.entities.mapper.relation.query; package org.hibernate.envers.internal.entities.mapper.relation.query;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.GlobalConfiguration; import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData;
@ -13,6 +15,7 @@
import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.envers.internal.tools.query.Parameters;
import org.hibernate.envers.internal.tools.query.QueryBuilder; import org.hibernate.envers.internal.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy; import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.internal.util.StringHelper;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS;
@ -25,17 +28,34 @@
* *
* @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) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @author Chris Cranford
*/ */
public final class TwoEntityQueryGenerator extends AbstractRelationQueryGenerator { public final class TwoEntityQueryGenerator extends AbstractRelationQueryGenerator {
private final String queryString; private final MiddleIdData referencedIdData;
private final String queryRemovedString; private final MiddleComponentData[] componentDatas;
public TwoEntityQueryGenerator( public TwoEntityQueryGenerator(
GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg, GlobalConfiguration globalCfg,
AuditStrategy auditStrategy, String versionsMiddleEntityName, AuditEntitiesConfiguration verEntCfg,
MiddleIdData referencingIdData, MiddleIdData referencedIdData, AuditStrategy auditStrategy,
boolean revisionTypeInId, MiddleComponentData... componentData) { String versionsMiddleEntityName,
super( verEntCfg, referencingIdData, revisionTypeInId ); MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
boolean revisionTypeInId,
String orderBy,
MiddleComponentData... componentData) {
super(
globalCfg,
verEntCfg,
auditStrategy,
versionsMiddleEntityName,
referencingIdData,
revisionTypeInId,
orderBy
);
this.referencedIdData = referencedIdData;
this.componentDatas = componentData;
/* /*
* The valid query that we need to create: * The valid query that we need to create:
@ -68,34 +88,14 @@ public TwoEntityQueryGenerator(
* ee.revision_type != DEL AND * ee.revision_type != DEL AND
* e.revision_type != DEL * e.revision_type != DEL
*/ */
final QueryBuilder commonPart = commonQueryPart(
referencedIdData,
versionsMiddleEntityName,
verEntCfg.getOriginalIdPropName()
);
final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, validQuery,
validQuery.getRootParameters(), true, componentData
);
createValidAndRemovedDataRestrictions(
globalCfg, auditStrategy, referencedIdData, versionsMiddleEntityName, removedQuery, componentData
);
queryString = queryToString( validQuery );
queryRemovedString = queryToString( removedQuery );
} }
/** @Override
* Compute common part for both queries. protected QueryBuilder buildQueryBuilderCommon(SessionFactoryImplementor sessionFactory) {
*/ final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
private QueryBuilder commonQueryPart(
MiddleIdData referencedIdData, String versionsMiddleEntityName,
String originalIdPropertyName) {
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
// SELECT new list(ee) FROM middleEntity ee // SELECT new list(ee) FROM middleEntity ee
QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS ); QueryBuilder qb = new QueryBuilder( entityName, MIDDLE_ENTITY_ALIAS, sessionFactory );
qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS, false ); qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS, false );
qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, null, false ); qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, null, false );
// WHERE // WHERE
@ -107,16 +107,17 @@ private QueryBuilder commonQueryPart(
); );
// ee.originalId.id_ref_ing = :id_ref_ing // ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true ); referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( rootParameters, originalIdPropertyName, true );
// ORDER BY
if ( !StringHelper.isEmpty( orderBy ) ) {
qb.addOrderFragment( REFERENCED_ENTITY_ALIAS, orderBy );
}
return qb; return qb;
} }
/** @Override
* Creates query restrictions used to retrieve only actual data. protected void applyValidPredicates(QueryBuilder qb, Parameters rootParameters, boolean inclusive) {
*/
private void createValidDataRestrictions(
GlobalConfiguration globalCfg, AuditStrategy auditStrategy, MiddleIdData referencedIdData,
String versionsMiddleEntityName, QueryBuilder qb, Parameters rootParameters,
boolean inclusive, MiddleComponentData... componentData) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String originalIdPropertyName = verEntCfg.getOriginalIdPropName(); final String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName;
@ -140,10 +141,19 @@ private void createValidDataRestrictions(
// (with ee association at revision :revision) // (with ee association at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)
auditStrategy.addAssociationAtRevisionRestriction( auditStrategy.addAssociationAtRevisionRestriction(
qb, rootParameters, revisionPropertyPath, qb,
verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName, rootParameters,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, revisionPropertyPath,
inclusive, componentData verEntCfg.getRevisionEndFieldName(),
true,
referencingIdData,
entityName,
eeOriginalIdPropertyPath,
revisionPropertyPath,
originalIdPropertyName,
MIDDLE_ENTITY_ALIAS,
inclusive,
componentDatas
); );
// ee.revision_type != DEL // ee.revision_type != DEL
rootParameters.addWhereWithNamedParam( revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER ); rootParameters.addWhereWithNamedParam( revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER );
@ -156,13 +166,8 @@ private void createValidDataRestrictions(
); );
} }
/** @Override
* Create query restrictions used to retrieve actual data and deletions that took place at exactly given revision. protected void applyValidAndRemovePredicates(QueryBuilder remQb) {
*/
private void createValidAndRemovedDataRestrictions(
GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, String versionsMiddleEntityName,
QueryBuilder remQb, MiddleComponentData... componentData) {
final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" ); final Parameters disjoint = remQb.getRootParameters().addSubParameters( "or" );
// Restrictions to match all valid rows. // Restrictions to match all valid rows.
final Parameters valid = disjoint.addSubParameters( "and" ); final Parameters valid = disjoint.addSubParameters( "and" );
@ -171,15 +176,10 @@ private void createValidAndRemovedDataRestrictions(
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
final String revisionTypePropName = getRevisionTypePath(); final String revisionTypePropName = getRevisionTypePath();
// Excluding current revision, because we need to match data valid at the previous one. // Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions( applyValidPredicates(
globalCfg,
auditStrategy,
referencedIdData,
versionsMiddleEntityName,
remQb, remQb,
valid, valid,
false, false
componentData
); );
// ee.revision = :revision // ee.revision = :revision
removed.addWhereWithNamedParam( revisionPropertyPath, "=", REVISION_PARAMETER ); removed.addWhereWithNamedParam( revisionPropertyPath, "=", REVISION_PARAMETER );
@ -200,14 +200,4 @@ private void createValidAndRemovedDataRestrictions(
DEL_REVISION_TYPE_PARAMETER DEL_REVISION_TYPE_PARAMETER
); );
} }
@Override
protected String getQueryString() {
return queryString;
}
@Override
protected String getQueryRemovedString() {
return queryRemovedString;
}
} }

View File

@ -8,19 +8,33 @@
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import javax.persistence.criteria.JoinType; import javax.persistence.criteria.JoinType;
import org.hibernate.HibernateException;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.SharedSessionContract;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.envers.RevisionType; import org.hibernate.envers.RevisionType;
import org.hibernate.envers.internal.entities.RevisionTypeType; import org.hibernate.envers.internal.entities.RevisionTypeType;
import org.hibernate.envers.internal.tools.MutableInteger; import org.hibernate.envers.internal.tools.MutableInteger;
import org.hibernate.envers.internal.tools.StringTools; import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.envers.internal.tools.Triple; import org.hibernate.envers.internal.tools.Triple;
import org.hibernate.envers.tools.Pair;
import org.hibernate.query.Query; import org.hibernate.query.Query;
import org.hibernate.sql.Template;
import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.sql.ordering.antlr.ColumnReference;
import org.hibernate.sql.ordering.antlr.FormulaReference;
import org.hibernate.sql.ordering.antlr.OrderByAliasResolver;
import org.hibernate.sql.ordering.antlr.OrderByTranslation;
import org.hibernate.sql.ordering.antlr.SqlValueReference;
import org.hibernate.type.CustomType; import org.hibernate.type.CustomType;
/** /**
@ -59,19 +73,30 @@ public class QueryBuilder {
*/ */
private final List<String> projections; private final List<String> projections;
private final List<Pair<String, String>> orderFragments;
private final SessionFactoryImplementor sessionFactory;
/** /**
* @param entityName Main entity which should be selected. * @param entityName Main entity which should be selected.
* @param alias Alias of the entity * @param alias Alias of the entity
* @param sessionFactory Session factory
*/ */
public QueryBuilder(String entityName, String alias) { public QueryBuilder(String entityName, String alias, SessionFactoryImplementor sessionFactory) {
this( entityName, alias, new MutableInteger(), new MutableInteger() ); this( entityName, alias, new MutableInteger(), new MutableInteger(), sessionFactory );
} }
private QueryBuilder(String entityName, String alias, MutableInteger aliasCounter, MutableInteger paramCounter) { private QueryBuilder(
String entityName,
String alias,
MutableInteger aliasCounter,
MutableInteger paramCounter,
SessionFactoryImplementor sessionFactory) {
this.entityName = entityName; this.entityName = entityName;
this.alias = alias; this.alias = alias;
this.aliasCounter = aliasCounter; this.aliasCounter = aliasCounter;
this.paramCounter = paramCounter; this.paramCounter = paramCounter;
this.sessionFactory = sessionFactory;
final Parameters rootParameters = new Parameters( alias, "and", paramCounter ); final Parameters rootParameters = new Parameters( alias, "and", paramCounter );
parameters.add( rootParameters ); parameters.add( rootParameters );
@ -79,6 +104,7 @@ private QueryBuilder(String entityName, String alias, MutableInteger aliasCounte
froms = new ArrayList<>(); froms = new ArrayList<>();
orders = new ArrayList<>(); orders = new ArrayList<>();
projections = new ArrayList<>(); projections = new ArrayList<>();
orderFragments = new ArrayList<>();
addFrom( entityName, alias, true ); addFrom( entityName, alias, true );
} }
@ -87,6 +113,7 @@ private QueryBuilder(String entityName, String alias, MutableInteger aliasCounte
private QueryBuilder(QueryBuilder other) { private QueryBuilder(QueryBuilder other) {
this.entityName = other.entityName; this.entityName = other.entityName;
this.alias = other.alias; this.alias = other.alias;
this.sessionFactory = other.sessionFactory;
this.aliasCounter = other.aliasCounter.deepCopy(); this.aliasCounter = other.aliasCounter.deepCopy();
this.paramCounter = other.paramCounter.deepCopy(); this.paramCounter = other.paramCounter.deepCopy();
for (final Parameters params : other.parameters) { for (final Parameters params : other.parameters) {
@ -96,6 +123,7 @@ private QueryBuilder(QueryBuilder other) {
froms = new ArrayList<>( other.froms ); froms = new ArrayList<>( other.froms );
orders = new ArrayList<>( other.orders ); orders = new ArrayList<>( other.orders );
projections = new ArrayList<>( other.projections ); projections = new ArrayList<>( other.projections );
orderFragments = new ArrayList<>( other.orderFragments );
} }
public QueryBuilder deepCopy() { public QueryBuilder deepCopy() {
@ -123,7 +151,13 @@ public void addFrom(String entityName, String alias, boolean select) {
public Parameters addJoin(JoinType joinType, String entityName, String alias, boolean select) { public Parameters addJoin(JoinType joinType, String entityName, String alias, boolean select) {
Parameters joinConditionParameters = new Parameters( alias, Parameters.AND, paramCounter ); Parameters joinConditionParameters = new Parameters( alias, Parameters.AND, paramCounter );
InnerOuterJoinParameter joinParameter = new InnerOuterJoinParameter( joinType, entityName, alias, select, joinConditionParameters ); InnerOuterJoinParameter joinParameter = new InnerOuterJoinParameter(
joinType,
entityName,
alias,
select,
joinConditionParameters
);
froms.add( joinParameter ); froms.add( joinParameter );
return joinConditionParameters; return joinConditionParameters;
} }
@ -140,7 +174,7 @@ public String generateAlias() {
* be later used as a value of a parameter. * be later used as a value of a parameter.
*/ */
public QueryBuilder newSubQueryBuilder(String entityName, String alias) { public QueryBuilder newSubQueryBuilder(String entityName, String alias) {
return new QueryBuilder( entityName, alias, aliasCounter, paramCounter ); return new QueryBuilder( entityName, alias, aliasCounter, paramCounter, sessionFactory );
} }
public Parameters getRootParameters() { public Parameters getRootParameters() {
@ -157,6 +191,10 @@ public void addOrder(String alias, String propertyName, boolean ascending) {
orders.add( Triple.make( alias, propertyName, ascending ) ); orders.add( Triple.make( alias, propertyName, ascending ) );
} }
public void addOrderFragment(String alias, String fragment) {
orderFragments.add( Pair.make( alias, fragment ) );
}
public void addProjection(String function, String alias, String propertyName, boolean distinct) { public void addProjection(String function, String alias, String propertyName, boolean distinct) {
final String effectivePropertyName = propertyName == null ? "" : ".".concat( propertyName ); final String effectivePropertyName = propertyName == null ? "" : ".".concat( propertyName );
if ( function == null ) { if ( function == null ) {
@ -209,10 +247,47 @@ public void build(StringBuilder sb, Map<String, Object> queryParamValues) {
} }
} }
// orders // orders
if ( orders.size() > 0 ) { if ( !orders.isEmpty() ) {
sb.append( " order by " ); sb.append( " order by " );
StringTools.append( sb, getOrderList().iterator(), ", " ); StringTools.append( sb, getOrderList().iterator(), ", " );
} }
else if ( !orderFragments.isEmpty() ) {
sb.append( " order by " );
final Iterator<Pair<String, String>> fragmentIterator = orderFragments.iterator();
while( fragmentIterator.hasNext() ) {
final Pair<String, String> fragment = fragmentIterator.next();
final OrderByTranslation orderByFragmentTranslation = Template.translateOrderBy(
fragment.getSecond(),
new ColumnMapper() {
@Override
public SqlValueReference[] map(String reference) throws HibernateException {
return new SqlValueReference[ 0 ];
}
},
sessionFactory,
sessionFactory.getJdbcServices().getDialect(),
sessionFactory.getSqlFunctionRegistry()
);
sb.append( orderByFragmentTranslation.injectAliases( new QueryOrderByAliasResolver( fragment.getFirst() ) ) );
if ( fragmentIterator.hasNext() ) {
sb.append( ", " );
}
}
}
}
private class QueryOrderByAliasResolver implements OrderByAliasResolver {
private String alias;
public QueryOrderByAliasResolver(String alias) {
this.alias = alias;
}
@Override
public String resolveTableAlias(String columnReference) {
return alias;
}
} }
private List<String> getSelectAliasList() { private List<String> getSelectAliasList() {
@ -294,8 +369,8 @@ public CrossJoinParameter(String entityName, String alias, boolean select) {
} }
@Override @Override
public void appendJoin(final boolean firstFromElement, final StringBuilder builder, final Map<String, Object> queryParamValues) { public void appendJoin(boolean firstFromElement, StringBuilder builder, Map<String, Object> queryParamValues) {
if (!firstFromElement) { if ( !firstFromElement ) {
builder.append( ", " ); builder.append( ", " );
} }
builder.append( entityName ).append( ' ' ).append( getAlias() ); builder.append( entityName ).append( ' ' ).append( getAlias() );
@ -309,7 +384,12 @@ private static class InnerOuterJoinParameter extends JoinParameter {
private final String entityName; private final String entityName;
private final Parameters joinConditionParameters; private final Parameters joinConditionParameters;
public InnerOuterJoinParameter(JoinType joinType, String entityName, String alias, boolean select, Parameters joinConditionParameters) { public InnerOuterJoinParameter(
JoinType joinType,
String entityName,
String alias,
boolean select,
Parameters joinConditionParameters) {
super(alias, select); super(alias, select);
this.joinType = joinType; this.joinType = joinType;
this.entityName = entityName; this.entityName = entityName;

View File

@ -86,7 +86,7 @@ protected AbstractAuditQuery(
} }
aliasToEntityNameMap.put( REFERENCED_ENTITY_ALIAS, entityName ); aliasToEntityNameMap.put( REFERENCED_ENTITY_ALIAS, entityName );
qb = new QueryBuilder( versionsEntityName, REFERENCED_ENTITY_ALIAS ); qb = new QueryBuilder( versionsEntityName, REFERENCED_ENTITY_ALIAS, versionsReader.getSessionImplementor().getFactory() );
} }
@Override @Override
@ -95,7 +95,7 @@ public String getAlias() {
} }
protected Query buildQuery() { protected Query buildQuery() {
Query query = qb.toQuery( versionsReader.getSession() ); Query query = qb.toQuery( versionsReader.getSessionImplementor() );
setQueryProperties( query ); setQueryProperties( query );
return query; return query;
} }

View File

@ -17,8 +17,8 @@
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.action.spi.BeforeTransactionCompletionProcess; import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.envers.RevisionType; import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.GlobalConfiguration; import org.hibernate.envers.configuration.internal.GlobalConfiguration;
@ -241,7 +241,11 @@ public void performCollectionChange(
String propertyName, String propertyName,
AuditEntitiesConfiguration auditEntitiesConfiguration, AuditEntitiesConfiguration auditEntitiesConfiguration,
PersistentCollectionChangeData persistentCollectionChangeData, Object revision) { PersistentCollectionChangeData persistentCollectionChangeData, Object revision) {
final QueryBuilder qb = new QueryBuilder( persistentCollectionChangeData.getEntityName(), MIDDLE_ENTITY_ALIAS ); final QueryBuilder qb = new QueryBuilder(
persistentCollectionChangeData.getEntityName(),
MIDDLE_ENTITY_ALIAS,
( (SharedSessionContractImplementor) session ).getFactory()
);
final String originalIdPropName = auditEntitiesConfiguration.getOriginalIdPropName(); final String originalIdPropName = auditEntitiesConfiguration.getOriginalIdPropName();
final Map<String, Object> originalId = (Map<String, Object>) persistentCollectionChangeData.getData().get( final Map<String, Object> originalId = (Map<String, Object>) persistentCollectionChangeData.getData().get(