HHH-17375 Introduce array_includes() and INCLUDES predicate for checking if array contains all elements of subarray as replacement to array_contains() overload

This commit is contained in:
Christian Beikov 2024-05-13 08:34:39 +02:00
parent dcedc5cf18
commit c8aa4f39da
26 changed files with 897 additions and 172 deletions

View File

@ -1189,8 +1189,10 @@ The following functions deal with SQL array types, which are not supported on ev
| `array_concat()` | Concatenates array with each other in order
| `array_prepend()` | Prepends element to array
| `array_append()` | Appends element to array
| `array_contains()` | Whether an array contains element(s)
| `array_contains_nullable()` | Whether an array contains element(s), supporting `null` elements
| `array_contains()` | Whether an array contains an element
| `array_contains_nullable()` | Whether an array contains an element, supporting `null` element
| `array_includes()` | Whether an array contains another array
| `array_includes_nullable()` | Whether an array contains another array, supporting `null` elements
| `array_intersects()` | Whether an array holds at least one element of another array
| `array_intersects_nullable()` | Whether an array holds at least one element of another array, supporting `null` elements
| `array_get()` | Accesses the element of an array by index
@ -1367,7 +1369,7 @@ include::{array-example-dir-hql}/ArrayAppendTest.java[tags=hql-array-append-exam
[[hql-array-contains-functions]]
===== `array_contains()` and `array_contains_nullable()`
Checks if the first array argument contains the element(s) of the second argument.
Checks if the first array argument contains the element represented by the second argument.
Returns `null` if the first argument is `null`. The result of the `array_contains` function
is undefined when the second argument, the element to search, is `null`.
@ -1379,29 +1381,8 @@ include::{array-example-dir-hql}/ArrayContainsTest.java[tags=hql-array-contains-
----
====
The second argument may be an array of the same type as the first argument.
The result of `array_contains` is undefined when the second argument is an array that contains a `null` element.
[[hql-array-contains-array-example]]
====
[source, JAVA, indent=0]
----
include::{array-example-dir-hql}/ArrayContainsArrayTest.java[tags=hql-array-contains-array-example]
----
====
To search for `null` elements, the `array_contains_nullable` function must be used with an array argument.
[[hql-array-contains-array-nullable-example]]
====
[source, JAVA, indent=0]
----
include::{array-example-dir-hql}/ArrayContainsArrayTest.java[tags=hql-array-contains-array-nullable-example]
----
====
Alternatively, it's also possible to check for containment with the `contains` predicate,
where the left hand side of the predicate is the array and the right hand side the value(s) to check.
where the left hand side of the predicate is the array and the right hand side the value to check.
This is syntax sugar that translates to the `array_contains` function.
[[hql-array-contains-hql-example]]
@ -1412,29 +1393,42 @@ include::{array-example-dir-hql}/ArrayContainsTest.java[tags=hql-array-contains-
----
====
[[hql-array-contains-array-hql-example]]
[[hql-array-includes-functions]]
===== `array_includes()` and `array_includes_nullable()`
Checks if the first array argument contains the elements of the second array argument.
Returns `null` if the first argument is `null`. The result of the `array_includes` function
is undefined when the second argument contains a `null`.
[[hql-array-contains-array-example]]
[[hql-array-includes-example]]
====
[source, JAVA, indent=0]
----
include::{array-example-dir-hql}/ArrayContainsArrayTest.java[tags=hql-array-contains-array-hql-example]
include::{array-example-dir-hql}/ArrayIncludesTest.java[tags=hql-array-includes-example]
----
====
In addition to that, it is also possible to use the `in` predicate with arrays.
To search for `null` elements, the `array_includes_nullable` function must be used.
[[hql-array-in-hql-example]]
[[hql-array-contains-array-nullable-example]]
[[hql-array-includes-nullable-example]]
====
[source, JAVA, indent=0]
----
include::{array-example-dir-hql}/ArrayContainsTest.java[tags=hql-array-in-hql-example]
include::{array-example-dir-hql}/ArrayIncludesTest.java[tags=hql-array-includes-nullable-example]
----
====
[[hql-array-in-array-hql-example]]
Alternatively, it's also possible to use the `includes` predicate,
where the left hand side of the predicate is the array and the right hand side the array of values to check.
This is syntax sugar that translates to the `array_includes` function.
[[hql-array-includes-hql-example]]
====
[source, JAVA, indent=0]
----
include::{array-example-dir-hql}/ArrayContainsArrayTest.java[tags=hql-array-in-array-hql-example]
include::{array-example-dir-hql}/ArrayIncludesTest.java[tags=hql-array-includes-hql-example]
----
====

View File

@ -210,6 +210,7 @@ HOUR : [hH] [oO] [uU] [rR];
IGNORE : [iI] [gG] [nN] [oO] [rR] [eE];
ILIKE : [iI] [lL] [iI] [kK] [eE];
IN : [iI] [nN];
INCLUDES : [iI] [nN] [cC] [lL] [uU] [dD] [eE] [sS];
INDEX : [iI] [nN] [dD] [eE] [xX];
INDICES : [iI] [nN] [dD] [iI] [cC] [eE] [sS];
INNER : [iI] [nN] [nN] [eE] [rR];

View File

@ -674,6 +674,7 @@ predicate
| expression NOT? BETWEEN expression AND expression # BetweenPredicate
| expression NOT? (LIKE | ILIKE) expression likeEscape? # LikePredicate
| expression NOT? CONTAINS expression # ContainsPredicate
| expression NOT? INCLUDES expression # IncludesPredicate
| expression NOT? INTERSECTS expression # IntersectsPredicate
| expression comparisonOperator expression # ComparisonPredicate
| EXISTS collectionQuantifier LEFT_PAREN simplePath RIGHT_PAREN # ExistsCollectionPartPredicate
@ -1703,6 +1704,7 @@ rollup
| ILIKE
| IN
| INDEX
| INCLUDES
| INDICES
// | INNER
| INSERT

View File

@ -109,7 +109,7 @@ public class OracleUserDefinedTypeExporter extends StandardUserDefinedTypeExport
"return arr.count; " +
"end;",
createOrReplaceConcatFunction( arrayTypeName ),
"create or replace function " + arrayTypeName + "_contains(haystack in " + arrayTypeName +
"create or replace function " + arrayTypeName + "_includes(haystack in " + arrayTypeName +
", needle in " + arrayTypeName + ", nullable in number) return number deterministic is found number(1,0); begin " +
"if haystack is null or needle is null then return null; end if; " +
"for i in 1 .. needle.count loop " +
@ -295,7 +295,7 @@ public class OracleUserDefinedTypeExporter extends StandardUserDefinedTypeExport
"drop function " + arrayTypeName + "_position",
"drop function " + arrayTypeName + "_length",
"drop function " + arrayTypeName + "_concat",
"drop function " + arrayTypeName + "_contains",
"drop function " + arrayTypeName + "_includes",
"drop function " + arrayTypeName + "_intersects",
"drop function " + arrayTypeName + "_get",
"drop function " + arrayTypeName + "_set",

View File

@ -21,6 +21,8 @@ import org.hibernate.dialect.function.array.ArrayConcatFunction;
import org.hibernate.dialect.function.array.ArrayConstructorFunction;
import org.hibernate.dialect.function.array.ArrayContainsOperatorFunction;
import org.hibernate.dialect.function.array.ArrayContainsUnnestFunction;
import org.hibernate.dialect.function.array.ArrayIncludesOperatorFunction;
import org.hibernate.dialect.function.array.ArrayIncludesUnnestFunction;
import org.hibernate.dialect.function.array.ArrayIntersectsOperatorFunction;
import org.hibernate.dialect.function.array.ArrayIntersectsUnnestFunction;
import org.hibernate.dialect.function.array.ArrayGetUnnestFunction;
@ -34,6 +36,7 @@ import org.hibernate.dialect.function.array.CockroachArrayFillFunction;
import org.hibernate.dialect.function.array.ElementViaArrayArgumentReturnTypeResolver;
import org.hibernate.dialect.function.array.H2ArrayContainsFunction;
import org.hibernate.dialect.function.array.H2ArrayFillFunction;
import org.hibernate.dialect.function.array.H2ArrayIncludesFunction;
import org.hibernate.dialect.function.array.H2ArrayIntersectsFunction;
import org.hibernate.dialect.function.array.H2ArrayPositionFunction;
import org.hibernate.dialect.function.array.H2ArrayPositionsFunction;
@ -52,6 +55,7 @@ import org.hibernate.dialect.function.array.HSQLArrayToStringFunction;
import org.hibernate.dialect.function.array.OracleArrayConcatElementFunction;
import org.hibernate.dialect.function.array.OracleArrayConcatFunction;
import org.hibernate.dialect.function.array.OracleArrayFillFunction;
import org.hibernate.dialect.function.array.OracleArrayIncludesFunction;
import org.hibernate.dialect.function.array.OracleArrayIntersectsFunction;
import org.hibernate.dialect.function.array.OracleArrayGetFunction;
import org.hibernate.dialect.function.array.OracleArrayLengthFunction;
@ -2681,6 +2685,14 @@ public class CommonFunctionFactory {
"array_contains_nullable",
new H2ArrayContainsFunction( true, maximumArraySize, typeConfiguration )
);
functionRegistry.register(
"array_includes",
new H2ArrayIncludesFunction( false, maximumArraySize, typeConfiguration )
);
functionRegistry.register(
"array_includes_nullable",
new H2ArrayIncludesFunction( true, maximumArraySize, typeConfiguration )
);
}
/**
@ -2695,6 +2707,14 @@ public class CommonFunctionFactory {
"array_contains_nullable",
new ArrayContainsUnnestFunction( true, typeConfiguration )
);
functionRegistry.register(
"array_includes",
new ArrayIncludesUnnestFunction( false, typeConfiguration )
);
functionRegistry.register(
"array_includes_nullable",
new ArrayIncludesUnnestFunction( true, typeConfiguration )
);
}
/**
@ -2703,6 +2723,8 @@ public class CommonFunctionFactory {
public void arrayContains_postgresql() {
functionRegistry.register( "array_contains", new ArrayContainsOperatorFunction( false, typeConfiguration ) );
functionRegistry.register( "array_contains_nullable", new ArrayContainsOperatorFunction( true, typeConfiguration ) );
functionRegistry.register( "array_includes", new ArrayIncludesOperatorFunction( false, typeConfiguration ) );
functionRegistry.register( "array_includes_nullable", new ArrayIncludesOperatorFunction( true, typeConfiguration ) );
}
/**
@ -2711,6 +2733,8 @@ public class CommonFunctionFactory {
public void arrayContains_oracle() {
functionRegistry.register( "array_contains", new OracleArrayContainsFunction( false, typeConfiguration ) );
functionRegistry.register( "array_contains_nullable", new OracleArrayContainsFunction( true, typeConfiguration ) );
functionRegistry.register( "array_includes", new OracleArrayIncludesFunction( false, typeConfiguration ) );
functionRegistry.register( "array_includes_nullable", new OracleArrayIncludesFunction( true, typeConfiguration ) );
}
/**

View File

@ -6,17 +6,22 @@
*/
package org.hibernate.dialect.function.array;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
/**
* Encapsulates the validator, return type and argument type resolvers for the array_contains function.
* Subclasses only have to implement the rendering.
*/
public abstract class AbstractArrayContainsFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
protected static final DeprecationLogger LOG = Logger.getMessageLogger( DeprecationLogger.class, AbstractArrayContainsFunction.class.getName() );
protected final boolean nullable;
public AbstractArrayContainsFunction(boolean nullable, TypeConfiguration typeConfiguration) {

View File

@ -0,0 +1,39 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function.array;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Encapsulates the validator, return type and argument type resolvers for the array_includes function.
* Subclasses only have to implement the rendering.
*/
public abstract class AbstractArrayIncludesFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
protected final boolean nullable;
public AbstractArrayIncludesFunction(boolean nullable, TypeConfiguration typeConfiguration) {
super(
"array_includes" + ( nullable ? "_nullable" : "" ),
StandardArgumentsValidators.composite(
StandardArgumentsValidators.exactly( 2 ),
ArrayIncludesArgumentValidator.INSTANCE
),
StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.standardBasicTypeForJavaType( Boolean.class ) ),
ArrayIncludesArgumentTypeResolver.INSTANCE
);
this.nullable = nullable;
}
@Override
public String getArgumentListSignature() {
return "(ARRAY haystackArray, OBJECT needleArray)";
}
}

View File

@ -39,6 +39,7 @@ public class ArrayContainsOperatorFunction extends ArrayContainsUnnestFunction {
final JdbcMappingContainer needleTypeContainer = needleExpression.getExpressionType();
final JdbcMapping needleType = needleTypeContainer == null ? null : needleTypeContainer.getSingleJdbcMapping();
if ( needleType == null || needleType instanceof BasicPluralType<?, ?> ) {
LOG.deprecatedArrayContainsWithArray();
if ( nullable ) {
super.render( sqlAppender, sqlAstArguments, returnType, walker );
}

View File

@ -39,6 +39,7 @@ public class ArrayContainsUnnestFunction extends AbstractArrayContainsFunction {
final JdbcMappingContainer needleTypeContainer = needleExpression.getExpressionType();
final JdbcMapping needleType = needleTypeContainer == null ? null : needleTypeContainer.getSingleJdbcMapping();
if ( needleType == null || needleType instanceof BasicPluralType<?, ?> ) {
LOG.deprecatedArrayContainsWithArray();
sqlAppender.append( '(' );
if ( ArrayHelper.isNullable( haystackExpression ) ) {
walker.render( haystackExpression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.dialect.function.array;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.sqm.produce.function.FunctionArgumentTypeResolver;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.type.BasicPluralType;
/**
* A {@link FunctionArgumentTypeResolver} that resolves the argument types for the {@code array_includes} function.
*/
public class ArrayIncludesArgumentTypeResolver implements FunctionArgumentTypeResolver {
public static final FunctionArgumentTypeResolver INSTANCE = new ArrayIncludesArgumentTypeResolver();
@Override
public MappingModelExpressible<?> resolveFunctionArgumentType(
SqmFunction<?> function,
int argumentIndex,
SqmToSqlAstConverter converter) {
if ( argumentIndex == 0 ) {
final SqmTypedNode<?> node = function.getArguments().get( 1 );
if ( node instanceof SqmExpression<?> ) {
return converter.determineValueMapping( (SqmExpression<?>) node );
}
}
else if ( argumentIndex == 1 ) {
final SqmTypedNode<?> node = function.getArguments().get( 0 );
if ( node instanceof SqmExpression<?> ) {
return converter.determineValueMapping( (SqmExpression<?>) node );
}
}
return null;
}
}

View File

@ -0,0 +1,50 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.query.sqm.produce.function.ArgumentsValidator;
import org.hibernate.query.sqm.produce.function.FunctionArgumentException;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.spi.TypeConfiguration;
/**
* A {@link ArgumentsValidator} that validates the arguments for the {@code array_includes} function.
*/
public class ArrayIncludesArgumentValidator extends ArrayArgumentValidator {
public static final ArgumentsValidator INSTANCE = new ArrayIncludesArgumentValidator();
protected ArrayIncludesArgumentValidator() {
super( 0 );
}
@Override
public void validate(
List<? extends SqmTypedNode<?>> arguments,
String functionName,
TypeConfiguration typeConfiguration) {
final BasicPluralType<?, ?> haystackType =
getPluralType( 0, arguments, functionName, typeConfiguration );
final BasicPluralType<?, ?> needleType =
getPluralType( 1, arguments, functionName, typeConfiguration );
if ( haystackType != null && needleType != null
&& !haystackType.equals( needleType )
&& !haystackType.getElementType().equals( needleType ) ) {
throw new FunctionArgumentException(
String.format(
"Parameter 1 of function '%s()' has type %s, but argument is of type '%s'",
functionName,
haystackType.getJavaTypeDescriptor().getTypeName(),
needleType.getTypeName()
)
);
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Special array includes implementation that uses the PostgreSQL {@code @>} operator.
*/
public class ArrayIncludesOperatorFunction extends ArrayIncludesUnnestFunction {
public ArrayIncludesOperatorFunction(boolean nullable, TypeConfiguration typeConfiguration) {
super( nullable, typeConfiguration );
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression haystackExpression = (Expression) sqlAstArguments.get( 0 );
final Expression needleExpression = (Expression) sqlAstArguments.get( 1 );
if ( nullable ) {
super.render( sqlAppender, sqlAstArguments, returnType, walker );
}
else {
haystackExpression.accept( walker );
sqlAppender.append( "@>" );
needleExpression.accept( walker );
}
}
}

View File

@ -0,0 +1,60 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Implement the array includes function by using {@code unnest}.
*/
public class ArrayIncludesUnnestFunction extends AbstractArrayIncludesFunction {
public ArrayIncludesUnnestFunction(boolean nullable, TypeConfiguration typeConfiguration) {
super( nullable, typeConfiguration );
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression haystackExpression = (Expression) sqlAstArguments.get( 0 );
final Expression needleExpression = (Expression) sqlAstArguments.get( 1 );
sqlAppender.append( '(' );
if ( ArrayHelper.isNullable( haystackExpression ) ) {
walker.render( haystackExpression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
sqlAppender.append( " is not null and " );
}
if ( ArrayHelper.isNullable( needleExpression ) ) {
walker.render( needleExpression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
sqlAppender.append( " is not null and " );
}
if ( !nullable ) {
sqlAppender.append( "not exists(select 1 from unnest(" );
walker.render( needleExpression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
sqlAppender.append( ") t(i) where t.i is null) and " );
}
sqlAppender.append( "not exists(select * from unnest(" );
walker.render( needleExpression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
sqlAppender.append( ") except select * from unnest(" );
walker.render( haystackExpression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
sqlAppender.append( ")))" );
}
}

View File

@ -44,6 +44,7 @@ public class H2ArrayContainsFunction extends AbstractArrayContainsFunction {
final JdbcMappingContainer needleTypeContainer = needleExpression.getExpressionType();
final JdbcMapping needleType = needleTypeContainer == null ? null : needleTypeContainer.getSingleJdbcMapping();
if ( needleType == null || needleType instanceof BasicPluralType<?, ?> ) {
LOG.deprecatedArrayContainsWithArray();
sqlAppender.append( '(' );
if ( ArrayHelper.isNullable( haystackExpression ) ) {
haystackExpression.accept( walker );

View File

@ -0,0 +1,76 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.spi.TypeConfiguration;
/**
* H2 requires a very special emulation, because {@code unnest} is pretty much useless,
* due to https://github.com/h2database/h2database/issues/1815.
* This emulation uses {@code array_get}, {@code array_length} and {@code system_range} functions to roughly achieve the same.
*/
public class H2ArrayIncludesFunction extends AbstractArrayIncludesFunction {
private final int maximumArraySize;
public H2ArrayIncludesFunction(boolean nullable, int maximumArraySize, TypeConfiguration typeConfiguration) {
super( nullable, typeConfiguration );
this.maximumArraySize = maximumArraySize;
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression haystackExpression = (Expression) sqlAstArguments.get( 0 );
final Expression needleExpression = (Expression) sqlAstArguments.get( 1 );
sqlAppender.append( '(' );
if ( ArrayHelper.isNullable( haystackExpression ) ) {
haystackExpression.accept( walker );
sqlAppender.append( " is not null and " );
}
if ( ArrayHelper.isNullable( needleExpression ) ) {
needleExpression.accept( walker );
sqlAppender.append( " is not null and " );
}
if ( !nullable ) {
sqlAppender.append( "not array_contains(" );
needleExpression.accept( walker );
sqlAppender.append( ",null) and " );
}
sqlAppender.append( "not " );
sqlAppender.append( "exists(select array_get(" );
walker.render( needleExpression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
sqlAppender.append( ",t.i) from system_range(1," );
sqlAppender.append( Integer.toString( maximumArraySize ) );
sqlAppender.append( ") t(i) where array_length(" );
needleExpression.accept( walker );
sqlAppender.append( ")>=t.i" );
sqlAppender.append( " except " );
sqlAppender.append( "select array_get(" );
haystackExpression.accept( walker );
sqlAppender.append( ",t.i) from system_range(1," );
sqlAppender.append( Integer.toString( maximumArraySize ) );
sqlAppender.append( ") t(i) where array_length(" );
haystackExpression.accept( walker );
sqlAppender.append( ")>=t.i))" );
}
}

View File

@ -40,7 +40,8 @@ public class OracleArrayContainsFunction extends AbstractArrayContainsFunction {
);
sqlAppender.appendSql( arrayTypeName );
if ( needleType == null || needleType instanceof BasicPluralType<?, ?> ) {
sqlAppender.append( "_contains(" );
LOG.deprecatedArrayContainsWithArray();
sqlAppender.append( "_includes(" );
haystackExpression.accept( walker );
sqlAppender.append( ',' );
sqlAstArguments.get( 1 ).accept( walker );

View File

@ -0,0 +1,47 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.spi.TypeConfiguration;
public class OracleArrayIncludesFunction extends AbstractArrayIncludesFunction {
public OracleArrayIncludesFunction(boolean nullable, TypeConfiguration typeConfiguration) {
super( nullable, typeConfiguration );
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression haystackExpression = (Expression) sqlAstArguments.get( 0 );
final String arrayTypeName = DdlTypeHelper.getTypeName(
haystackExpression.getExpressionType(),
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.appendSql( arrayTypeName );
sqlAppender.append( "_includes(" );
haystackExpression.accept( walker );
sqlAppender.append( ',' );
sqlAstArguments.get( 1 ).accept( walker );
sqlAppender.append( ',' );
sqlAppender.append( nullable ? "1" : "0" );
sqlAppender.append( ")>0" );
}
}

View File

@ -290,4 +290,11 @@ public interface DeprecationLogger extends BasicLogger {
)
void deprecatedNativeQueryColonEscaping(String oldOperator, String newOperator);
@LogMessage(level = WARN)
@Message(
id = 90000032,
value = "The support for passing arrays to array_contains() is deprecated and will be removed. Use array_includes() instead."
)
void deprecatedArrayContainsWithArray();
}

View File

@ -2745,52 +2745,124 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
<T> JpaPredicate arrayContainsNullable(T[] array, Expression<T> elementExpression);
/**
* Whether an array contains another array.
* Whether an array is a subset of another array.
*
* @since 6.4
* @deprecated Replaced with {@link #arrayIncludes(Expression, Expression)}
*/
@Incubating
<T> JpaPredicate arrayContainsAll(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression);
@Deprecated(forRemoval = true)
default <T> JpaPredicate arrayContainsAll(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression) {
return arrayIncludes( arrayExpression, subArrayExpression );
}
/**
* Whether an array contains another array.
* Whether an array is a subset of another array.
*
* @since 6.4
* @deprecated Replaced with {@link #arrayIncludes(Expression, T[])}
*/
@Incubating
<T> JpaPredicate arrayContainsAll(Expression<T[]> arrayExpression, T[] subArray);
@Deprecated(forRemoval = true)
default <T> JpaPredicate arrayContainsAll(Expression<T[]> arrayExpression, T[] subArray) {
return arrayIncludes( arrayExpression, subArray );
}
/**
* Whether an array contains another array.
* Whether an array is a subset of another array.
*
* @since 6.4
* @deprecated Replaced with {@link #arrayIncludes(T[], Expression)}
*/
@Incubating
<T> JpaPredicate arrayContainsAll(T[] array, Expression<T[]> subArrayExpression);
@Deprecated(forRemoval = true)
default <T> JpaPredicate arrayContainsAll(T[] array, Expression<T[]> subArrayExpression) {
return arrayIncludes( array, subArrayExpression );
}
/**
* Whether an array contains another array with nullable elements.
* Whether an array is a subset of another array with nullable elements.
*
* @since 6.4
* @deprecated Replaced with {@link #arrayIncludesNullable(Expression, Expression)}
*/
@Incubating
<T> JpaPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression);
@Deprecated(forRemoval = true)
default <T> JpaPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression) {
return arrayIncludesNullable( arrayExpression, subArrayExpression );
}
/**
* Whether an array contains another array with nullable elements.
* Whether an array is a subset of another array with nullable elements.
*
* @since 6.4
* @deprecated Replaced with {@link #arrayIncludesNullable(Expression, T[])}
*/
@Incubating
<T> JpaPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, T[] subArray);
@Deprecated(forRemoval = true)
default <T> JpaPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, T[] subArray) {
return arrayIncludesNullable( arrayExpression, subArray );
}
/**
* Whether an array contains another array with nullable elements.
* Whether an array is a subset of another array with nullable elements.
*
* @since 6.4
* @deprecated Replaced with {@link #arrayIncludesNullable(T[], Expression)}
*/
@Incubating
<T> JpaPredicate arrayContainsAllNullable(T[] array, Expression<T[]> subArrayExpression);
@Deprecated(forRemoval = true)
default <T> JpaPredicate arrayContainsAllNullable(T[] array, Expression<T[]> subArrayExpression) {
return arrayIncludesNullable( array, subArrayExpression );
}
/**
* Whether an array is a subset of another array.
*
* @since 6.6
*/
@Incubating
<T> JpaPredicate arrayIncludes(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression);
/**
* Whether an array is a subset of another array.
*
* @since 6.6
*/
@Incubating
<T> JpaPredicate arrayIncludes(Expression<T[]> arrayExpression, T[] subArray);
/**
* Whether an array is a subset of another array.
*
* @since 6.6
*/
@Incubating
<T> JpaPredicate arrayIncludes(T[] array, Expression<T[]> subArrayExpression);
/**
* Whether an array is a subset of another array with nullable elements.
*
* @since 6.6
*/
@Incubating
<T> JpaPredicate arrayIncludesNullable(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression);
/**
* Whether an array is a subset of another array with nullable elements.
*
* @since 6.6
*/
@Incubating
<T> JpaPredicate arrayIncludesNullable(Expression<T[]> arrayExpression, T[] subArray);
/**
* Whether an array is a subset of another array with nullable elements.
*
* @since 6.6
*/
@Incubating
<T> JpaPredicate arrayIncludesNullable(T[] array, Expression<T[]> subArrayExpression);
/**
* Whether one array has any elements common with another array.
@ -3296,52 +3368,124 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
<E> JpaPredicate collectionContainsNullable(Collection<E> collection, Expression<E> elementExpression);
/**
* Whether a basic collection contains another basic collection.
* Whether a basic collection is a subset of another basic collection.
*
* @since 6.4
* @deprecated Replaced with {@link #collectionIncludes(Expression, Expression)}
*/
@Incubating
<E> JpaPredicate collectionContainsAll(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression);
@Deprecated(forRemoval = true)
default <E> JpaPredicate collectionContainsAll(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression) {
return collectionIncludes( collectionExpression, subCollectionExpression );
}
/**
* Whether a basic collection contains another basic collection.
* Whether a basic collection is a subset of another basic collection.
*
* @since 6.4
* @deprecated Replaced with {@link #collectionIncludes(Expression, Collection)}
*/
@Incubating
<E> JpaPredicate collectionContainsAll(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection);
@Deprecated(forRemoval = true)
default <E> JpaPredicate collectionContainsAll(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection) {
return collectionIncludes( collectionExpression, subCollection );
}
/**
* Whether a basic collection contains another basic collection.
* Whether a basic collection is a subset of another basic collection.
*
* @since 6.4
* @deprecated Replaced with {@link #collectionIncludes(Collection, Expression)}
*/
@Incubating
<E> JpaPredicate collectionContainsAll(Collection<E> collection, Expression<? extends Collection<? extends E>> subArrayExpression);
@Deprecated(forRemoval = true)
default <E> JpaPredicate collectionContainsAll(Collection<E> collection, Expression<? extends Collection<? extends E>> subCollectionExpression) {
return collectionIncludes( collection, subCollectionExpression );
}
/**
* Whether a basic collection contains another basic collection with nullable elements.
* Whether a basic collection is a subset of another basic collection with nullable elements.
*
* @since 6.4
* @deprecated Replaced with {@link #collectionIncludesNullable(Expression, Expression)}
*/
@Incubating
<E> JpaPredicate collectionContainsAllNullable(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression);
@Deprecated(forRemoval = true)
default <E> JpaPredicate collectionContainsAllNullable(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression) {
return collectionIncludesNullable( collectionExpression, subCollectionExpression );
}
/**
* Whether a basic collection contains another basic collection with nullable elements.
* Whether a basic collection is a subset of another basic collection with nullable elements.
*
* @since 6.4
* @deprecated Replaced with {@link #collectionIncludesNullable(Expression, Collection)}
*/
@Incubating
<E> JpaPredicate collectionContainsAllNullable(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection);
@Deprecated(forRemoval = true)
default <E> JpaPredicate collectionContainsAllNullable(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection) {
return collectionIncludesNullable( collectionExpression, subCollection );
}
/**
* Whether a basic collection contains another basic collection with nullable elements.
* Whether a basic collection is a subset of another basic collection with nullable elements.
*
* @since 6.4
* @deprecated Replaced with {@link #collectionIncludesNullable(Collection, Expression)}
*/
@Incubating
@Deprecated(forRemoval = true)
default <E> JpaPredicate collectionContainsAllNullable(Collection<E> collection, Expression<? extends Collection<? extends E>> subCollectionExpression) {
return collectionIncludesNullable( collection, subCollectionExpression );
}
/**
* Whether a basic collection is a subset of another basic collection.
*
* @since 6.4
*/
@Incubating
<E> JpaPredicate collectionContainsAllNullable(Collection<E> collection, Expression<? extends Collection<? extends E>> subCollectionExpression);
<E> JpaPredicate collectionIncludes(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression);
/**
* Whether a basic collection is a subset of another basic collection.
*
* @since 6.4
*/
@Incubating
<E> JpaPredicate collectionIncludes(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection);
/**
* Whether a basic collection is a subset of another basic collection.
*
* @since 6.4
*/
@Incubating
<E> JpaPredicate collectionIncludes(Collection<E> collection, Expression<? extends Collection<? extends E>> subCollectionExpression);
/**
* Whether a basic collection is a subset of another basic collection with nullable elements.
*
* @since 6.4
*/
@Incubating
<E> JpaPredicate collectionIncludesNullable(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression);
/**
* Whether a basic collection is a subset of another basic collection with nullable elements.
*
* @since 6.4
*/
@Incubating
<E> JpaPredicate collectionIncludesNullable(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection);
/**
* Whether a basic collection is a subset of another basic collection with nullable elements.
*
* @since 6.4
*/
@Incubating
<E> JpaPredicate collectionIncludesNullable(Collection<E> collection, Expression<? extends Collection<? extends E>> subCollectionExpression);
/**
* Whether one basic collection has any elements common with another basic collection.

View File

@ -2493,24 +2493,28 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <T> JpaPredicate arrayContainsAll(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression) {
return criteriaBuilder.arrayContainsAll( arrayExpression, subArrayExpression );
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <T> JpaPredicate arrayContainsAll(Expression<T[]> arrayExpression, T[] subArray) {
return criteriaBuilder.arrayContainsAll( arrayExpression, subArray );
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <T> JpaPredicate arrayContainsAll(T[] array, Expression<T[]> subArrayExpression) {
return criteriaBuilder.arrayContainsAll( array, subArrayExpression );
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <T> JpaPredicate arrayContainsAllNullable(
Expression<T[]> arrayExpression,
@ -2519,17 +2523,57 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <T> JpaPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, T[] subArray) {
return criteriaBuilder.arrayContainsAllNullable( arrayExpression, subArray );
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <T> JpaPredicate arrayContainsAllNullable(T[] array, Expression<T[]> subArrayExpression) {
return criteriaBuilder.arrayContainsAllNullable( array, subArrayExpression );
}
@Override
@Incubating
public <T> JpaPredicate arrayIncludes(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression) {
return criteriaBuilder.arrayIncludes( arrayExpression, subArrayExpression );
}
@Override
@Incubating
public <T> JpaPredicate arrayIncludes(Expression<T[]> arrayExpression, T[] subArray) {
return criteriaBuilder.arrayIncludes( arrayExpression, subArray );
}
@Override
@Incubating
public <T> JpaPredicate arrayIncludes(T[] array, Expression<T[]> subArrayExpression) {
return criteriaBuilder.arrayIncludes( array, subArrayExpression );
}
@Override
@Incubating
public <T> JpaPredicate arrayIncludesNullable(
Expression<T[]> arrayExpression,
Expression<T[]> subArrayExpression) {
return criteriaBuilder.arrayIncludesNullable( arrayExpression, subArrayExpression );
}
@Override
@Incubating
public <T> JpaPredicate arrayIncludesNullable(Expression<T[]> arrayExpression, T[] subArray) {
return criteriaBuilder.arrayIncludesNullable( arrayExpression, subArray );
}
@Override
@Incubating
public <T> JpaPredicate arrayIncludesNullable(T[] array, Expression<T[]> subArrayExpression) {
return criteriaBuilder.arrayIncludesNullable( array, subArrayExpression );
}
@Override
@Deprecated(forRemoval = true)
@Incubating
@ -2979,6 +3023,7 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <E> JpaPredicate collectionContainsAll(
Expression<? extends Collection<E>> collectionExpression,
@ -2987,6 +3032,7 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <E> JpaPredicate collectionContainsAll(
Expression<? extends Collection<E>> collectionExpression,
@ -2995,14 +3041,16 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <E> JpaPredicate collectionContainsAll(
Collection<E> collection,
Expression<? extends Collection<? extends E>> subArrayExpression) {
return criteriaBuilder.collectionContainsAll( collection, subArrayExpression );
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return criteriaBuilder.collectionContainsAll( collection, subCollectionExpression );
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <E> JpaPredicate collectionContainsAllNullable(
Expression<? extends Collection<E>> collectionExpression,
@ -3011,6 +3059,7 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <E> JpaPredicate collectionContainsAllNullable(
Expression<? extends Collection<E>> collectionExpression,
@ -3019,6 +3068,7 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
}
@Override
@Deprecated(forRemoval = true)
@Incubating
public <E> JpaPredicate collectionContainsAllNullable(
Collection<E> collection,
@ -3026,6 +3076,54 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
return criteriaBuilder.collectionContainsAllNullable( collection, subCollectionExpression );
}
@Override
@Incubating
public <E> JpaPredicate collectionIncludes(
Expression<? extends Collection<E>> collectionExpression,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return criteriaBuilder.collectionIncludes( collectionExpression, subCollectionExpression );
}
@Override
@Incubating
public <E> JpaPredicate collectionIncludes(
Expression<? extends Collection<E>> collectionExpression,
Collection<? extends E> subCollection) {
return criteriaBuilder.collectionIncludes( collectionExpression, subCollection );
}
@Override
@Incubating
public <E> JpaPredicate collectionIncludes(
Collection<E> collection,
Expression<? extends Collection<? extends E>> subArrayExpression) {
return criteriaBuilder.collectionIncludes( collection, subArrayExpression );
}
@Override
@Incubating
public <E> JpaPredicate collectionIncludesNullable(
Expression<? extends Collection<E>> collectionExpression,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return criteriaBuilder.collectionIncludesNullable( collectionExpression, subCollectionExpression );
}
@Override
@Incubating
public <E> JpaPredicate collectionIncludesNullable(
Expression<? extends Collection<E>> collectionExpression,
Collection<? extends E> subCollection) {
return criteriaBuilder.collectionIncludesNullable( collectionExpression, subCollection );
}
@Override
@Incubating
public <E> JpaPredicate collectionIncludesNullable(
Collection<E> collection,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return criteriaBuilder.collectionIncludesNullable( collection, subCollectionExpression );
}
@Override
@Deprecated(forRemoval = true)
@Incubating

View File

@ -2661,6 +2661,33 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return new SqmBooleanExpressionPredicate( contains, negated, creationContext.getNodeBuilder() );
}
@Override
public SqmPredicate visitIncludesPredicate(HqlParser.IncludesPredicateContext ctx) {
final boolean negated = ctx.NOT() != null;
final SqmExpression<?> lhs = (SqmExpression<?>) ctx.expression( 0 ).accept( this );
final SqmExpression<?> rhs = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
final SqmExpressible<?> lhsExpressible = lhs.getExpressible();
final SqmExpressible<?> rhsExpressible = rhs.getExpressible();
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
throw new SemanticException(
"First operand for includes predicate must be a basic plural type expression, but found: " + lhsExpressible.getSqmType(),
query
);
}
if ( rhsExpressible != null && !( rhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
throw new SemanticException(
"Second operand for includes predicate must be a basic plural type expression, but found: " + rhsExpressible.getSqmType(),
query
);
}
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor( "array_includes" ).generateSqmExpression(
asList( lhs, rhs ),
null,
creationContext.getQueryEngine()
);
return new SqmBooleanExpressionPredicate( contains, negated, creationContext.getNodeBuilder() );
}
@Override
public SqmPredicate visitIntersectsPredicate(HqlParser.IntersectsPredicateContext ctx) {
final boolean negated = ctx.NOT() != null;

View File

@ -265,22 +265,52 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
<T> SqmPredicate arrayContainsNullable(T[] array, Expression<T> elementExpression);
@Override
<T> SqmPredicate arrayContainsAll(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression);
default <T> SqmPredicate arrayContainsAll(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression) {
return arrayIncludes( arrayExpression, subArrayExpression );
}
@Override
<T> SqmPredicate arrayContainsAll(Expression<T[]> arrayExpression, T[] subArray);
default <T> SqmPredicate arrayContainsAll(Expression<T[]> arrayExpression, T[] subArray) {
return arrayIncludes( arrayExpression, subArray );
}
@Override
<T> SqmPredicate arrayContainsAll(T[] array, Expression<T[]> subArrayExpression);
default <T> SqmPredicate arrayContainsAll(T[] array, Expression<T[]> subArrayExpression) {
return arrayIncludes( array, subArrayExpression );
}
@Override
<T> SqmPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression);
default <T> SqmPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression) {
return arrayIncludesNullable( arrayExpression, subArrayExpression );
}
@Override
<T> SqmPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, T[] subArray);
default <T> SqmPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, T[] subArray) {
return arrayIncludesNullable( arrayExpression, subArray );
}
@Override
<T> SqmPredicate arrayContainsAllNullable(T[] array, Expression<T[]> subArrayExpression);
default <T> SqmPredicate arrayContainsAllNullable(T[] array, Expression<T[]> subArrayExpression) {
return arrayIncludesNullable( array, subArrayExpression );
}
@Override
<T> SqmPredicate arrayIncludes(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression);
@Override
<T> SqmPredicate arrayIncludes(Expression<T[]> arrayExpression, T[] subArray);
@Override
<T> SqmPredicate arrayIncludes(T[] array, Expression<T[]> subArrayExpression);
@Override
<T> SqmPredicate arrayIncludesNullable(Expression<T[]> arrayExpression, Expression<T[]> subArrayExpression);
@Override
<T> SqmPredicate arrayIncludesNullable(Expression<T[]> arrayExpression, T[] subArray);
@Override
<T> SqmPredicate arrayIncludesNullable(T[] array, Expression<T[]> subArrayExpression);
@Override
default <T> SqmPredicate arrayOverlaps(Expression<T[]> arrayExpression1, Expression<T[]> arrayExpression2) {
@ -475,22 +505,62 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
<E> SqmPredicate collectionContainsNullable(Collection<E> collection, Expression<E> elementExpression);
@Override
<E> SqmPredicate collectionContainsAll(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression);
default <E> SqmPredicate collectionContainsAll(
Expression<? extends Collection<E>> collectionExpression,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return collectionIncludes( collectionExpression, subCollectionExpression );
}
@Override
<E> SqmPredicate collectionContainsAll(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection);
default <E> SqmPredicate collectionContainsAll(
Expression<? extends Collection<E>> collectionExpression,
Collection<? extends E> subCollection) {
return collectionIncludes( collectionExpression, subCollection );
}
@Override
<E> SqmPredicate collectionContainsAll(Collection<E> collection, Expression<? extends Collection<? extends E>> subArrayExpression);
default <E> SqmPredicate collectionContainsAll(
Collection<E> collection,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return collectionIncludes( collection, subCollectionExpression );
}
@Override
<E> SqmPredicate collectionContainsAllNullable(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression);
default <E> SqmPredicate collectionContainsAllNullable(
Expression<? extends Collection<E>> collectionExpression,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return collectionIncludesNullable( collectionExpression, subCollectionExpression );
}
@Override
<E> SqmPredicate collectionContainsAllNullable(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection);
default <E> SqmPredicate collectionContainsAllNullable(
Expression<? extends Collection<E>> collectionExpression,
Collection<? extends E> subCollection) {
return collectionIncludesNullable( collectionExpression, subCollection );
}
@Override
<E> SqmPredicate collectionContainsAllNullable(Collection<E> collection, Expression<? extends Collection<? extends E>> subCollectionExpression);
default <E> SqmPredicate collectionContainsAllNullable(Collection<E> collection, Expression<? extends Collection<? extends E>> subCollectionExpression) {
return collectionIncludesNullable( collection, subCollectionExpression );
}
@Override
<E> SqmPredicate collectionIncludes(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression);
@Override
<E> SqmPredicate collectionIncludes(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection);
@Override
<E> SqmPredicate collectionIncludes(Collection<E> collection, Expression<? extends Collection<? extends E>> subArrayExpression);
@Override
<E> SqmPredicate collectionIncludesNullable(Expression<? extends Collection<E>> collectionExpression, Expression<? extends Collection<? extends E>> subCollectionExpression);
@Override
<E> SqmPredicate collectionIncludesNullable(Expression<? extends Collection<E>> collectionExpression, Collection<? extends E> subCollection);
@Override
<E> SqmPredicate collectionIncludesNullable(Collection<E> collection, Expression<? extends Collection<? extends E>> subCollectionExpression);
@Override
default <E> SqmPredicate collectionOverlaps(Expression<? extends Collection<E>> collectionExpression1, Expression<? extends Collection<? extends E>> collectionExpression2) {

View File

@ -3929,10 +3929,10 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <T> SqmPredicate arrayContainsAll(
public <T> SqmPredicate arrayIncludes(
Expression<T[]> arrayExpression,
Expression<T[]> subArrayExpression) {
return isTrue( getFunctionDescriptor( "array_contains" ).generateSqmExpression(
return isTrue( getFunctionDescriptor( "array_includes" ).generateSqmExpression(
asList( (SqmExpression<?>) arrayExpression, (SqmExpression<?>) subArrayExpression ),
null,
queryEngine
@ -3940,8 +3940,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <T> SqmPredicate arrayContainsAll(Expression<T[]> arrayExpression, T[] subArray) {
return isTrue( getFunctionDescriptor( "array_contains" ).generateSqmExpression(
public <T> SqmPredicate arrayIncludes(Expression<T[]> arrayExpression, T[] subArray) {
return isTrue( getFunctionDescriptor( "array_includes" ).generateSqmExpression(
asList( (SqmExpression<?>) arrayExpression, value( subArray, (SqmExpression<?>) arrayExpression ) ),
null,
queryEngine
@ -3949,8 +3949,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <T> SqmPredicate arrayContainsAll(T[] array, Expression<T[]> subArrayExpression) {
return isTrue( getFunctionDescriptor( "array_contains" ).generateSqmExpression(
public <T> SqmPredicate arrayIncludes(T[] array, Expression<T[]> subArrayExpression) {
return isTrue( getFunctionDescriptor( "array_includes" ).generateSqmExpression(
asList( value( array, (SqmExpression<?>) subArrayExpression ), (SqmExpression<?>) subArrayExpression ),
null,
queryEngine
@ -3958,10 +3958,10 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <T> SqmPredicate arrayContainsAllNullable(
public <T> SqmPredicate arrayIncludesNullable(
Expression<T[]> arrayExpression,
Expression<T[]> subArrayExpression) {
return isTrue( getFunctionDescriptor( "array_contains_nullable" ).generateSqmExpression(
return isTrue( getFunctionDescriptor( "array_includes_nullable" ).generateSqmExpression(
asList( (SqmExpression<?>) arrayExpression, (SqmExpression<?>) subArrayExpression ),
null,
queryEngine
@ -3969,8 +3969,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <T> SqmPredicate arrayContainsAllNullable(Expression<T[]> arrayExpression, T[] subArray) {
return isTrue( getFunctionDescriptor( "array_contains_nullable" ).generateSqmExpression(
public <T> SqmPredicate arrayIncludesNullable(Expression<T[]> arrayExpression, T[] subArray) {
return isTrue( getFunctionDescriptor( "array_includes_nullable" ).generateSqmExpression(
asList( (SqmExpression<?>) arrayExpression, value( subArray, (SqmExpression<?>) arrayExpression ) ),
null,
queryEngine
@ -3978,8 +3978,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <T> SqmPredicate arrayContainsAllNullable(T[] array, Expression<T[]> subArrayExpression) {
return isTrue( getFunctionDescriptor( "array_contains_nullable" ).generateSqmExpression(
public <T> SqmPredicate arrayIncludesNullable(T[] array, Expression<T[]> subArrayExpression) {
return isTrue( getFunctionDescriptor( "array_includes_nullable" ).generateSqmExpression(
asList( value( array, (SqmExpression<?>) subArrayExpression ), (SqmExpression<?>) subArrayExpression ),
null,
queryEngine
@ -4548,10 +4548,10 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <E> SqmPredicate collectionContainsAll(
public <E> SqmPredicate collectionIncludes(
Expression<? extends Collection<E>> collectionExpression,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return isTrue( getFunctionDescriptor( "array_contains" ).generateSqmExpression(
return isTrue( getFunctionDescriptor( "array_includes" ).generateSqmExpression(
asList( (SqmExpression<?>) collectionExpression, (SqmExpression<?>) subCollectionExpression ),
null,
queryEngine
@ -4559,10 +4559,10 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <E> SqmPredicate collectionContainsAll(
public <E> SqmPredicate collectionIncludes(
Expression<? extends Collection<E>> collectionExpression,
Collection<? extends E> subCollection) {
return isTrue( getFunctionDescriptor( "array_contains" ).generateSqmExpression(
return isTrue( getFunctionDescriptor( "array_includes" ).generateSqmExpression(
asList( (SqmExpression<?>) collectionExpression, value( subCollection, (SqmExpression<?>) collectionExpression ) ),
null,
queryEngine
@ -4570,10 +4570,10 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <E> SqmPredicate collectionContainsAll(
public <E> SqmPredicate collectionIncludes(
Collection<E> collection,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return isTrue( getFunctionDescriptor( "array_contains" ).generateSqmExpression(
return isTrue( getFunctionDescriptor( "array_includes" ).generateSqmExpression(
asList( value( collection, (SqmExpression<?>) subCollectionExpression ), (SqmExpression<?>) subCollectionExpression ),
null,
queryEngine
@ -4581,10 +4581,10 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <E> SqmPredicate collectionContainsAllNullable(
public <E> SqmPredicate collectionIncludesNullable(
Expression<? extends Collection<E>> collectionExpression,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return isTrue( getFunctionDescriptor( "array_contains_nullable" ).generateSqmExpression(
return isTrue( getFunctionDescriptor( "array_includes_nullable" ).generateSqmExpression(
asList( (SqmExpression<?>) collectionExpression, (SqmExpression<?>) subCollectionExpression ),
null,
queryEngine
@ -4592,10 +4592,10 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <E> SqmPredicate collectionContainsAllNullable(
public <E> SqmPredicate collectionIncludesNullable(
Expression<? extends Collection<E>> collectionExpression,
Collection<? extends E> subCollection) {
return isTrue( getFunctionDescriptor( "array_contains_nullable" ).generateSqmExpression(
return isTrue( getFunctionDescriptor( "array_includes_nullable" ).generateSqmExpression(
asList( (SqmExpression<?>) collectionExpression, value( subCollection, (SqmExpression<?>) collectionExpression ) ),
null,
queryEngine
@ -4603,10 +4603,10 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
@Override
public <E> SqmPredicate collectionContainsAllNullable(
public <E> SqmPredicate collectionIncludesNullable(
Collection<E> collection,
Expression<? extends Collection<? extends E>> subCollectionExpression) {
return isTrue( getFunctionDescriptor( "array_contains_nullable" ).generateSqmExpression(
return isTrue( getFunctionDescriptor( "array_includes_nullable" ).generateSqmExpression(
asList( value( collection, (SqmExpression<?>) subCollectionExpression ), (SqmExpression<?>) subCollectionExpression ),
null,
queryEngine

View File

@ -36,7 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsStructuralArrays.class)
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
@BootstrapServiceRegistry(integrators = SharedDriverManagerTypeCacheClearingIntegrator.class)
public class ArrayContainsArrayTest {
public class ArrayIncludesTest {
@BeforeEach
public void prepareData(SessionFactoryScope scope) {
@ -55,39 +55,39 @@ public class ArrayContainsArrayTest {
}
@Test
public void testContainsArray(SessionFactoryScope scope) {
public void testIncludesArray(SessionFactoryScope scope) {
scope.inSession( em -> {
//tag::hql-array-contains-array-example[]
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_contains(e.theArray, array('abc', 'def'))", EntityWithArrays.class )
//tag::hql-array-includes-example[]
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_includes(e.theArray, array('abc', 'def'))", EntityWithArrays.class )
.getResultList();
//end::hql-array-contains-array-example[]
//end::hql-array-includes-example[]
assertEquals( 1, results.size() );
assertEquals( 2L, results.get( 0 ).getId() );
} );
}
@Test
public void testDoesNotContainArray(SessionFactoryScope scope) {
public void testDoesNotIncludeArray(SessionFactoryScope scope) {
scope.inSession( em -> {
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_contains(e.theArray, array('xyz'))", EntityWithArrays.class )
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_includes(e.theArray, array('xyz'))", EntityWithArrays.class )
.getResultList();
assertEquals( 0, results.size() );
} );
}
@Test
public void testContainsArrayPartly(SessionFactoryScope scope) {
public void testIncludesArrayPartly(SessionFactoryScope scope) {
scope.inSession( em -> {
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_contains(e.theArray, array('abc','xyz'))", EntityWithArrays.class )
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_includes(e.theArray, array('abc','xyz'))", EntityWithArrays.class )
.getResultList();
assertEquals( 0, results.size() );
} );
}
@Test
public void testContainsArrayWithNullElementOnly(SessionFactoryScope scope) {
public void testIncludesArrayWithNullElementOnly(SessionFactoryScope scope) {
scope.inSession( em -> {
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_contains_nullable(e.theArray, array(null))", EntityWithArrays.class )
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_includes_nullable(e.theArray, array(null))", EntityWithArrays.class )
.getResultList();
assertEquals( 1, results.size() );
assertEquals( 2L, results.get( 0 ).getId() );
@ -95,34 +95,22 @@ public class ArrayContainsArrayTest {
}
@Test
public void testContainsArrayWithNullElement(SessionFactoryScope scope) {
public void testIncludesArrayWithNullElement(SessionFactoryScope scope) {
scope.inSession( em -> {
//tag::hql-array-contains-array-nullable-example[]
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_contains_nullable(e.theArray, array('abc',null))", EntityWithArrays.class )
//tag::hql-array-includes-nullable-example[]
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_includes_nullable(e.theArray, array('abc',null))", EntityWithArrays.class )
.getResultList();
//end::hql-array-contains-array-nullable-example[]
//end::hql-array-includes-nullable-example[]
assertEquals( 1, results.size() );
assertEquals( 2L, results.get( 0 ).getId() );
} );
}
@Test
public void testContainsElementParameter(SessionFactoryScope scope) {
public void testIncludesArrayParameter(SessionFactoryScope scope) {
scope.inSession( em -> {
List<EntityWithArrays> results = em.createQuery(
"from EntityWithArrays e where array_contains_nullable(e.theArray, :param)",
EntityWithArrays.class
).setParameter( "param", "abc" ).getResultList();
assertEquals( 1, results.size() );
assertEquals( 2L, results.get( 0 ).getId() );
} );
}
@Test
public void testContainsArrayParameter(SessionFactoryScope scope) {
scope.inSession( em -> {
List<EntityWithArrays> results = em.createQuery(
"from EntityWithArrays e where array_contains_nullable(e.theArray, :param)",
"from EntityWithArrays e where array_includes_nullable(e.theArray, :param)",
EntityWithArrays.class
).setParameter( "param", new String[]{ "abc", null } ).getResultList();
assertEquals( 1, results.size() );
@ -131,10 +119,10 @@ public class ArrayContainsArrayTest {
}
@Test
public void testContainsNullParameter(SessionFactoryScope scope) {
public void testIncludesNullParameter(SessionFactoryScope scope) {
scope.inSession( em -> {
List<EntityWithArrays> results = em.createQuery(
"from EntityWithArrays e where array_contains_nullable(e.theArray, :param)",
"from EntityWithArrays e where array_includes_nullable(e.theArray, :param)",
EntityWithArrays.class
).setParameter( "param", null ).getResultList();
assertEquals( 0, results.size() );
@ -149,24 +137,24 @@ public class ArrayContainsArrayTest {
final JpaRoot<EntityWithArrays> root = cq.from( EntityWithArrays.class );
cq.multiselect(
root.get( "id" ),
cb.arrayContainsAll( root.get( "theArray" ), cb.arrayLiteral( "xyz" ) ),
cb.arrayContainsAll( root.get( "theArray" ), new String[]{ "xyz" } ),
cb.arrayContainsAll( new String[]{ "abc", "xyz" }, cb.arrayLiteral( "xyz" ) ),
cb.arrayContainsAllNullable( root.get( "theArray" ), cb.arrayLiteral( "xyz" ) ),
cb.arrayContainsAllNullable( root.get( "theArray" ), new String[]{ "xyz" } ),
cb.arrayContainsAllNullable( new String[]{ "abc", "xyz" }, cb.arrayLiteral( "xyz" ) )
cb.arrayIncludes( root.get( "theArray" ), cb.arrayLiteral( "xyz" ) ),
cb.arrayIncludes( root.get( "theArray" ), new String[]{ "xyz" } ),
cb.arrayIncludes( new String[]{ "abc", "xyz" }, cb.arrayLiteral( "xyz" ) ),
cb.arrayIncludesNullable( root.get( "theArray" ), cb.arrayLiteral( "xyz" ) ),
cb.arrayIncludesNullable( root.get( "theArray" ), new String[]{ "xyz" } ),
cb.arrayIncludesNullable( new String[]{ "abc", "xyz" }, cb.arrayLiteral( "xyz" ) )
);
em.createQuery( cq ).getResultList();
// Should all fail to compile
// cb.arrayContainsAll( root.<Integer[]>get( "theArray" ), cb.arrayLiteral( "xyz" ) );
// cb.arrayContainsAll( root.<Integer[]>get( "theArray" ), new String[]{ "xyz" } );
// cb.arrayContainsAll( new String[0], cb.literal( 1 ) );
// cb.arrayContainsAll( new Integer[0], cb.literal( "" ) );
// cb.arrayContainsAllNullable( root.<Integer[]>get( "theArray" ), cb.arrayLiteral( "xyz" ) );
// cb.arrayContainsAllNullable( root.<Integer[]>get( "theArray" ), new String[]{ "xyz" } );
// cb.arrayContainsAllNullable( new String[0], cb.literal( 1 ) );
// cb.arrayContainsAllNullable( new Integer[0], cb.literal( "" ) );
// cb.arrayIncludes( root.<Integer[]>get( "theArray" ), cb.arrayLiteral( "xyz" ) );
// cb.arrayIncludes( root.<Integer[]>get( "theArray" ), new String[]{ "xyz" } );
// cb.arrayIncludes( new String[0], cb.literal( 1 ) );
// cb.arrayIncludes( new Integer[0], cb.literal( "" ) );
// cb.arrayIncludesNullable( root.<Integer[]>get( "theArray" ), cb.arrayLiteral( "xyz" ) );
// cb.arrayIncludesNullable( root.<Integer[]>get( "theArray" ), new String[]{ "xyz" } );
// cb.arrayIncludesNullable( new String[0], cb.literal( 1 ) );
// cb.arrayIncludesNullable( new Integer[0], cb.literal( "" ) );
} );
}
@ -178,46 +166,34 @@ public class ArrayContainsArrayTest {
final JpaRoot<EntityWithArrays> root = cq.from( EntityWithArrays.class );
cq.multiselect(
root.get( "id" ),
cb.collectionContainsAll( root.<Collection<String>>get( "theCollection" ), cb.collectionLiteral( "xyz" ) ),
cb.collectionContainsAll( root.get( "theCollection" ), List.of( "xyz" ) ),
cb.collectionContainsAll( List.of( "abc", "xyz" ), cb.collectionLiteral( "xyz" ) ),
cb.collectionContainsAllNullable( root.<Collection<String>>get( "theCollection" ), cb.collectionLiteral( "xyz" ) ),
cb.collectionContainsAllNullable( root.get( "theCollection" ), List.of( "xyz" ) ),
cb.collectionContainsAllNullable( List.of( "abc", "xyz" ), cb.collectionLiteral( "xyz" ) )
cb.collectionIncludes( root.<Collection<String>>get( "theCollection" ), cb.collectionLiteral( "xyz" ) ),
cb.collectionIncludes( root.get( "theCollection" ), List.of( "xyz" ) ),
cb.collectionIncludes( List.of( "abc", "xyz" ), cb.collectionLiteral( "xyz" ) ),
cb.collectionIncludesNullable( root.<Collection<String>>get( "theCollection" ), cb.collectionLiteral( "xyz" ) ),
cb.collectionIncludesNullable( root.get( "theCollection" ), List.of( "xyz" ) ),
cb.collectionIncludesNullable( List.of( "abc", "xyz" ), cb.collectionLiteral( "xyz" ) )
);
em.createQuery( cq ).getResultList();
// Should all fail to compile
// cb.collectionContainsAll( root.<Collection<Integer>>get( "theCollection" ), cb.collectionLiteral( "xyz" ) );
// cb.collectionContainsAll( root.<Collection<Integer>>get( "theCollection" ), List.of( "xyz" ) );
// cb.collectionContainsAll( Collections.<String>emptyList(), cb.literal( 1 ) );
// cb.collectionContainsAll( Collections.<Integer>emptyList(), cb.literal( "" ) );
// cb.collectionContainsAllNullable( root.<Collection<Integer>>get( "theCollection" ), cb.collectionLiteral( "xyz" ) );
// cb.collectionContainsAllNullable( root.<Collection<Integer>>get( "theCollection" ), List.of( "xyz" ) );
// cb.collectionContainsAllNullable( Collections.<String>emptyList(), cb.literal( 1 ) );
// cb.collectionContainsAllNullable( Collections.<Integer>emptyList(), cb.literal( "" ) );
// cb.collectionIncludes( root.<Collection<Integer>>get( "theCollection" ), cb.collectionLiteral( "xyz" ) );
// cb.collectionIncludes( root.<Collection<Integer>>get( "theCollection" ), List.of( "xyz" ) );
// cb.collectionIncludes( Collections.<String>emptyList(), cb.literal( 1 ) );
// cb.collectionIncludes( Collections.<Integer>emptyList(), cb.literal( "" ) );
// cb.collectionIncludesNullable( root.<Collection<Integer>>get( "theCollection" ), cb.collectionLiteral( "xyz" ) );
// cb.collectionIncludesNullable( root.<Collection<Integer>>get( "theCollection" ), List.of( "xyz" ) );
// cb.collectionIncludesNullable( Collections.<String>emptyList(), cb.literal( 1 ) );
// cb.collectionIncludesNullable( Collections.<Integer>emptyList(), cb.literal( "" ) );
} );
}
@Test
public void testContainsArraySyntax(SessionFactoryScope scope) {
public void testIncludesSyntax(SessionFactoryScope scope) {
scope.inSession( em -> {
//tag::hql-array-contains-array-hql-example[]
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where e.theArray contains ['abc', 'def']", EntityWithArrays.class )
//tag::hql-array-includes-hql-example[]
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where e.theArray includes ['abc', 'def']", EntityWithArrays.class )
.getResultList();
//end::hql-array-contains-array-hql-example[]
assertEquals( 1, results.size() );
assertEquals( 2L, results.get( 0 ).getId() );
} );
}
@Test
public void testInArraySyntax(SessionFactoryScope scope) {
scope.inSession( em -> {
//tag::hql-array-in-array-hql-example[]
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where ['abc', 'def'] in e.theArray", EntityWithArrays.class )
.getResultList();
//end::hql-array-in-array-hql-example[]
//end::hql-array-includes-hql-example[]
assertEquals( 1, results.size() );
assertEquals( 2L, results.get( 0 ).getId() );
} );

View File

@ -31,6 +31,7 @@ and would now be `BigIntegerBigDecimalArray`, because the preferred Java type fo
To specify a custom array type name, annotate the persistent property with `@Column(columnDefinition = "BigIntegerArray")`.
[[user-defined-type]]
== Changes to `UserDefinedType`
`UserDefinedType` was renamed to `UserDefinedObjectType` and everything except access to column information
was abstracted in a new interface named `UserDefinedType`. This was done to allow modelling dependencies between
@ -48,3 +49,11 @@ to make it clear that these contracts might still evolve.
Another change is to the already incubating `ColumnOrderingStrategy`,
where the argument type of `orderUserDefinedTypeColumns` was changed from `UserDefinedType` to `UserDefinedObjectType`.
[[array-contains-array-deprecation]]
== Subset check for arrays to use `array_includes`
Support for `array_contains()` to accept an array as element argument is deprecated and will emit a warning.
To check if an array is a subset of another array, use the `array_includes()` function,
or the new `INCLUDES` predicate i.e. `array INCLUDES subarray`.

View File

@ -131,6 +131,10 @@ Plenty of syntax sugar for array operations was added:
|`array contains element` or `element in array`
|Contains predicate for containment check
|`array_includes(array, array)`
|`array includes subArray`
|Predicate to for subset checking
|`array_intersects(array, array(1, 2))`
|`array intersects [1, 2]`
|Overlaps predicate for overlaps check