Skip to content

Commit

Permalink
HHH-3555 - Extendend Envers Criteria API to express relation traversion
Browse files Browse the repository at this point in the history
(traverseRelation) with different Join Types. Implementation is done for
inner joins and will be extended to support further join types in future
commits.
  • Loading branch information
Felix Feisst authored and sebersole committed Feb 9, 2016
1 parent 0a2a709 commit 83f060b
Show file tree
Hide file tree
Showing 42 changed files with 1,012 additions and 98 deletions.
Expand Up @@ -94,7 +94,12 @@ public boolean hasAuditedEntities() {
}

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;
Expand Down
Expand Up @@ -70,7 +70,7 @@ public OneAuditEntityQueryGenerator(
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 );
Expand Down
Expand Up @@ -70,7 +70,7 @@ public OneEntityQueryGenerator(
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(
Expand Down
Expand Up @@ -115,11 +115,11 @@ private QueryBuilder commonQueryPart(
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();
Expand Down
Expand Up @@ -82,8 +82,8 @@ private QueryBuilder commonQueryPart(
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
Expand Down
Expand Up @@ -96,8 +96,8 @@ private QueryBuilder commonQueryPart(
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
Expand Down
Expand Up @@ -164,6 +164,17 @@ public void addWhere(String left, boolean addAliasLeft, String op, String right,
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 );
Expand Down
Expand Up @@ -17,7 +17,7 @@
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;

/**
Expand All @@ -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).
*/
Expand All @@ -70,13 +70,14 @@ private QueryBuilder(String entityName, String alias, MutableInteger aliasCounte
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.
Expand All @@ -85,28 +86,38 @@ private QueryBuilder(QueryBuilder other) {
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 );
}

public QueryBuilder deepCopy() {
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();
}

Expand All @@ -130,26 +141,27 @@ public QueryBuilder newSubQueryBuilder(String entityName, String alias) {
}

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 + ")"
);
}
}
Expand All @@ -169,15 +181,23 @@ public void build(StringBuilder sb, Map<String, Object> queryParamValues) {
}
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 ) {
Expand All @@ -186,10 +206,12 @@ public void build(StringBuilder sb, Map<String, Object> queryParamValues) {
}
}

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;
Expand All @@ -201,7 +223,7 @@ public String getRootAlias() {

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() );
}

Expand All @@ -210,8 +232,8 @@ private List<String> getFromList() {

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;
Expand Down
@@ -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();

}
Expand Up @@ -19,6 +19,8 @@
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
Expand Down Expand Up @@ -114,4 +116,13 @@ public static AuditConjunction conjunction() {
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 );
}
}
Expand Up @@ -9,6 +9,7 @@
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;
Expand All @@ -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);
Expand Down

0 comments on commit 83f060b

Please sign in to comment.