HHH-11452 Added possibility to apply the like operator on a function criterion. Added possibility to use the id of an entity as function argument.

This commit is contained in:
Felix Feisst 2017-05-09 15:11:50 -04:00 committed by Chris Cranford
parent 640bd85975
commit e50037ec8c
8 changed files with 183 additions and 21 deletions

View File

@ -317,14 +317,28 @@ public class Parameters {
* Add where clause with a function call on the left and a scalar value on the right.
*
* @param configuration the configuration.
* @param aliasToEntityNameMap alias to entity name map, never {@literal null}
* @param function the function.
* @param op the operator.
* @param value the scalar value.
*/
public void addWhereWithFunction(Configuration configuration, AuditFunction function, String op, Object value) {
public void addWhereWithFunction(
Configuration configuration,
Map<String, String> aliasToEntityNameMap,
AuditFunction function,
String op,
Object value) {
final StringBuilder expression = new StringBuilder();
QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, function );
QueryBuilder.appendFunctionArgument(
configuration,
aliasToEntityNameMap,
queryParamCounter,
localQueryParamValues,
alias,
expression,
function
);
expression.append( ' ' ).append( op );
@ -339,15 +353,30 @@ public class Parameters {
* Add a where clause with a function call on the left and an optionally aliased property on the right.
*
* @param configuration the configuration.
* @param aliasToEntityNameMap alias to entity name map, never {@literal null}
* @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) {
public void addWhereWithFunction(
Configuration configuration,
Map<String, String> aliasToEntityNameMap,
AuditFunction function,
String op,
String aliasRight,
String right) {
final StringBuilder expression = new StringBuilder();
QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, function );
QueryBuilder.appendFunctionArgument(
configuration,
aliasToEntityNameMap,
queryParamCounter,
localQueryParamValues,
alias,
expression,
function
);
expression.append( ' ' ).append( op ).append( ' ' );
@ -363,12 +392,19 @@ public class Parameters {
* Adds a where clause with a left (optionally aliased) property and a function call on the right side.
*
* @param configuration the configuration.
* @param aliasToEntityNameMap alias to entity name map, never {@literal null}
* @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) {
public void addWhereWithFunction(
Configuration configuration,
Map<String, String> aliasToEntityNameMap,
String aliasLeft,
String left,
String op,
AuditFunction function) {
final StringBuilder expression = new StringBuilder();
if ( aliasLeft != null ) {
@ -378,7 +414,15 @@ public class Parameters {
expression.append( ' ' ).append( op ).append( ' ' );
QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, function );
QueryBuilder.appendFunctionArgument(
configuration,
aliasToEntityNameMap,
queryParamCounter,
localQueryParamValues,
alias,
expression,
function
);
expressions.add( expression.toString() );
}
@ -387,18 +431,40 @@ public class Parameters {
* Adds a where clause with a function call on both the left and right of the predicate.
*
* @param configuration the configuration.
* @param aliasToEntityNameMap alias to entity name map, never {@literal null}
* @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) {
public void addWhereWithFunction(
Configuration configuration,
Map<String, String> aliasToEntityNameMap,
AuditFunction left,
String op,
AuditFunction right) {
final StringBuilder expression = new StringBuilder();
QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, left );
QueryBuilder.appendFunctionArgument(
configuration,
aliasToEntityNameMap,
queryParamCounter,
localQueryParamValues,
alias,
expression,
left
);
expression.append( ' ' ).append( op ).append( ' ' );
QueryBuilder.appendFunctionArgument( configuration, queryParamCounter, localQueryParamValues, alias, expression, right );
QueryBuilder.appendFunctionArgument(
configuration,
aliasToEntityNameMap,
queryParamCounter,
localQueryParamValues,
alias,
expression,
right
);
expressions.add( expression.toString() );
}

View File

@ -12,18 +12,23 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import jakarta.persistence.criteria.JoinType;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.boot.internal.EnversService;
import org.hibernate.envers.configuration.Configuration;
import org.hibernate.envers.function.OrderByFragmentFunction;
import org.hibernate.envers.internal.entities.RevisionTypeType;
import org.hibernate.envers.internal.entities.mapper.id.QueryParameterData;
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.AuditId;
import org.hibernate.envers.query.criteria.AuditProperty;
import org.hibernate.envers.tools.Pair;
import org.hibernate.query.Query;
@ -214,14 +219,23 @@ public class QueryBuilder {
}
}
public void addProjection(Configuration configuration, AuditFunction function) {
public void addProjection(Configuration configuration, Map<String, String> aliasToEntityNameMap, AuditFunction function) {
final StringBuilder expression = new StringBuilder();
appendFunctionArgument( configuration, paramCounter, projectionQueryParamValues, alias, expression, function );
appendFunctionArgument(
configuration,
aliasToEntityNameMap,
paramCounter,
projectionQueryParamValues,
alias,
expression,
function
);
projections.add( expression.toString() );
}
protected static void appendFunctionArgument(
Configuration configuration,
Map<String, String> aliasToEntityNameMap,
MutableInteger paramCounter,
Map<String, Object> queryParamValues,
String alias,
@ -235,11 +249,42 @@ public class QueryBuilder {
if ( !first ) {
expression.append( ',' );
}
appendFunctionArgument( configuration, paramCounter, queryParamValues, alias, expression, innerArg );
appendFunctionArgument(
configuration,
aliasToEntityNameMap,
paramCounter,
queryParamValues,
alias,
expression,
innerArg
);
first = false;
}
expression.append( ')' );
}
else if ( argument instanceof AuditId ) {
AuditId<?> id = (AuditId<?>) argument;
String prefix = configuration.getOriginalIdPropertyName();
String idAlias = id.getAlias( alias );
String entityName = aliasToEntityNameMap.get( idAlias );
/*
* Resolve the name of the id property by reusing the IdMapper.mapToQueryParametersFromId() method. Null is
* passed as value because only the name of the property is of interest. TODO: is there a better way to
* obtain the name of the id property?
*/
EnversService enversService = configuration.getEnversService();
List<QueryParameterData> parameters = enversService.getEntitiesConfigurations().get( entityName )
.getIdMapper()
.mapToQueryParametersFromId( null );
if ( parameters.size() != 1 ) {
throw new HibernateException( "Cannot add id property as function argument when id property is not a single column property" );
}
String propertyName = parameters.get( 0 ).getProperty( prefix );
if ( idAlias != null ) {
expression.append( idAlias ).append( '.' );
}
expression.append( propertyName );
}
else if ( argument instanceof AuditProperty ) {
AuditProperty<?> property = (AuditProperty<?>) argument;
String propertyAlias = property.getAlias( alias );

View File

@ -55,6 +55,20 @@ public class AuditFunction implements AuditProjection {
return new SimpleFunctionAuditExpression( this, value, "<>" );
}
/**
* Apply a "like" constraint
*/
public AuditCriterion like(Object value) {
return new SimpleFunctionAuditExpression( this, value, " like " );
}
/**
* Apply a "like" constraint
*/
public AuditCriterion like(String value, MatchMode matchMode) {
return new SimpleFunctionAuditExpression( this, matchMode.toMatchString( value ), " like " );
}
/**
* Apply a "greater than" constraint
*/
@ -222,9 +236,13 @@ public class AuditFunction implements AuditProjection {
}
@Override
public void addProjectionToQuery(EnversService enversService, AuditReaderImplementor auditReader,
Map<String, String> aliasToEntityNameMap, String baseAlias, QueryBuilder queryBuilder) {
queryBuilder.addProjection( enversService.getConfig(), this );
public void addProjectionToQuery(
EnversService enversService,
AuditReaderImplementor auditReader,
Map<String, String> aliasToEntityNameMap,
String baseAlias,
QueryBuilder queryBuilder) {
queryBuilder.addProjection( enversService.getConfig(), aliasToEntityNameMap, this );
}
@Override

View File

@ -43,6 +43,12 @@ public class FunctionFunctionAuditExpression implements AuditCriterion {
String baseAlias,
QueryBuilder queryBuilder,
Parameters parameters) {
parameters.addWhereWithFunction( enversService.getConfig(), leftFunction, op, rightFunction );
parameters.addWhereWithFunction(
enversService.getConfig(),
aliasToEntityNameMap,
leftFunction,
op,
rightFunction
);
}
}

View File

@ -56,6 +56,13 @@ public class FunctionPropertyAuditExpression implements AuditCriterion {
entityName,
propertyNameGetter );
CriteriaTools.checkPropertyNotARelation( enversService, entityName, propertyName );
parameters.addWhereWithFunction( enversService.getConfig(), effectiveAlias, propertyName, op, function );
parameters.addWhereWithFunction(
enversService.getConfig(),
aliasToEntityNameMap,
effectiveAlias,
propertyName,
op,
function
);
}
}

View File

@ -55,6 +55,13 @@ public class PropertyFunctionAuditExpression implements AuditCriterion {
if ( enversService.getEntitiesConfigurations().isVersioned( otherEntityName ) ) {
CriteriaTools.checkPropertyNotARelation( enversService, otherEntityName, otherPropertyName );
}
parameters.addWhereWithFunction( enversService.getConfig(), function, op, effectiveOtherAlias, otherPropertyName );
parameters.addWhereWithFunction(
enversService.getConfig(),
aliasToEntityNameMap,
function,
op,
effectiveOtherAlias,
otherPropertyName
);
}
}

View File

@ -33,9 +33,14 @@ public class SimpleFunctionAuditExpression implements AuditCriterion {
}
@Override
public void addToQuery(EnversService enversService, AuditReaderImplementor versionsReader,
Map<String, String> aliasToEntityNameMap, String baseAlias, QueryBuilder qb, Parameters parameters) {
parameters.addWhereWithFunction( enversService.getConfig(), function, op, value );
public void addToQuery(
EnversService enversService,
AuditReaderImplementor versionsReader,
Map<String, String> aliasToEntityNameMap,
String baseAlias,
QueryBuilder qb,
Parameters parameters) {
parameters.addWhereWithFunction( enversService.getConfig(), aliasToEntityNameMap, function, op, value );
}
}

View File

@ -153,4 +153,12 @@ public class AuditFunctionQueryTest extends BaseEnversJPAFunctionalTestCase {
assertEquals( "Expected the entity to be returned", testEntity1.getId(), entity.getId() );
}
@Test
public void testFunctionOnIdProperty() {
TestEntity entity = (TestEntity) getAuditReader().createQuery().forEntitiesAtRevision( TestEntity.class, 1 )
.add( AuditEntity.function( "str", AuditEntity.id() ).like( "%1%" ) ).getSingleResult();
assertNotNull( "Expected the entity to be returned", entity );
assertEquals( "Expected the entity to be returned", testEntity1.getId(), entity.getId() );
}
}