HHH-3555 - Extendend Envers Criteria API to express relation traversion
(traverseRelation) with different Join Types. Implementation is done for inner joins and will be extended to support further join types in future commits.
This commit is contained in:
parent
0a2a709f9e
commit
83f060bea7
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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> parameters = new ArrayList<Parameters>();
|
||||
|
||||
/**
|
||||
* 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<Pair<String, String>> froms;
|
||||
private final List<Triple<String, String, Boolean>> froms;
|
||||
/**
|
||||
* A list of pairs (property name, order ascending?).
|
||||
* A list of triples (alias, property name, order ascending?).
|
||||
*/
|
||||
private final List<Pair<String, Boolean>> orders;
|
||||
private final List<Triple<String, String, Boolean>> 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<Pair<String, String>>();
|
||||
orders = new ArrayList<Pair<String, Boolean>>();
|
||||
froms = new ArrayList<Triple<String, String, Boolean>>();
|
||||
orders = new ArrayList<Triple<String, String, Boolean>>();
|
||||
projections = new ArrayList<String>();
|
||||
|
||||
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<Pair<String, String>>( other.froms );
|
||||
orders = new ArrayList<Pair<String, Boolean>>( other.orders );
|
||||
froms = new ArrayList<Triple<String, String, Boolean>>( other.froms );
|
||||
orders = new ArrayList<Triple<String, String, Boolean>>( other.orders );
|
||||
projections = new ArrayList<String>( 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<String> getAliasList() {
|
||||
private List<String> getSelectAliasList() {
|
||||
final List<String> aliasList = new ArrayList<String>();
|
||||
for ( Pair<String, String> from : froms ) {
|
||||
aliasList.add( from.getSecond() );
|
||||
for ( Triple<String, String, Boolean> from : froms ) {
|
||||
if ( from.getThird() ) {
|
||||
aliasList.add( from.getSecond() );
|
||||
}
|
||||
}
|
||||
|
||||
return aliasList;
|
||||
|
@ -201,7 +223,7 @@ public class QueryBuilder {
|
|||
|
||||
private List<String> getFromList() {
|
||||
final List<String> fromList = new ArrayList<String>();
|
||||
for ( Pair<String, String> from : froms ) {
|
||||
for ( Triple<String, String, Boolean> from : froms ) {
|
||||
fromList.add( from.getFirst() + " " + from.getSecond() );
|
||||
}
|
||||
|
||||
|
@ -210,8 +232,8 @@ public class QueryBuilder {
|
|||
|
||||
private List<String> getOrderList() {
|
||||
final List<String> orderList = new ArrayList<String>();
|
||||
for ( Pair<String, Boolean> order : orders ) {
|
||||
orderList.add( alias + "." + order.getFirst() + " " + (order.getSecond() ? "asc" : "desc") );
|
||||
for ( Triple<String, String, Boolean> order : orders ) {
|
||||
orderList.add( order.getFirst() + "." + order.getSecond() + " " + (order.getThird() ? "asc" : "desc") );
|
||||
}
|
||||
|
||||
return orderList;
|
||||
|
|
|
@ -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<Q extends AuditQuery> extends AuditQuery {
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> add(AuditCriterion criterion);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> addOrder(AuditOrder order);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> addProjection(AuditProjection projection);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setMaxResults(int maxResults);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setFirstResult(int firstResult);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setCacheable(boolean cacheable);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setCacheRegion(String cacheRegion);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setComment(String comment);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setFlushMode(FlushMode flushMode);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setCacheMode(CacheMode cacheMode);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setTimeout(int timeout);
|
||||
|
||||
@Override
|
||||
AuditAssociationQuery<Q> setLockMode(LockMode lockMode);
|
||||
|
||||
Q up();
|
||||
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<? extends AuditQuery> traverseRelation(String associationName, JoinType joinType);
|
||||
|
||||
AuditQuery add(AuditCriterion criterion);
|
||||
|
||||
AuditQuery addProjection(AuditProjection projection);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ public interface AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters);
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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" ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<AuditCriterion> 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<AuditAssociationQueryImplementor<?>> associationQueries = new ArrayList<AuditAssociationQueryImplementor<?>>();
|
||||
protected final Map<String, AuditAssociationQueryImplementor<AuditQueryImplementor>> associationQueryMap = new HashMap<String, AuditAssociationQueryImplementor<AuditQueryImplementor>>();
|
||||
protected final List<Pair<String, AuditProjection>> projections = new ArrayList<Pair<String,AuditProjection>>();
|
||||
|
||||
protected AbstractAuditQuery(
|
||||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
|
@ -116,17 +124,26 @@ public abstract class AbstractAuditQuery implements AuditQuery {
|
|||
|
||||
public AuditQuery addProjection(AuditProjection projection) {
|
||||
Triple<String, String, Boolean> 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<String, Boolean> 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<? extends AuditQuery> traverseRelation(String associationName, JoinType joinType) {
|
||||
AuditAssociationQueryImplementor<AuditQueryImplementor> result = associationQueryMap.get( associationName );
|
||||
if (result == null) {
|
||||
result = new AuditAssociationQueryImplementor<AuditQueryImplementor>( 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<String, AuditProjection> 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<String, AuditProjection> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Q extends AuditQueryImplementor> implements AuditAssociationQuery<Q>, 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<AuditCriterion> criterions = new ArrayList<AuditCriterion>();
|
||||
private final Parameters parameters;
|
||||
private final List<AuditAssociationQueryImplementor<?>> associationQueries = new ArrayList<AuditAssociationQueryImplementor<?>>();
|
||||
private final Map<String, AuditAssociationQueryImplementor<AuditAssociationQueryImplementor<Q>>> associationQueryMap = new HashMap<String, AuditAssociationQueryImplementor<AuditAssociationQueryImplementor<Q>>>();
|
||||
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<AuditAssociationQueryImplementor<Q>> traverseRelation(String associationName, JoinType joinType) {
|
||||
AuditAssociationQueryImplementor<AuditAssociationQueryImplementor<Q>> result = associationQueryMap.get( associationName );
|
||||
if (result == null) {
|
||||
result = new AuditAssociationQueryImplementor<AuditAssociationQueryImplementor<Q>>(
|
||||
enversService, auditReader, this, queryBuilder, entityName, associationName, joinType, alias );
|
||||
associationQueries.add( result );
|
||||
associationQueryMap.put( associationName, result );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> add(AuditCriterion criterion) {
|
||||
criterions.add( criterion );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> addProjection(AuditProjection projection) {
|
||||
hasProjections = true;
|
||||
Triple<String, String, Boolean> 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<Q> addOrder(AuditOrder order) {
|
||||
hasOrders = true;
|
||||
Pair<String, Boolean> orderData = order.getData( enversService );
|
||||
String propertyName = CriteriaTools.determinePropertyName( enversService, auditReader, entityName, orderData.getFirst() );
|
||||
queryBuilder.addOrder( alias, propertyName, orderData.getSecond() );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> setMaxResults(int maxResults) {
|
||||
parent.setMaxResults( maxResults );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> setFirstResult(int firstResult) {
|
||||
parent.setFirstResult( firstResult );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> setCacheable(boolean cacheable) {
|
||||
parent.setCacheable( cacheable );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> setCacheRegion(String cacheRegion) {
|
||||
parent.setCacheRegion( cacheRegion );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> setComment(String comment) {
|
||||
parent.setComment( comment );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> setFlushMode(FlushMode flushMode) {
|
||||
parent.setFlushMode( flushMode );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> setCacheMode(CacheMode cacheMode) {
|
||||
parent.setCacheMode( cacheMode );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> setTimeout(int timeout) {
|
||||
parent.setTimeout( timeout );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQueryImplementor<Q> 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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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<String> 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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Object> queryResult = buildAndExecuteQuery();
|
||||
if ( hasProjection ) {
|
||||
if ( hasProjection() ) {
|
||||
return queryResult;
|
||||
}
|
||||
else {
|
||||
|
@ -146,4 +158,10 @@ public class RevisionsOfEntityQuery extends AbstractAuditQuery {
|
|||
return entities;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditAssociationQuery<? extends AuditQuery> traverseRelation(String associationName, JoinType joinType) {
|
||||
throw new UnsupportedOperationException( "Not yet implemented for revisions of entity queries" );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<String, String, Boolean> 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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<String, String, Boolean> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Car> 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<Car> 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<Car> 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<Integer> 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<Address> 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<Address> 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<Object[]> 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] );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Person> drivers = new HashSet<Person>();
|
||||
|
||||
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<Person> getDrivers() {
|
||||
return drivers;
|
||||
}
|
||||
|
||||
public void setDrivers(Set<Person> drivers) {
|
||||
this.drivers = drivers;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue