Skip to content

Commit

Permalink
HHH-11452 Extended audit query API to specify the use of scalar
Browse files Browse the repository at this point in the history
functions.
  • Loading branch information
Felix Feisst authored and Naros committed Dec 16, 2021
1 parent bb09222 commit 640bd85
Show file tree
Hide file tree
Showing 15 changed files with 925 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
import java.util.List;
import java.util.Map;

import org.hibernate.envers.configuration.Configuration;
import org.hibernate.envers.internal.tools.MutableBoolean;
import org.hibernate.envers.internal.tools.MutableInteger;
import org.hibernate.envers.query.criteria.AuditFunction;

/**
* Parameters of a query, built using {@link QueryBuilder}.
Expand Down Expand Up @@ -311,6 +313,96 @@ public void addWhereOrNullRestriction(String left, boolean addAliasLeft, String
}
}

/**
* Add where clause with a function call on the left and a scalar value on the right.
*
* @param configuration the configuration.
* @param function the function.
* @param op the operator.
* @param value the scalar value.
*/
public void addWhereWithFunction(Configuration configuration, AuditFunction function, String op, Object value) {
final StringBuilder expression = new StringBuilder();

QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, function );

expression.append( ' ' ).append( op );

String queryParam = generateQueryParam();
localQueryParamValues.put( queryParam, value );
expression.append( ' ' ).append( ':' ).append( queryParam );

expressions.add( expression.toString() );
}

/**
* Add a where clause with a function call on the left and an optionally aliased property on the right.
*
* @param configuration the configuration.
* @param function the function.
* @param op the operator.
* @param aliasRight the optional alias of the right property, may be {@literal null}
* @param right the property.
*/
public void addWhereWithFunction(Configuration configuration, AuditFunction function, String op, String aliasRight, String right) {
final StringBuilder expression = new StringBuilder();

QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, function );

expression.append( ' ' ).append( op ).append( ' ' );

if ( aliasRight != null ) {
expression.append( aliasRight ).append( '.' );
}
expression.append( right );

expressions.add( expression.toString() );
}

/**
* Adds a where clause with a left (optionally aliased) property and a function call on the right side.
*
* @param configuration the configuration.
* @param aliasLeft the optional alias of the left property, may be {@literal null}
* @param left the property.
* @param op the operator.
* @param function the function.
*/
public void addWhereWithFunction(Configuration configuration, String aliasLeft, String left, String op, AuditFunction function) {
final StringBuilder expression = new StringBuilder();

if ( aliasLeft != null ) {
expression.append( aliasLeft ).append( '.' );
}
expression.append( left );

expression.append( ' ' ).append( op ).append( ' ' );

QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, function );

expressions.add( expression.toString() );
}

/**
* Adds a where clause with a function call on both the left and right of the predicate.
*
* @param configuration the configuration.
* @param left the left-side function.
* @param op the operator.
* @param right the right-side function.
*/
public void addWhereWithFunction(Configuration configuration, AuditFunction left, String op, AuditFunction right) {
final StringBuilder expression = new StringBuilder();

QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, left );

expression.append( ' ' ).append( op ).append( ' ' );

QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, right );

expressions.add( expression.toString() );
}

private void append(StringBuilder sb, String toAppend, MutableBoolean isFirst) {
if ( !isFirst.isSet() ) {
sb.append( " " ).append( connective ).append( " " );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.Configuration;
import org.hibernate.envers.function.OrderByFragmentFunction;
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.internal.tools.Triple;
import org.hibernate.envers.query.criteria.AuditFunction;
import org.hibernate.envers.query.criteria.AuditProperty;
import org.hibernate.envers.tools.Pair;
import org.hibernate.query.Query;
import org.hibernate.query.internal.QueryLiteralHelper;
Expand Down Expand Up @@ -62,6 +65,10 @@ public class QueryBuilder {
* A list of complete projection definitions: either a sole property name, or a function(property name).
*/
private final List<String> projections;
/**
* Values of parameters used in projections.
*/
private final Map<String, Object> projectionQueryParamValues;

private final List<Pair<String, String>> orderFragments;

Expand Down Expand Up @@ -100,6 +107,7 @@ private QueryBuilder(
froms = new ArrayList<>();
orders = new ArrayList<>();
projections = new ArrayList<>();
projectionQueryParamValues = new HashMap<>();
orderFragments = new ArrayList<>();

addFrom( entityName, alias, true );
Expand All @@ -120,6 +128,7 @@ private QueryBuilder(QueryBuilder other) {
froms = new ArrayList<>( other.froms );
orders = new ArrayList<>( other.orders );
projections = new ArrayList<>( other.projections );
projectionQueryParamValues = new HashMap<>( other.projectionQueryParamValues );
orderFragments = new ArrayList<>( other.orderFragments );
}

Expand Down Expand Up @@ -205,6 +214,48 @@ public void addProjection(String function, String alias, String propertyName, bo
}
}

public void addProjection(Configuration configuration, AuditFunction function) {
final StringBuilder expression = new StringBuilder();
appendFunctionArgument( configuration, paramCounter, projectionQueryParamValues, alias, expression, function );
projections.add( expression.toString() );
}

protected static void appendFunctionArgument(
Configuration configuration,
MutableInteger paramCounter,
Map<String, Object> queryParamValues,
String alias,
StringBuilder expression,
Object argument) {
if ( argument instanceof AuditFunction ) {
AuditFunction function = (AuditFunction) argument;
expression.append( function.getFunction() ).append( '(' );
boolean first = true;
for ( final Object innerArg : function.getArguments() ) {
if ( !first ) {
expression.append( ',' );
}
appendFunctionArgument( configuration, paramCounter, queryParamValues, alias, expression, innerArg );
first = false;
}
expression.append( ')' );
}
else if ( argument instanceof AuditProperty ) {
AuditProperty<?> property = (AuditProperty<?>) argument;
String propertyAlias = property.getAlias( alias );
if ( propertyAlias != null ) {
expression.append( propertyAlias ).append( '.' );
}
String propertyName = property.getPropertyNameGetter().get( configuration );
expression.append( propertyName );
}
else {
String queryParam = "_p" + paramCounter.getAndIncrease();
queryParamValues.put( queryParam, argument );
expression.append( ':' ).append( queryParam );
}
}

/**
* Builds the given query, appending results to the given string buffer, and adding all query parameter values
* that are used to the map provided.
Expand All @@ -222,6 +273,7 @@ public void build(StringBuilder sb, Map<String, Object> queryParamValues) {
// all aliases separated with commas
StringTools.append( sb, getSelectAliasList().iterator(), ", " );
}
queryParamValues.putAll( projectionQueryParamValues );
sb.append( " from " );
// all from entities with aliases
boolean first = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
*/
package org.hibernate.envers.query;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.hibernate.envers.RevisionType;
import org.hibernate.envers.query.criteria.AuditConjunction;
import org.hibernate.envers.query.criteria.AuditCriterion;
import org.hibernate.envers.query.criteria.AuditDisjunction;
import org.hibernate.envers.query.criteria.AuditFunction;
import org.hibernate.envers.query.criteria.AuditId;
import org.hibernate.envers.query.criteria.AuditProperty;
import org.hibernate.envers.query.criteria.AuditRelatedId;
Expand Down Expand Up @@ -176,4 +181,30 @@ public static AuditDisjunction disjunction() {
public static AuditProjection selectEntity(boolean distinct) {
return new EntityAuditProjection( null, distinct );
}

/**
* Create restrictions or projections using a function.
* <p>
* Examples:
* <ul>
* <li>AuditEntity.function("upper", AuditEntity.property("prop"))</li>
* <li>AuditEntity.function("substring", AuditEntity.property("prop"), 3, 2)</li>
* <li>AuditEntity.function("concat", AuditEntity.function("upper", AuditEntity.property("prop1")),
* AuditEntity.function("substring", AuditEntity.property("prop2"), 1, 2))</li>
* </ul>
*
* @param function the name of the function
* @param arguments the arguments of the function. A function argument can either be
* <ul>
* <li>a primitive value like a Number or a String</li>
* <li>an AuditProperty (see {@link #property(String)})</li>
* <li>an other AuditFunction</li>
* </ul>
* @return
*/
public static AuditFunction function(final String function, final Object... arguments) {
List<Object> argumentList = new ArrayList<>();
Collections.addAll( argumentList, arguments );
return new AuditFunction( function, argumentList );
}
}

0 comments on commit 640bd85

Please sign in to comment.