diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntitiesConfigurations.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntitiesConfigurations.java index c0e05a92fb..03d47c3931 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntitiesConfigurations.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntitiesConfigurations.java @@ -94,7 +94,12 @@ public class EntitiesConfigurations { } public RelationDescription getRelationDescription(String entityName, String propertyName) { - final EntityConfiguration entCfg = entitiesConfigurations.get( entityName ); + final EntityConfiguration entCfg; + if ( isVersioned( entityName ) ) { + entCfg = get( entityName ); + } else { + entCfg = getNotVersionEntityConfiguration( entityName ); + } final RelationDescription relDesc = entCfg.getRelationDescription( propertyName ); if ( relDesc != null ) { return relDesc; diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/OneAuditEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/OneAuditEntityQueryGenerator.java index b46553a157..20a3b60581 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/OneAuditEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/OneAuditEntityQueryGenerator.java @@ -70,7 +70,7 @@ public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGen private QueryBuilder commonQueryPart(String versionsReferencedEntityName) { // SELECT e FROM versionsEntity e final QueryBuilder qb = new QueryBuilder( versionsReferencedEntityName, REFERENCED_ENTITY_ALIAS ); - qb.addProjection( null, REFERENCED_ENTITY_ALIAS, false, false ); + qb.addProjection( null, REFERENCED_ENTITY_ALIAS, null, false ); // WHERE // e.id_ref_ed = :id_ref_ed referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( qb.getRootParameters(), null, true ); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/OneEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/OneEntityQueryGenerator.java index 540edabf37..71d6950e07 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/OneEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/OneEntityQueryGenerator.java @@ -70,7 +70,7 @@ public final class OneEntityQueryGenerator extends AbstractRelationQueryGenerato private QueryBuilder commonQueryPart(String versionsMiddleEntityName) { // SELECT ee FROM middleEntity ee final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS ); - qb.addProjection( null, MIDDLE_ENTITY_ALIAS, false, false ); + qb.addProjection( null, MIDDLE_ENTITY_ALIAS, null, false ); // WHERE // ee.originalId.id_ref_ing = :id_ref_ing referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/ThreeEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/ThreeEntityQueryGenerator.java index 40240b083d..e71334b885 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/ThreeEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/ThreeEntityQueryGenerator.java @@ -115,11 +115,11 @@ public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenera final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; // SELECT new list(ee) FROM middleEntity ee final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS ); - qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS ); - qb.addFrom( indexIdData.getAuditEntityName(), INDEX_ENTITY_ALIAS ); + qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS, false ); + qb.addFrom( indexIdData.getAuditEntityName(), INDEX_ENTITY_ALIAS, false ); qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS + ", " + INDEX_ENTITY_ALIAS, - false, false + null, false ); // WHERE final Parameters rootParameters = qb.getRootParameters(); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityOneAuditedQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityOneAuditedQueryGenerator.java index 0af03513f2..6c2d615b07 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityOneAuditedQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityOneAuditedQueryGenerator.java @@ -82,8 +82,8 @@ public final class TwoEntityOneAuditedQueryGenerator extends AbstractRelationQue final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; // SELECT new list(ee) FROM middleEntity ee final QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS ); - qb.addFrom( referencedIdData.getEntityName(), REFERENCED_ENTITY_ALIAS ); - qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, false, false ); + qb.addFrom( referencedIdData.getEntityName(), REFERENCED_ENTITY_ALIAS, false ); + qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, null, false ); // WHERE final Parameters rootParameters = qb.getRootParameters(); // ee.id_ref_ed = e.id_ref_ed diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityQueryGenerator.java index d63510c010..1ebc3b440c 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityQueryGenerator.java @@ -96,8 +96,8 @@ public final class TwoEntityQueryGenerator extends AbstractRelationQueryGenerato final String eeOriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS + "." + originalIdPropertyName; // SELECT new list(ee) FROM middleEntity ee QueryBuilder qb = new QueryBuilder( versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS ); - qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS ); - qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, false, false ); + qb.addFrom( referencedIdData.getAuditEntityName(), REFERENCED_ENTITY_ALIAS, false ); + qb.addProjection( "new list", MIDDLE_ENTITY_ALIAS + ", " + REFERENCED_ENTITY_ALIAS, null, false ); // WHERE final Parameters rootParameters = qb.getRootParameters(); // ee.id_ref_ed = e.id_ref_ed diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/Parameters.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/Parameters.java index 01d08d58e0..1efa854eac 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/Parameters.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/Parameters.java @@ -164,6 +164,17 @@ public class Parameters { expressions.add( expression.toString() ); } + // compare properties from two different entities (aliases) + public void addWhere(final String aliasLeft, final String left, final String op, final String aliasRight, final String right) { + final StringBuilder expression = new StringBuilder(); + + expression.append( aliasLeft ).append( '.' ).append( left ); + expression.append( ' ' ).append( op ).append( ' ' ); + expression.append( aliasRight ).append( '.' ).append( right ); + + expressions.add( expression.toString() ); + } + public void addWhereWithFunction(String left, String leftFunction, String op, Object paramValue){ final String paramName = generateQueryParam(); localQueryParamValues.put( paramName, paramValue ); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/QueryBuilder.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/QueryBuilder.java index ac8c673c56..49e4ea7cf9 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/QueryBuilder.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/QueryBuilder.java @@ -17,7 +17,7 @@ import org.hibernate.envers.RevisionType; import org.hibernate.envers.internal.entities.RevisionTypeType; import org.hibernate.envers.internal.tools.MutableInteger; import org.hibernate.envers.internal.tools.StringTools; -import org.hibernate.envers.tools.Pair; +import org.hibernate.envers.internal.tools.Triple; import org.hibernate.type.CustomType; /** @@ -39,18 +39,18 @@ public class QueryBuilder { */ private final MutableInteger paramCounter; /** - * Main "where" parameters for this query. + * "where" parameters for this query. Each parameter element of the list for one alias from the "from" part. */ - private final Parameters rootParameters; + private final List parameters = new ArrayList(); /** - * A list of pairs (from entity name, alias name). + * A list of triples (from entity name, alias name, whether to select the entity). */ - private final List> froms; + private final List> froms; /** - * A list of pairs (property name, order ascending?). + * A list of triples (alias, property name, order ascending?). */ - private final List> orders; + private final List> orders; /** * A list of complete projection definitions: either a sole property name, or a function(property name). */ @@ -70,13 +70,14 @@ public class QueryBuilder { this.aliasCounter = aliasCounter; this.paramCounter = paramCounter; - rootParameters = new Parameters( alias, "and", paramCounter ); + final Parameters rootParameters = new Parameters( alias, "and", paramCounter ); + parameters.add( rootParameters ); - froms = new ArrayList>(); - orders = new ArrayList>(); + froms = new ArrayList>(); + orders = new ArrayList>(); projections = new ArrayList(); - addFrom( entityName, alias ); + addFrom( entityName, alias, true ); } // Only for deep copy purpose. @@ -85,10 +86,12 @@ public class QueryBuilder { this.alias = other.alias; this.aliasCounter = other.aliasCounter.deepCopy(); this.paramCounter = other.paramCounter.deepCopy(); - this.rootParameters = other.rootParameters.deepCopy(); + for (final Parameters params : other.parameters) { + this.parameters.add( params.deepCopy() ); + } - froms = new ArrayList>( other.froms ); - orders = new ArrayList>( other.orders ); + froms = new ArrayList>( other.froms ); + orders = new ArrayList>( other.orders ); projections = new ArrayList( other.projections ); } @@ -96,17 +99,25 @@ public class QueryBuilder { return new QueryBuilder( this ); } + /** + * @return the main alias of this query builder + */ + public String getAlias() { + return alias; + } + /** * Add an entity from which to select. * * @param entityName Name of the entity from which to select. * @param alias Alias of the entity. Should be different than all other aliases. + * @param select whether the entity should be selected */ - public void addFrom(String entityName, String alias) { - froms.add( Pair.make( entityName, alias ) ); + public void addFrom(String entityName, String alias, boolean select) { + froms.add( Triple.make( entityName, alias, select ) ); } - private String generateAlias() { + public String generateAlias() { return "_e" + aliasCounter.getAndIncrease(); } @@ -130,26 +141,27 @@ public class QueryBuilder { } public Parameters getRootParameters() { - return rootParameters; + return parameters.get( 0 ); } - public void addOrder(String propertyName, boolean ascending) { - orders.add( Pair.make( propertyName, ascending ) ); + public Parameters addParameters(final String alias) { + final Parameters result = new Parameters( alias, Parameters.AND, paramCounter); + parameters.add( result ); + return result; } - public void addProjection(String function, String propertyName, boolean distinct) { - addProjection( function, propertyName, distinct, true ); + public void addOrder(String alias, String propertyName, boolean ascending) { + orders.add( Triple.make( alias, propertyName, ascending ) ); } - public void addProjection(String function, String propertyName, boolean distinct, boolean addAlias) { + public void addProjection(String function, String alias, String propertyName, boolean distinct) { + final String effectivePropertyName = propertyName == null ? "" : ".".concat( propertyName ); if ( function == null ) { - projections.add( (distinct ? "distinct " : "") + (addAlias ? alias + "." : "") + propertyName ); - } - else { + projections.add( (distinct ? "distinct " : "") + alias + effectivePropertyName ); + } else { projections.add( - function + "(" + (distinct ? "distinct " : "") + (addAlias ? - alias + "." : - "") + propertyName + ")" + function + "(" + (distinct ? "distinct " : "") + alias + + effectivePropertyName + ")" ); } } @@ -169,15 +181,23 @@ public class QueryBuilder { } else { // all aliases separated with commas - StringTools.append( sb, getAliasList().iterator(), ", " ); + StringTools.append( sb, getSelectAliasList().iterator(), ", " ); } sb.append( " from " ); // all from entities with aliases, separated with commas StringTools.append( sb, getFromList().iterator(), ", " ); // where part - rootParameters - if ( !rootParameters.isEmpty() ) { - sb.append( " where " ); - rootParameters.build( sb, queryParamValues ); + boolean first = true; + for (final Parameters params : parameters) { + if (!params.isEmpty()) { + if (first) { + sb.append( " where " ); + first = false; + } else { + sb.append( " and " ); + } + params.build( sb, queryParamValues ); + } } // orders if ( orders.size() > 0 ) { @@ -186,10 +206,12 @@ public class QueryBuilder { } } - private List getAliasList() { + private List getSelectAliasList() { final List aliasList = new ArrayList(); - for ( Pair from : froms ) { - aliasList.add( from.getSecond() ); + for ( Triple from : froms ) { + if ( from.getThird() ) { + aliasList.add( from.getSecond() ); + } } return aliasList; @@ -201,7 +223,7 @@ public class QueryBuilder { private List getFromList() { final List fromList = new ArrayList(); - for ( Pair from : froms ) { + for ( Triple from : froms ) { fromList.add( from.getFirst() + " " + from.getSecond() ); } @@ -210,8 +232,8 @@ public class QueryBuilder { private List getOrderList() { final List orderList = new ArrayList(); - for ( Pair order : orders ) { - orderList.add( alias + "." + order.getFirst() + " " + (order.getSecond() ? "asc" : "desc") ); + for ( Triple order : orders ) { + orderList.add( order.getFirst() + "." + order.getSecond() + " " + (order.getThird() ? "asc" : "desc") ); } return orderList; diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditAssociationQuery.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditAssociationQuery.java new file mode 100644 index 0000000000..ae08eb24ea --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditAssociationQuery.java @@ -0,0 +1,56 @@ +/** + * + */ +package org.hibernate.envers.query; + +import org.hibernate.CacheMode; +import org.hibernate.FlushMode; +import org.hibernate.LockMode; +import org.hibernate.envers.query.criteria.AuditCriterion; +import org.hibernate.envers.query.order.AuditOrder; +import org.hibernate.envers.query.projection.AuditProjection; + +/** + * @author Felix Feisst (feisst dot felix at gmail dot com) + */ +public interface AuditAssociationQuery extends AuditQuery { + + @Override + AuditAssociationQuery add(AuditCriterion criterion); + + @Override + AuditAssociationQuery addOrder(AuditOrder order); + + @Override + AuditAssociationQuery addProjection(AuditProjection projection); + + @Override + AuditAssociationQuery setMaxResults(int maxResults); + + @Override + AuditAssociationQuery setFirstResult(int firstResult); + + @Override + AuditAssociationQuery setCacheable(boolean cacheable); + + @Override + AuditAssociationQuery setCacheRegion(String cacheRegion); + + @Override + AuditAssociationQuery setComment(String comment); + + @Override + AuditAssociationQuery setFlushMode(FlushMode flushMode); + + @Override + AuditAssociationQuery setCacheMode(CacheMode cacheMode); + + @Override + AuditAssociationQuery setTimeout(int timeout); + + @Override + AuditAssociationQuery setLockMode(LockMode lockMode); + + Q up(); + +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditEntity.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditEntity.java index 1ab4d0b6a1..003d2f1d2b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditEntity.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditEntity.java @@ -19,6 +19,8 @@ import org.hibernate.envers.query.internal.property.EntityPropertyName; import org.hibernate.envers.query.internal.property.RevisionNumberPropertyName; import org.hibernate.envers.query.internal.property.RevisionPropertyPropertyName; import org.hibernate.envers.query.internal.property.RevisionTypePropertyName; +import org.hibernate.envers.query.projection.AuditProjection; +import org.hibernate.envers.query.projection.internal.EntityAuditProjection; /** * TODO: ilike @@ -114,4 +116,13 @@ public class AuditEntity { public static AuditDisjunction disjunction() { return new AuditDisjunction(); } + + /** + * Adds a projection to the current entity itself. Useful for + * selecting entities which are reached through associations within the query. + * @param distinct whether to distinct select the entity + */ + public static AuditProjection selectEntity(boolean distinct) { + return new EntityAuditProjection( distinct ); + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditQuery.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditQuery.java index e6242b50d5..1b69e5075e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditQuery.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditQuery.java @@ -9,6 +9,7 @@ package org.hibernate.envers.query; import java.util.List; import javax.persistence.NoResultException; import javax.persistence.NonUniqueResultException; +import javax.persistence.criteria.JoinType; import org.hibernate.CacheMode; import org.hibernate.FlushMode; @@ -27,6 +28,8 @@ public interface AuditQuery { Object getSingleResult() throws AuditException, NonUniqueResultException, NoResultException; + AuditAssociationQuery traverseRelation(String associationName, JoinType joinType); + AuditQuery add(AuditCriterion criterion); AuditQuery addProjection(AuditProjection projection); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java index 6d91fa53f4..b17d5cf5fe 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java @@ -49,6 +49,7 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( @@ -68,17 +69,17 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit // Adding all specified conditions both to the main query, as well as to the // aggregated one. for ( AuditCriterion versionsCriteria : criterions ) { - versionsCriteria.addToQuery( enversService, versionsReader, entityName, qb, subParams ); - versionsCriteria.addToQuery( enversService, versionsReader, entityName, subQb, subQb.getRootParameters() ); + versionsCriteria.addToQuery( enversService, versionsReader, entityName, qb.getAlias(), qb, subParams ); + versionsCriteria.addToQuery( enversService, versionsReader, entityName, subQb.getAlias(), subQb, subQb.getRootParameters() ); } // Setting the desired projection of the aggregated query switch ( mode ) { case MIN: - subQb.addProjection( "min", propertyName, false ); + subQb.addProjection( "min", subQb.getAlias(), propertyName, false ); break; case MAX: - subQb.addProjection( "max", propertyName, false ); + subQb.addProjection( "max", subQb.getAlias(), propertyName, false ); } // Correlating subquery with the outer query by entity id. See JIRA HHH-7827. diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditConjunction.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditConjunction.java index 07433fdbf7..583d478dc3 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditConjunction.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditConjunction.java @@ -35,6 +35,7 @@ public class AuditConjunction implements AuditCriterion, ExtendableCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { Parameters andParameters = parameters.addSubParameters( Parameters.AND ); @@ -44,7 +45,7 @@ public class AuditConjunction implements AuditCriterion, ExtendableCriterion { } else { for ( AuditCriterion criterion : criterions ) { - criterion.addToQuery( enversService, versionsReader, entityName, qb, andParameters ); + criterion.addToQuery( enversService, versionsReader, entityName, alias, qb, andParameters ); } } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditCriterion.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditCriterion.java index fb897f7e9c..d3c08be68b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditCriterion.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditCriterion.java @@ -19,6 +19,7 @@ public interface AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditDisjunction.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditDisjunction.java index a02a745d2d..bd986c27e9 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditDisjunction.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditDisjunction.java @@ -35,6 +35,7 @@ public class AuditDisjunction implements AuditCriterion, ExtendableCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { Parameters orParameters = parameters.addSubParameters( Parameters.OR ); @@ -44,7 +45,7 @@ public class AuditDisjunction implements AuditCriterion, ExtendableCriterion { } else { for ( AuditCriterion criterion : criterions ) { - criterion.addToQuery( enversService, versionsReader, entityName, qb, orParameters ); + criterion.addToQuery( enversService, versionsReader, entityName, alias, qb, orParameters ); } } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditProperty.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditProperty.java index c0f859cbe1..2685d4d1e5 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditProperty.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AuditProperty.java @@ -10,6 +10,7 @@ import java.util.Collection; import org.hibernate.criterion.MatchMode; import org.hibernate.envers.boot.internal.EnversService; +import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.Triple; import org.hibernate.envers.query.criteria.internal.BetweenAuditExpression; import org.hibernate.envers.query.criteria.internal.IlikeAuditExpression; @@ -275,4 +276,10 @@ public class AuditProperty implements AuditProjection { public AuditOrder desc() { return new PropertyAuditOrder( propertyNameGetter, false ); } + + @Override + public Object convertQueryResult(EnversService enversService, EntityInstantiator entityInstantiator, String entityName, Number revision, Object value) { + return value; + } + } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/BetweenAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/BetweenAuditExpression.java index f6f1b10bbe..9e3e458da9 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/BetweenAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/BetweenAuditExpression.java @@ -31,6 +31,7 @@ public class BetweenAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/IdentifierEqAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/IdentifierEqAuditExpression.java index c564690bf4..03d9d6b902 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/IdentifierEqAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/IdentifierEqAuditExpression.java @@ -31,6 +31,7 @@ public class IdentifierEqAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { enversService.getEntitiesConfigurations().get( entityName ) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/IlikeAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/IlikeAuditExpression.java index 4c4520dbe9..5d4186de76 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/IlikeAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/IlikeAuditExpression.java @@ -27,7 +27,7 @@ public class IlikeAuditExpression implements AuditCriterion { public void addToQuery( EnversService enversService, AuditReaderImplementor versionsReader, String entityName, - QueryBuilder qb, Parameters parameters) { + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( enversService, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/InAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/InAuditExpression.java index 2e280a6e00..cf89fb4453 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/InAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/InAuditExpression.java @@ -29,6 +29,7 @@ public class InAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/LogicalAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/LogicalAuditExpression.java index b989fe1e57..f50e91bb1e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/LogicalAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/LogicalAuditExpression.java @@ -30,11 +30,12 @@ public class LogicalAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { Parameters opParameters = parameters.addSubParameters( op ); - lhs.addToQuery( enversService, versionsReader, entityName, qb, opParameters.addSubParameters( "and" ) ); - rhs.addToQuery( enversService, versionsReader, entityName, qb, opParameters.addSubParameters( "and" ) ); + lhs.addToQuery( enversService, versionsReader, entityName, alias, qb, opParameters.addSubParameters( "and" ) ); + rhs.addToQuery( enversService, versionsReader, entityName, alias, qb, opParameters.addSubParameters( "and" ) ); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NotAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NotAuditExpression.java index af67bc955c..ef147f175f 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NotAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NotAuditExpression.java @@ -26,8 +26,9 @@ public class NotAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { - criterion.addToQuery( enversService, versionsReader, entityName, qb, parameters.addNegatedParameters() ); + criterion.addToQuery( enversService, versionsReader, entityName, alias, qb, parameters.addNegatedParameters() ); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NotNullAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NotNullAuditExpression.java index ea2e2d82b9..777b302576 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NotNullAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NotNullAuditExpression.java @@ -28,6 +28,7 @@ public class NotNullAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NullAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NullAuditExpression.java index f413024ce4..4a6771f7f4 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NullAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/NullAuditExpression.java @@ -28,6 +28,7 @@ public class NullAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/PropertyAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/PropertyAuditExpression.java index 7112f35def..0e439f33ba 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/PropertyAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/PropertyAuditExpression.java @@ -28,8 +28,10 @@ public class PropertyAuditExpression implements AuditCriterion { } public void addToQuery( - EnversService enversService, AuditReaderImplementor versionsReader, + EnversService enversService, + AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/RelatedAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/RelatedAuditExpression.java index c818e8340a..ad3811a157 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/RelatedAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/RelatedAuditExpression.java @@ -34,6 +34,7 @@ public class RelatedAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/RevisionTypeAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/RevisionTypeAuditExpression.java index 1b6a586a3f..6370999e46 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/RevisionTypeAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/RevisionTypeAuditExpression.java @@ -29,6 +29,7 @@ public class RevisionTypeAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { parameters.addWhereWithParam( enversService.getAuditEntitiesConfiguration().getRevisionTypePropName(), op, value ); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/SimpleAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/SimpleAuditExpression.java index ecce5320eb..ab1e0db4ce 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/SimpleAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/internal/SimpleAuditExpression.java @@ -33,6 +33,7 @@ public class SimpleAuditExpression implements AuditCriterion { EnversService enversService, AuditReaderImplementor versionsReader, String entityName, + String alias, QueryBuilder qb, Parameters parameters) { String propertyName = CriteriaTools.determinePropertyName( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AbstractAuditQuery.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AbstractAuditQuery.java index 4a9f221c42..90e3c52d7a 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AbstractAuditQuery.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AbstractAuditQuery.java @@ -7,9 +7,13 @@ package org.hibernate.envers.query.internal.impl; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; + import javax.persistence.NoResultException; import javax.persistence.NonUniqueResultException; +import javax.persistence.criteria.JoinType; import org.hibernate.CacheMode; import org.hibernate.FlushMode; @@ -22,6 +26,7 @@ import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.reader.AuditReaderImplementor; import org.hibernate.envers.internal.tools.Triple; import org.hibernate.envers.internal.tools.query.QueryBuilder; +import org.hibernate.envers.query.AuditAssociationQuery; import org.hibernate.envers.query.AuditQuery; import org.hibernate.envers.query.criteria.AuditCriterion; import org.hibernate.envers.query.criteria.internal.CriteriaTools; @@ -35,7 +40,7 @@ import static org.hibernate.envers.internal.entities.mapper.relation.query.Query * @author Adam Warski (adam at warski dot org) * @author HernпїЅn Chanfreau */ -public abstract class AbstractAuditQuery implements AuditQuery { +public abstract class AbstractAuditQuery implements AuditQueryImplementor { protected EntityInstantiator entityInstantiator; protected List criterions; @@ -44,12 +49,15 @@ public abstract class AbstractAuditQuery implements AuditQuery { protected String versionsEntityName; protected QueryBuilder qb; - protected boolean hasProjection; protected boolean hasOrder; protected final EnversService enversService; protected final AuditReaderImplementor versionsReader; + protected final List> associationQueries = new ArrayList>(); + protected final Map> associationQueryMap = new HashMap>(); + protected final List> projections = new ArrayList>(); + protected AbstractAuditQuery( EnversService enversService, AuditReaderImplementor versionsReader, @@ -116,17 +124,26 @@ public abstract class AbstractAuditQuery implements AuditQuery { public AuditQuery addProjection(AuditProjection projection) { Triple projectionData = projection.getData( enversService ); - hasProjection = true; + registerProjection( entityName, projection ); String propertyName = CriteriaTools.determinePropertyName( enversService, versionsReader, entityName, projectionData.getSecond() ); - qb.addProjection( projectionData.getFirst(), propertyName, projectionData.getThird() ); + qb.addProjection( projectionData.getFirst(), REFERENCED_ENTITY_ALIAS, propertyName, projectionData.getThird() ); return this; } + @Override + public void registerProjection(String entityName, AuditProjection projection) { + projections.add( Pair.make( entityName, projection ) ); + } + + protected boolean hasProjection() { + return !projections.isEmpty(); + } + public AuditQuery addOrder(AuditOrder order) { hasOrder = true; Pair orderData = order.getData( enversService ); @@ -136,10 +153,21 @@ public abstract class AbstractAuditQuery implements AuditQuery { entityName, orderData.getFirst() ); - qb.addOrder( propertyName, orderData.getSecond() ); + qb.addOrder( REFERENCED_ENTITY_ALIAS, propertyName, orderData.getSecond() ); return this; } + @Override + public AuditAssociationQuery traverseRelation(String associationName, JoinType joinType) { + AuditAssociationQueryImplementor result = associationQueryMap.get( associationName ); + if (result == null) { + result = new AuditAssociationQueryImplementor( enversService, versionsReader, this, qb, entityName, associationName, joinType, REFERENCED_ENTITY_ALIAS ); + associationQueries.add( result ); + associationQueryMap.put( associationName, result ); + } + return result; + } + // Query properties private Integer maxResults; @@ -248,4 +276,29 @@ public abstract class AbstractAuditQuery implements AuditQuery { query.setLockMode( REFERENCED_ENTITY_ALIAS, lockOptions.getLockMode() ); } } + + protected List applyProjections(final List queryResult, final Number revision) { + final List result = new ArrayList( queryResult.size() ); + if ( hasProjection() ) { + for (final Object qr : queryResult) { + if ( projections.size() == 1 ) { + // qr is the value of the projection itself + final Pair projection = projections.get( 0 ); + result.add( projection.getSecond().convertQueryResult( enversService, entityInstantiator, projection.getFirst(), revision, qr ) ); + } else { + // qr is an array where each of its components holds the value of corresponding projection + Object[] qresults = (Object[]) qr; + Object[] tresults = new Object[qresults.length]; + for ( int i = 0; i < qresults.length; i++ ) { + final Pair projection = projections.get( i ); + tresults[i] = projection.getSecond().convertQueryResult( enversService, entityInstantiator, projection.getFirst(), revision, qresults[i] ); + } + result.add( tresults ); + } + } + } else { + entityInstantiator.addInstancesFromVersionsEntities( entityName, result, queryResult, revision ); + } + return result; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AuditAssociationQueryImplementor.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AuditAssociationQueryImplementor.java new file mode 100644 index 0000000000..c917cd084e --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AuditAssociationQueryImplementor.java @@ -0,0 +1,263 @@ +/** + * + */ +package org.hibernate.envers.query.internal.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.persistence.NoResultException; +import javax.persistence.NonUniqueResultException; +import javax.persistence.criteria.JoinType; + +import org.hibernate.CacheMode; +import org.hibernate.FlushMode; +import org.hibernate.LockMode; +import org.hibernate.envers.boot.internal.EnversService; +import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; +import org.hibernate.envers.exception.AuditException; +import org.hibernate.envers.internal.entities.RelationDescription; +import org.hibernate.envers.internal.entities.mapper.id.IdMapper; +import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData; +import org.hibernate.envers.internal.reader.AuditReaderImplementor; +import org.hibernate.envers.internal.tools.Triple; +import org.hibernate.envers.internal.tools.query.Parameters; +import org.hibernate.envers.internal.tools.query.QueryBuilder; +import org.hibernate.envers.query.AuditAssociationQuery; +import org.hibernate.envers.query.criteria.AuditCriterion; +import org.hibernate.envers.query.criteria.internal.CriteriaTools; +import org.hibernate.envers.query.order.AuditOrder; +import org.hibernate.envers.query.projection.AuditProjection; +import org.hibernate.envers.tools.Pair; + +/** + * @author Felix Feisst (feisst dot felix at gmail dot com) + */ +public class AuditAssociationQueryImplementor implements AuditAssociationQuery, AuditQueryImplementor { + + private final EnversService enversService; + private final AuditReaderImplementor auditReader; + private final Q parent; + private final QueryBuilder queryBuilder; + private final JoinType joinType; + private final String entityName; + private final IdMapper ownerAssociationIdMapper; + private final String ownerAlias; + private final String alias; + private final List criterions = new ArrayList(); + private final Parameters parameters; + private final List> associationQueries = new ArrayList>(); + private final Map>> associationQueryMap = new HashMap>>(); + private boolean hasProjections; + private boolean hasOrders; + + public AuditAssociationQueryImplementor(final EnversService enversService, final AuditReaderImplementor auditReader, final Q parent, + final QueryBuilder queryBuilder, final String ownerEntityName, final String propertyName, final JoinType joinType, final String ownerAlias) { + this.enversService = enversService; + this.auditReader = auditReader; + this.parent = parent; + this.queryBuilder = queryBuilder; + this.joinType = joinType; + + final RelationDescription relationDescription = CriteriaTools.getRelatedEntity( enversService, ownerEntityName, propertyName ); + if ( relationDescription == null ) { + throw new IllegalArgumentException( "Property " + propertyName + " of entity " + ownerEntityName + " is not a valid association for queries" ); + } + this.entityName = relationDescription.getToEntityName(); + this.ownerAssociationIdMapper = relationDescription.getIdMapper(); + this.ownerAlias = ownerAlias; + alias = queryBuilder.generateAlias(); + parameters = queryBuilder.addParameters( alias ); + } + + @Override + public List getResultList() throws AuditException { + return parent.getResultList(); + } + + @Override + public Object getSingleResult() throws AuditException, NonUniqueResultException, NoResultException { + return parent.getSingleResult(); + } + + @Override + public AuditAssociationQueryImplementor> traverseRelation(String associationName, JoinType joinType) { + AuditAssociationQueryImplementor> result = associationQueryMap.get( associationName ); + if (result == null) { + result = new AuditAssociationQueryImplementor>( + enversService, auditReader, this, queryBuilder, entityName, associationName, joinType, alias ); + associationQueries.add( result ); + associationQueryMap.put( associationName, result ); + } + return result; + } + + @Override + public AuditAssociationQueryImplementor add(AuditCriterion criterion) { + criterions.add( criterion ); + return this; + } + + @Override + public AuditAssociationQueryImplementor addProjection(AuditProjection projection) { + hasProjections = true; + Triple projectionData = projection.getData( enversService ); + String propertyName = CriteriaTools.determinePropertyName( enversService, auditReader, entityName, projectionData.getSecond() ); + queryBuilder.addProjection( projectionData.getFirst(), alias, propertyName, projectionData.getThird() ); + registerProjection( entityName, projection ); + return this; + } + + @Override + public AuditAssociationQueryImplementor addOrder(AuditOrder order) { + hasOrders = true; + Pair orderData = order.getData( enversService ); + String propertyName = CriteriaTools.determinePropertyName( enversService, auditReader, entityName, orderData.getFirst() ); + queryBuilder.addOrder( alias, propertyName, orderData.getSecond() ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setMaxResults(int maxResults) { + parent.setMaxResults( maxResults ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setFirstResult(int firstResult) { + parent.setFirstResult( firstResult ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setCacheable(boolean cacheable) { + parent.setCacheable( cacheable ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setCacheRegion(String cacheRegion) { + parent.setCacheRegion( cacheRegion ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setComment(String comment) { + parent.setComment( comment ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setFlushMode(FlushMode flushMode) { + parent.setFlushMode( flushMode ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setCacheMode(CacheMode cacheMode) { + parent.setCacheMode( cacheMode ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setTimeout(int timeout) { + parent.setTimeout( timeout ); + return this; + } + + @Override + public AuditAssociationQueryImplementor setLockMode(LockMode lockMode) { + parent.setLockMode( lockMode ); + return this; + } + + public Q up() { + return parent; + } + + protected boolean hasCriterions() { + boolean result = !criterions.isEmpty(); + if ( !result ) { + for ( final AuditAssociationQueryImplementor sub : associationQueries ) { + if ( sub.hasCriterions() ) { + result = true; + break; + } + } + } + return result; + } + + protected boolean hasOrders() { + boolean result = hasOrders; + if ( !result ) { + for ( final AuditAssociationQueryImplementor sub : associationQueries ) { + if ( sub.hasOrders() ) { + result = true; + break; + } + } + } + return result; + } + + protected boolean hasProjections() { + boolean result = hasProjections; + if ( !result ) { + for ( final AuditAssociationQueryImplementor sub : associationQueries ) { + if ( sub.hasProjections() ) { + result = true; + break; + } + } + } + return result; + } + + protected void addCriterionsToQuery(AuditReaderImplementor versionsReader) { + if ( hasCriterions() || hasOrders() || hasProjections() ) { + if ( enversService.getEntitiesConfigurations().isVersioned( entityName ) ) { + String auditEntityName = enversService.getAuditEntitiesConfiguration().getAuditEntityName( entityName ); + queryBuilder.addFrom( auditEntityName, alias, false ); + + // owner.reference_id = target.originalId.id + AuditEntitiesConfiguration verEntCfg = enversService.getAuditEntitiesConfiguration(); + String originalIdPropertyName = verEntCfg.getOriginalIdPropName(); + IdMapper idMapperTarget = enversService.getEntitiesConfigurations().get( entityName ).getIdMapper(); + final String prefix = alias.concat( "." ).concat( originalIdPropertyName ); + ownerAssociationIdMapper.addIdsEqualToQuery( queryBuilder.getRootParameters(), ownerAlias, idMapperTarget, prefix ); + + // filter reference of target entity + String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); + MiddleIdData referencedIdData = new MiddleIdData( verEntCfg, enversService.getEntitiesConfigurations().get( entityName ).getIdMappingData(), null, entityName, + enversService.getEntitiesConfigurations().isVersioned( entityName ) ); + enversService.getAuditStrategy().addEntityAtRevisionRestriction( enversService.getGlobalConfiguration(), queryBuilder, parameters, revisionPropertyPath, + verEntCfg.getRevisionEndFieldName(), true, referencedIdData, revisionPropertyPath, originalIdPropertyName, alias, + queryBuilder.generateAlias(), true ); + } + else { + queryBuilder.addFrom( entityName, alias, false ); + // owner.reference_id = target.id + IdMapper idMapperTarget = enversService.getEntitiesConfigurations().getNotVersionEntityConfiguration( entityName ).getIdMapper(); + ownerAssociationIdMapper.addIdsEqualToQuery( queryBuilder.getRootParameters(), ownerAlias, idMapperTarget, alias ); + } + + for ( AuditCriterion criterion : criterions ) { + criterion.addToQuery( enversService, versionsReader, entityName, alias, queryBuilder, parameters ); + } + + for ( final AuditAssociationQueryImplementor sub : associationQueries ) { + sub.addCriterionsToQuery( versionsReader ); + } + } + + } + + @Override + public void registerProjection(final String entityName, AuditProjection projection) { + parent.registerProjection( entityName, projection ); + } + +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AuditQueryImplementor.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AuditQueryImplementor.java new file mode 100644 index 0000000000..266959ca25 --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/AuditQueryImplementor.java @@ -0,0 +1,16 @@ +/** + * + */ +package org.hibernate.envers.query.internal.impl; + +import org.hibernate.envers.query.AuditQuery; +import org.hibernate.envers.query.projection.AuditProjection; + +/** + * @author Felix Feisst (feisst dot felix at gmail dot com) + */ +interface AuditQueryImplementor extends AuditQuery { + + void registerProjection(final String entityName, final AuditProjection projection); + +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesAtRevisionQuery.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesAtRevisionQuery.java index 85d2bde8a6..60585d8872 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesAtRevisionQuery.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesAtRevisionQuery.java @@ -6,7 +6,6 @@ */ package org.hibernate.envers.query.internal.impl; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -15,6 +14,7 @@ import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData; +import org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants; import org.hibernate.envers.internal.reader.AuditReaderImplementor; import org.hibernate.envers.query.criteria.AuditCriterion; @@ -50,7 +50,6 @@ public class EntitiesAtRevisionQuery extends AbstractAuditQuery { this.includeDeletions = includeDeletions; } - @SuppressWarnings({"unchecked"}) public List list() { /* * The query that we need to create: @@ -105,25 +104,27 @@ public class EntitiesAtRevisionQuery extends AbstractAuditQuery { // all specified conditions for ( AuditCriterion criterion : criterions ) { - criterion.addToQuery( enversService, versionsReader, entityName, qb, qb.getRootParameters() ); + criterion.addToQuery( + enversService, + versionsReader, + entityName, + QueryConstants.REFERENCED_ENTITY_ALIAS, + qb, + qb.getRootParameters() + ); + } + + for (final AuditAssociationQueryImplementor associationQuery : associationQueries) { + associationQuery.addCriterionsToQuery( versionsReader ); } Query query = buildQuery(); - // add named parameter (only used for ValidAuditTimeStrategy) + // add named parameter (used for ValidityAuditStrategy and association queries) List params = Arrays.asList( query.getNamedParameters() ); if ( params.contains( REVISION_PARAMETER ) ) { query.setParameter( REVISION_PARAMETER, revision ); } List queryResult = query.list(); - - if ( hasProjection ) { - return queryResult; - } - else { - List result = new ArrayList(); - entityInstantiator.addInstancesFromVersionsEntities( entityName, result, queryResult, revision ); - - return result; - } + return applyProjections( queryResult, revision ); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesModifiedAtRevisionQuery.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesModifiedAtRevisionQuery.java index dbd8674ef0..f6a2e1c04e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesModifiedAtRevisionQuery.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesModifiedAtRevisionQuery.java @@ -6,12 +6,12 @@ */ package org.hibernate.envers.query.internal.impl; -import java.util.ArrayList; import java.util.List; import org.hibernate.Query; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; +import org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants; import org.hibernate.envers.internal.reader.AuditReaderImplementor; import org.hibernate.envers.query.criteria.AuditCriterion; @@ -60,20 +60,22 @@ public class EntitiesModifiedAtRevisionQuery extends AbstractAuditQuery { // all specified conditions for ( AuditCriterion criterion : criterions ) { - criterion.addToQuery( enversService, versionsReader, entityName, qb, qb.getRootParameters() ); + criterion.addToQuery( + enversService, + versionsReader, + entityName, + QueryConstants.REFERENCED_ENTITY_ALIAS, + qb, + qb.getRootParameters() + ); + } + + for (final AuditAssociationQueryImplementor associationQuery : associationQueries) { + associationQuery.addCriterionsToQuery(versionsReader); } Query query = buildQuery(); List queryResult = query.list(); - - if ( hasProjection ) { - return queryResult; - } - else { - List result = new ArrayList(); - entityInstantiator.addInstancesFromVersionsEntities( entityName, result, queryResult, revision ); - - return result; - } + return applyProjections(queryResult, revision); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/RevisionsOfEntityQuery.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/RevisionsOfEntityQuery.java index d60ee08dab..d0d83c00c9 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/RevisionsOfEntityQuery.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/RevisionsOfEntityQuery.java @@ -10,11 +10,16 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import javax.persistence.criteria.JoinType; + import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.exception.AuditException; +import org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants; import org.hibernate.envers.internal.reader.AuditReaderImplementor; +import org.hibernate.envers.query.AuditAssociationQuery; +import org.hibernate.envers.query.AuditQuery; import org.hibernate.envers.query.criteria.AuditCriterion; import org.hibernate.proxy.HibernateProxy; @@ -86,16 +91,23 @@ public class RevisionsOfEntityQuery extends AbstractAuditQuery { // all specified conditions, transformed for ( AuditCriterion criterion : criterions ) { - criterion.addToQuery( enversService, versionsReader, entityName, qb, qb.getRootParameters() ); + criterion.addToQuery( + enversService, + versionsReader, + entityName, + QueryConstants.REFERENCED_ENTITY_ALIAS, + qb, + qb.getRootParameters() + ); } - if ( !hasProjection && !hasOrder ) { + if ( !hasProjection() && !hasOrder ) { String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); - qb.addOrder( revisionPropertyPath, true ); + qb.addOrder( QueryConstants.REFERENCED_ENTITY_ALIAS, revisionPropertyPath, true ); } if ( !selectEntitiesOnly ) { - qb.addFrom( enversService.getAuditEntitiesConfiguration().getRevisionInfoEntityName(), "r" ); + qb.addFrom( enversService.getAuditEntitiesConfiguration().getRevisionInfoEntityName(), "r", true ); qb.getRootParameters().addWhere( enversService.getAuditEntitiesConfiguration().getRevisionNumberPath(), true, @@ -106,7 +118,7 @@ public class RevisionsOfEntityQuery extends AbstractAuditQuery { } List queryResult = buildAndExecuteQuery(); - if ( hasProjection ) { + if ( hasProjection() ) { return queryResult; } else { @@ -146,4 +158,10 @@ public class RevisionsOfEntityQuery extends AbstractAuditQuery { return entities; } } + + @Override + public AuditAssociationQuery traverseRelation(String associationName, JoinType joinType) { + throw new UnsupportedOperationException( "Not yet implemented for revisions of entity queries" ); + } + } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/AuditProjection.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/AuditProjection.java index 47543f81bd..58aa14c789 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/AuditProjection.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/AuditProjection.java @@ -7,6 +7,7 @@ package org.hibernate.envers.query.projection; import org.hibernate.envers.boot.internal.EnversService; +import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.Triple; /** @@ -19,4 +20,20 @@ public interface AuditProjection { * @return A triple: (function name - possibly null, property name, add distinct?). */ Triple getData(EnversService enversService); + + /** + * @param enversService the Envers service + * @param entityInstantiator the entity instantiator + * @param entityName the name of the entity for which the projection has been added + * @param revision the revision + * @param value the value to convert + * @return the converted value + */ + Object convertQueryResult( + final EnversService enversService, + final EntityInstantiator entityInstantiator, + final String entityName, + final Number revision, + final Object value + ); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/internal/EntityAuditProjection.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/internal/EntityAuditProjection.java new file mode 100644 index 0000000000..69cbb6d3d9 --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/internal/EntityAuditProjection.java @@ -0,0 +1,42 @@ +/** + * + */ +package org.hibernate.envers.query.projection.internal; + +import java.util.Map; + +import org.hibernate.envers.boot.internal.EnversService; +import org.hibernate.envers.internal.entities.EntityInstantiator; +import org.hibernate.envers.internal.tools.Triple; +import org.hibernate.envers.query.projection.AuditProjection; + +/** + * @author Felix Feisst (feisst dot felix at gmail dot com) + */ +public class EntityAuditProjection implements AuditProjection { + + private final boolean distinct; + + public EntityAuditProjection(final boolean distinct) { + this.distinct = distinct; + } + + @Override + public Triple getData(final EnversService enversService) { + // no property is selected, instead the whole entity (alias) is selected + return Triple.make( null, null, distinct ); + } + + @Override + public Object convertQueryResult(final EnversService enversService, final EntityInstantiator entityInstantiator, final String entityName, + final Number revision, final Object value) { + final Object result; + if ( enversService.getEntitiesConfigurations().isVersioned( entityName ) ) { + result = entityInstantiator.createInstanceFromVersionsEntity( entityName, (Map) value, revision ); + } else { + result = value; + } + return result; + } + +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/internal/PropertyAuditProjection.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/internal/PropertyAuditProjection.java index bd55eb9b7b..f012b59d28 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/internal/PropertyAuditProjection.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/projection/internal/PropertyAuditProjection.java @@ -7,6 +7,7 @@ package org.hibernate.envers.query.projection.internal; import org.hibernate.envers.boot.internal.EnversService; +import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.Triple; import org.hibernate.envers.query.internal.property.PropertyNameGetter; import org.hibernate.envers.query.projection.AuditProjection; @@ -31,4 +32,9 @@ public class PropertyAuditProjection implements AuditProjection { return Triple.make( function, propertyName, distinct ); } + + @Override + public Object convertQueryResult(EnversService enversService, EntityInstantiator entityInstantiator, String entityName, Number revision, Object value) { + return value; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java index 410e9d129f..adda5922ab 100755 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java @@ -75,7 +75,7 @@ public class DefaultAuditStrategy implements AuditStrategy { // create a subquery builder // SELECT max(e.revision) FROM versionsReferencedEntity e2 QueryBuilder maxERevQb = rootQueryBuilder.newSubQueryBuilder( idData.getAuditEntityName(), alias2 ); - maxERevQb.addProjection( "max", revisionPropertyPath, false ); + maxERevQb.addProjection( "max", alias2, revisionPropertyPath, false ); // WHERE Parameters maxERevQbParameters = maxERevQb.getRootParameters(); // e2.revision <= :revision @@ -110,7 +110,7 @@ public class DefaultAuditStrategy implements AuditStrategy { versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS_DEF_AUD_STR ); - maxEeRevQb.addProjection( "max", revisionPropertyPath, false ); + maxEeRevQb.addProjection( "max", MIDDLE_ENTITY_ALIAS_DEF_AUD_STR, revisionPropertyPath, false ); // WHERE Parameters maxEeRevQbParameters = maxEeRevQb.getRootParameters(); // ee2.revision <= :revision diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/AssociationToOneQueryTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/AssociationToOneQueryTest.java new file mode 100644 index 0000000000..54101ed257 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/AssociationToOneQueryTest.java @@ -0,0 +1,162 @@ +/** + * + */ +package org.hibernate.envers.test.integration.query; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.JoinType; + +import org.hibernate.envers.AuditReader; +import org.hibernate.envers.query.AuditEntity; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.integration.query.entities.Address; +import org.hibernate.envers.test.integration.query.entities.Car; +import org.hibernate.envers.test.integration.query.entities.Person; +import org.junit.Test; + +/** + * @author Felix Feisst (feisst dot felix at gmail dot com) + */ +public class AssociationToOneQueryTest extends BaseEnversJPAFunctionalTestCase { + + private Car vw; + private Car ford; + private Car toyota; + private Address address1; + private Address address2; + private Person vwOwner; + private Person fordOwner; + private Person toyotaOwner; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Car.class, Person.class, Address.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + // revision 1 + em.getTransaction().begin(); + address1 = new Address( "Freiburgerstrasse", 5 ); + em.persist( address1 ); + address2 = new Address( "Hindenburgstrasse", 20 ); + em.persist( address2 ); + vwOwner = new Person( "VW owner", 20, address1 ); + em.persist( vwOwner ); + fordOwner = new Person( "Ford owner", 30, address1 ); + em.persist( fordOwner ); + toyotaOwner = new Person( "Toyota owner", 30, address2 ); + em.persist( toyotaOwner ); + final Person nonOwner = new Person( "NonOwner", 30, address1 ); + em.persist( nonOwner ); + vw = new Car( "VW" ); + vw.setOwner( vwOwner ); + em.persist( vw ); + ford = new Car( "Ford" ); + ford.setOwner( fordOwner ); + em.persist( ford ); + toyota = new Car( "Toyota" ); + toyota.setOwner( toyotaOwner ); + em.persist( toyota ); + em.getTransaction().commit(); + + // revision 2 + em.getTransaction().begin(); + toyotaOwner.setAge( 40 ); + em.getTransaction().commit(); + } + + @Test + public void testAssociationQuery() { + + final AuditReader auditReader = getAuditReader(); + final Car result1 = (Car) auditReader.createQuery().forEntitiesAtRevision( Car.class, 1 ).traverseRelation( "owner", JoinType.INNER ) + .add( AuditEntity.property( "name" ).like( "Ford%" ) ).getSingleResult(); + assertEquals( "Unexpected single car at revision 1", ford.getId(), result1.getId() ); + + Car result2 = (Car) auditReader.createQuery().forEntitiesAtRevision( Car.class, 1 ).traverseRelation( "owner", JoinType.INNER ).traverseRelation( "address", JoinType.INNER ) + .add( AuditEntity.property( "number" ).eq( 20 ) ).getSingleResult(); + assertEquals( "Unexpected single car at revision 1", toyota.getId(), result2.getId() ); + + List resultList1 = auditReader.createQuery().forEntitiesAtRevision( Car.class, 1 ).traverseRelation( "owner", JoinType.INNER ) + .add( AuditEntity.property( "age" ).ge( 30 ) ).add( AuditEntity.property( "age" ).lt( 40 ) ).up() + .addOrder( AuditEntity.property( "make" ).asc() ).getResultList(); + assertEquals( "Unexpected number of cars for query in revision 1", 2, resultList1.size() ); + assertEquals( "Unexpected car at index 0 in revision 1", ford.getId(), resultList1.get( 0 ).getId() ); + assertEquals( "Unexpected car at index 1 in revision 2", toyota.getId(), resultList1.get( 1 ).getId() ); + + Car result3 = (Car) auditReader.createQuery().forEntitiesAtRevision( Car.class, 2 ).traverseRelation( "owner", JoinType.INNER ) + .add( AuditEntity.property( "age" ).ge( 30 ) ).add( AuditEntity.property( "age" ).lt( 40 ) ).up() + .addOrder( AuditEntity.property( "make" ).asc() ).getSingleResult(); + assertEquals( "Unexpected car at revision 2", ford.getId(), result3.getId() ); + } + + @Test + public void testAssociationQueryWithOrdering() { + + AuditReader auditReader = getAuditReader(); + + List cars1 = auditReader.createQuery().forEntitiesAtRevision( Car.class, 1 ).traverseRelation( "owner", JoinType.INNER ).traverseRelation( "address", JoinType.INNER ) + .addOrder( AuditEntity.property( "number" ).asc() ).up().addOrder( AuditEntity.property( "age" ).desc() ).getResultList(); + assertEquals( "Unexpected number of results", 3, cars1.size() ); + assertEquals( "Unexpected car at index 0", ford.getId(), cars1.get( 0 ).getId() ); + assertEquals( "Unexpected car at index 1", vw.getId(), cars1.get( 1 ).getId() ); + assertEquals( "Unexpected car at index 2", toyota.getId(), cars1.get( 2 ).getId() ); + + List cars2 = auditReader.createQuery().forEntitiesAtRevision( Car.class, 1 ).traverseRelation( "owner", JoinType.INNER ).traverseRelation( "address", JoinType.INNER ) + .addOrder( AuditEntity.property( "number" ).asc() ).up().addOrder( AuditEntity.property( "age" ).asc() ).getResultList(); + assertEquals( "Unexpected number of results", 3, cars2.size() ); + assertEquals( "Unexpected car at index 0", vw.getId(), cars2.get( 0 ).getId() ); + assertEquals( "Unexpected car at index 1", ford.getId(), cars2.get( 1 ).getId() ); + assertEquals( "Unexpected car at index 2", toyota.getId(), cars2.get( 2 ).getId() ); + + } + + @Test + public void testAssociationQueryWithProjection() { + + AuditReader auditReader = getAuditReader(); + + List list1 = auditReader.createQuery().forEntitiesAtRevision( Car.class, 2 ).traverseRelation( "owner", JoinType.INNER ) + .addProjection( AuditEntity.property( "age" ) ).addOrder( AuditEntity.property( "age" ).asc() ).getResultList(); + assertEquals( "Unexpected number of results", 3, list1.size() ); + assertEquals( "Unexpected age at index 0", Integer.valueOf( 20 ), list1.get( 0 ) ); + assertEquals( "Unexpected age at index 0", Integer.valueOf( 30 ), list1.get( 1 ) ); + assertEquals( "Unexpected age at index 0", Integer.valueOf( 40 ), list1.get( 2 ) ); + + List
list2 = auditReader.createQuery().forEntitiesAtRevision( Car.class, 2 ).traverseRelation( "owner", JoinType.INNER ) + .addOrder( AuditEntity.property( "age" ).asc() ).traverseRelation( "address", JoinType.INNER ).addProjection( AuditEntity.selectEntity( false ) ).getResultList(); + assertEquals( "Unexpected number of results", 3, list2.size() ); + assertEquals( "Unexpected address at index 0", address1.getId(), list2.get( 0 ).getId() ); + assertEquals( "Unexpected address at index 1", address1.getId(), list2.get( 1 ).getId() ); + assertEquals( "Unexpected address at index 2", address2.getId(), list2.get( 2 ).getId() ); + + List
list3 = auditReader.createQuery().forEntitiesAtRevision( Car.class, 2 ).traverseRelation( "owner", JoinType.INNER ).traverseRelation( "address", JoinType.INNER ) + .addProjection( AuditEntity.selectEntity( true ) ).addOrder( AuditEntity.property( "number" ).asc() ).getResultList(); + assertEquals( "Unexpected number of results", 2, list3.size() ); + assertEquals( "Unexpected address at index 0", address1.getId(), list3.get( 0 ).getId() ); + assertEquals( "Unexpected address at index 1", address2.getId(), list3.get( 1 ).getId() ); + + List list4 = auditReader.createQuery().forEntitiesAtRevision( Car.class, 2 ).traverseRelation( "owner", JoinType.INNER ) + .addOrder( AuditEntity.property( "age" ).asc() ).addProjection( AuditEntity.selectEntity( false ) ).traverseRelation( "address", JoinType.INNER ) + .addProjection( AuditEntity.property( "number" ) ).getResultList(); + assertEquals( "Unexpected number of results", 3, list4.size() ); + final Object[] index0 = list4.get( 0 ); + assertEquals( "Unexpected owner at index 0", vwOwner.getId(), ( (Person) index0[0] ).getId() ); + assertEquals( "Unexpected number at index 0", Integer.valueOf( 5 ), index0[1] ); + final Object[] index1 = list4.get( 1 ); + assertEquals( "Unexpected owner at index 1", fordOwner.getId(), ( (Person) index1[0] ).getId() ); + assertEquals( "Unexpected number at index 1", Integer.valueOf( 5 ), index1[1] ); + final Object[] index2 = list4.get( 2 ); + assertEquals( "Unexpected owner at index 2", toyotaOwner.getId(), ( (Person) index2[0] ).getId() ); + assertEquals( "Unexpected number at index 2", Integer.valueOf( 20 ), index2[1] ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Address.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Address.java new file mode 100644 index 0000000000..e2f83417d1 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Address.java @@ -0,0 +1,56 @@ +/** + * + */ +package org.hibernate.envers.test.integration.query.entities; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Felix Feisst (feisst dot felix at gmail dot com) + */ +@Entity +public class Address { + + @Id + @GeneratedValue + private Long id; + + private String street; + private int number; + + public Address() { + + } + + public Address(String street, int number) { + this.street = street; + this.number = number; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } + +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Car.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Car.java new file mode 100644 index 0000000000..8c828568cb --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Car.java @@ -0,0 +1,74 @@ +/** + * + */ +package org.hibernate.envers.test.integration.query.entities; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; + +import org.hibernate.envers.Audited; + +/** + * @author Felix Feisst (feisst dot felix at gmail dot com) + */ +@Entity +@Audited +public class Car { + + @Id + @GeneratedValue + private Long id; + + private String make; + @ManyToOne + private Person owner; + @ManyToMany + private Set drivers = new HashSet(); + + public Car() { + + } + + public Car(final String make) { + this.make = make; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getMake() { + return make; + } + + public void setMake(String make) { + this.make = make; + } + + public Person getOwner() { + return owner; + } + + public void setOwner(Person owner) { + this.owner = owner; + } + + public Set getDrivers() { + return drivers; + } + + public void setDrivers(Set drivers) { + this.drivers = drivers; + } + +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Person.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Person.java new file mode 100644 index 0000000000..ccb01bfd07 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/entities/Person.java @@ -0,0 +1,73 @@ +/** + * + */ +package org.hibernate.envers.test.integration.query.entities; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; + +/** + * @author Felix Feisst (feisst dot felix at gmail dot com) + */ +@Entity +@Audited +public class Person { + + @Id + @GeneratedValue + private Long id; + + private String name; + private int age; + @ManyToOne + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) + private Address address; + + public Person() { + + } + + public Person(final String name, final int age, final Address address) { + this.name = name; + this.age = age; + this.address = address; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + +}