HHH-17335 Add array_contains function
This commit is contained in:
parent
5ea40e255d
commit
65fb449776
|
@ -5,6 +5,7 @@
|
|||
:example-dir-model: {testing-project-dir}/src/main/java/org/hibernate/testing/orm/domain/userguide
|
||||
:core-project-dir: {root-project-dir}/hibernate-core
|
||||
:example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/hql
|
||||
:array-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/array
|
||||
:extrasdir: extras
|
||||
|
||||
This chapter describes Hibernate Query Language (HQL) and Jakarta Persistence Query Language (JPQL).
|
||||
|
@ -1107,6 +1108,51 @@ Finally, the following functions evaluate the id, version, or natural id of an e
|
|||
Mainly useful with <<associations-not-found,`@NotFound` mappings>>. | ✗
|
||||
|===
|
||||
|
||||
[[hql-functions-arrays]]
|
||||
==== Functions for dealing with arrays
|
||||
|
||||
The following functions deal with SQL array types, which are not supported on every database.
|
||||
|
||||
[[hql-array-functions]]
|
||||
|===
|
||||
| Function | Purpose
|
||||
|
||||
| `array()` | Creates an array based on the passed arguments
|
||||
| `array_contains()` | Whether an array contains an element
|
||||
| `array_contains_null()` | Whether an array contains a null
|
||||
|===
|
||||
|
||||
===== `array()`
|
||||
|
||||
Creates an array based on the passed arguments, and infers the array type from the context if possible.
|
||||
|
||||
[[hql-array-constructor-example]]
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{array-example-dir-hql}/ArrayConstructorTest.java[tags=hql-array-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[hql-array-contains-functions]]
|
||||
===== `array_contains()` and `array_contains_null()`
|
||||
|
||||
Both functions return `null` if the first argument, the array, is null, yet the result of the `array_contains` function
|
||||
is undefined when the second argument, the element to search, is `null`.
|
||||
The `array_contains_null` function only takes a single argument, the array, and will return whether the array contains a `null` element.
|
||||
|
||||
[[hql-array-contains-example]]
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{array-example-dir-hql}/ArrayContainsTest.java[tags=hql-array-contains-example]
|
||||
----
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{array-example-dir-hql}/ArrayContainsTest.java[tags=hql-array-contains-null-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[hql-user-defined-functions]]
|
||||
==== Native and user-defined functions
|
||||
|
||||
|
@ -2194,7 +2240,7 @@ The following ordered set aggregate functions are available on many platforms:
|
|||
|
||||
| Inverse distribution functions | `mode()`, `percentile_cont()`, `percentile_disc()`
|
||||
| Hypothetical set functions | `rank()`, `dense_rank()`, `percent_rank()`, `cume_dist()`
|
||||
| Other | `listagg()`
|
||||
| Other | `listagg()`, `array_agg`
|
||||
|===
|
||||
|
||||
Actually, the most widely-supported ordered set aggregate function is one which builds a string by concatenating the values within a group.
|
||||
|
|
|
@ -463,6 +463,8 @@ public class CockroachLegacyDialect extends Dialect {
|
|||
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||
functionFactory.array_casting();
|
||||
functionFactory.arrayAggregate();
|
||||
functionFactory.arrayContains_operator();
|
||||
functionFactory.arrayContainsNull_array_position();
|
||||
|
||||
functionContributions.getFunctionRegistry().register(
|
||||
"trunc",
|
||||
|
|
|
@ -372,6 +372,8 @@ public class H2LegacyDialect extends Dialect {
|
|||
functionFactory.listagg( null );
|
||||
functionFactory.array();
|
||||
functionFactory.arrayAggregate();
|
||||
functionFactory.arrayContains();
|
||||
functionFactory.arrayContainsNull();
|
||||
}
|
||||
else {
|
||||
// Use group_concat until 2.x as listagg was buggy
|
||||
|
|
|
@ -249,6 +249,8 @@ public class HSQLLegacyDialect extends Dialect {
|
|||
functionFactory.listagg_groupConcat();
|
||||
functionFactory.array();
|
||||
functionFactory.arrayAggregate();
|
||||
functionFactory.arrayContains_hsql();
|
||||
functionFactory.arrayContainsNull_hsql();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -285,6 +285,8 @@ public class OracleLegacyDialect extends Dialect {
|
|||
|
||||
functionFactory.array_oracle();
|
||||
functionFactory.arrayAggregate_jsonArrayagg();
|
||||
functionFactory.arrayContains_oracle();
|
||||
functionFactory.arrayContainsNull_oracle();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -583,6 +583,8 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
|||
functionFactory.listagg_stringAgg( "varchar" );
|
||||
functionFactory.array_casting();
|
||||
functionFactory.arrayAggregate();
|
||||
functionFactory.arrayContains_operator();
|
||||
functionFactory.arrayContainsNull_array_position();
|
||||
|
||||
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
||||
functionFactory.makeDateTimeTimestamp();
|
||||
|
|
|
@ -450,6 +450,8 @@ public class CockroachDialect extends Dialect {
|
|||
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||
functionFactory.array_casting();
|
||||
functionFactory.arrayAggregate();
|
||||
functionFactory.arrayContains_operator();
|
||||
functionFactory.arrayContainsNull_array_position();
|
||||
|
||||
functionContributions.getFunctionRegistry().register(
|
||||
"trunc",
|
||||
|
|
|
@ -312,6 +312,8 @@ public class H2Dialect extends Dialect {
|
|||
functionFactory.hypotheticalOrderedSetAggregates();
|
||||
functionFactory.array();
|
||||
functionFactory.arrayAggregate();
|
||||
functionFactory.arrayContains();
|
||||
functionFactory.arrayContainsNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -189,6 +189,8 @@ public class HSQLDialect extends Dialect {
|
|||
functionFactory.listagg_groupConcat();
|
||||
functionFactory.array();
|
||||
functionFactory.arrayAggregate();
|
||||
functionFactory.arrayContains_hsql();
|
||||
functionFactory.arrayContainsNull_hsql();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.type.descriptor.ValueExtractor;
|
|||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.BasicBinder;
|
||||
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
||||
|
@ -42,51 +43,24 @@ import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_A
|
|||
* @author Christian Beikov
|
||||
* @author Jordan Gigov
|
||||
*/
|
||||
public class OracleArrayJdbcType implements JdbcType {
|
||||
public class OracleArrayJdbcType extends ArrayJdbcType {
|
||||
|
||||
private final JdbcType elementJdbcType;
|
||||
private final String typeName;
|
||||
|
||||
public OracleArrayJdbcType(JdbcType elementJdbcType, String typeName) {
|
||||
this.elementJdbcType = elementJdbcType;
|
||||
super( elementJdbcType );
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCode() {
|
||||
return Types.ARRAY;
|
||||
}
|
||||
|
||||
public JdbcType getElementJdbcType() {
|
||||
return elementJdbcType;
|
||||
}
|
||||
|
||||
public String getTypeName() {
|
||||
return typeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||
Integer precision,
|
||||
Integer scale,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final JavaType<Object> elementJavaType =
|
||||
elementJdbcType.getJdbcRecommendedJavaTypeMapping( precision, scale, typeConfiguration );
|
||||
return typeConfiguration.getJavaTypeRegistry().resolveDescriptor(
|
||||
Array.newInstance( elementJavaType.getJavaTypeClass(), 0 ).getClass()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaTypeDescriptor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return java.sql.Array.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
|
||||
//noinspection unchecked
|
||||
|
@ -257,6 +231,43 @@ public class OracleArrayJdbcType implements JdbcType {
|
|||
false
|
||||
)
|
||||
);
|
||||
database.addAuxiliaryDatabaseObject(
|
||||
new NamedAuxiliaryDatabaseObject(
|
||||
arrayTypeName + "_contains",
|
||||
database.getDefaultNamespace(),
|
||||
new String[]{
|
||||
"create or replace function " + arrayTypeName + "_contains(arr in " + arrayTypeName +
|
||||
", elem in " + getRawTypeName( elementType ) + ") return number deterministic is begin " +
|
||||
"if arr is null then return null; end if; " +
|
||||
"if elem is null then " +
|
||||
"for i in 1 .. arr.count loop " +
|
||||
"if arr(i) is null then return 1; end if; " +
|
||||
"end loop; " +
|
||||
"else " +
|
||||
"for i in 1 .. arr.count loop " +
|
||||
"if arr(i)=elem then return 1; end if; " +
|
||||
"end loop; " +
|
||||
"end if; " +
|
||||
"return 0; " +
|
||||
"end;"
|
||||
},
|
||||
new String[] { "drop function " + arrayTypeName + "_contains" },
|
||||
emptySet(),
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static String getRawTypeName(String typeName) {
|
||||
//trim off the length/precision/scale
|
||||
final int paren = typeName.indexOf( '(' );
|
||||
if ( paren > 0 ) {
|
||||
final int parenEnd = typeName.lastIndexOf( ')' );
|
||||
return parenEnd + 1 == typeName.length()
|
||||
? typeName.substring( 0, paren )
|
||||
: typeName.substring( 0, paren ) + typeName.substring( parenEnd + 1 );
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -314,6 +314,8 @@ public class OracleDialect extends Dialect {
|
|||
|
||||
functionFactory.array_oracle();
|
||||
functionFactory.arrayAggregate_jsonArrayagg();
|
||||
functionFactory.arrayContains_oracle();
|
||||
functionFactory.arrayContainsNull_oracle();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -631,6 +631,8 @@ public class PostgreSQLDialect extends Dialect {
|
|||
functionFactory.listagg_stringAgg( "varchar" );
|
||||
functionFactory.array_casting();
|
||||
functionFactory.arrayAggregate();
|
||||
functionFactory.arrayContains_operator();
|
||||
functionFactory.arrayContainsNull_array_position();
|
||||
|
||||
functionFactory.makeDateTimeTimestamp();
|
||||
// Note that PostgreSQL doesn't support the OVER clause for ordered set-aggregate functions
|
||||
|
|
|
@ -13,12 +13,20 @@ import org.hibernate.boot.model.FunctionContributions;
|
|||
import org.hibernate.dialect.Dialect;
|
||||
|
||||
import org.hibernate.dialect.function.array.ArrayAggFunction;
|
||||
import org.hibernate.dialect.function.array.ArrayAndElementArgumentTypeResolver;
|
||||
import org.hibernate.dialect.function.array.ArrayAndElementArgumentValidator;
|
||||
import org.hibernate.dialect.function.array.ArrayArgumentValidator;
|
||||
import org.hibernate.dialect.function.array.ArrayConstructorFunction;
|
||||
import org.hibernate.dialect.function.array.ArrayContainsOperatorFunction;
|
||||
import org.hibernate.dialect.function.array.CastingArrayConstructorFunction;
|
||||
import org.hibernate.dialect.function.array.OracleArrayAggEmulation;
|
||||
import org.hibernate.dialect.function.array.OracleArrayConstructorFunction;
|
||||
import org.hibernate.dialect.function.array.OracleArrayContainsFunction;
|
||||
import org.hibernate.dialect.function.array.OracleArrayContainsNullFunction;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.BasicTypeRegistry;
|
||||
|
@ -2580,4 +2588,107 @@ public class CommonFunctionFactory {
|
|||
public void arrayAggregate_jsonArrayagg() {
|
||||
functionRegistry.register( ArrayAggFunction.FUNCTION_NAME, new OracleArrayAggEmulation() );
|
||||
}
|
||||
|
||||
/**
|
||||
* H2 array_contains() function
|
||||
*/
|
||||
public void arrayContains() {
|
||||
functionRegistry.namedDescriptorBuilder( "array_contains" )
|
||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.invariant( booleanType ) )
|
||||
.setArgumentsValidator(
|
||||
StandardArgumentsValidators.composite(
|
||||
StandardArgumentsValidators.exactly( 2 ),
|
||||
ArrayAndElementArgumentValidator.DEFAULT_INSTANCE
|
||||
)
|
||||
)
|
||||
.setArgumentTypeResolver( ArrayAndElementArgumentTypeResolver.DEFAULT_INSTANCE )
|
||||
.setArgumentListSignature( "(ARRAY array, OBJECT element)" )
|
||||
.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* HSQL array_contains() function
|
||||
*/
|
||||
public void arrayContains_hsql() {
|
||||
functionRegistry.patternDescriptorBuilder( "array_contains", "position_array(?2 in ?1)>0" )
|
||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.invariant( booleanType ) )
|
||||
.setArgumentsValidator(
|
||||
StandardArgumentsValidators.composite(
|
||||
StandardArgumentsValidators.exactly( 2 ),
|
||||
ArrayAndElementArgumentValidator.DEFAULT_INSTANCE
|
||||
)
|
||||
)
|
||||
.setArgumentTypeResolver( ArrayAndElementArgumentTypeResolver.DEFAULT_INSTANCE )
|
||||
.setArgumentListSignature( "(ARRAY array, OBJECT element)" )
|
||||
.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* CockroachDB and PostgreSQL array contains operator
|
||||
*/
|
||||
public void arrayContains_operator() {
|
||||
functionRegistry.register( "array_contains", new ArrayContainsOperatorFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle array_contains() function
|
||||
*/
|
||||
public void arrayContains_oracle() {
|
||||
functionRegistry.register( "array_contains", new OracleArrayContainsFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* H2, HSQL array_contains_null() function
|
||||
*/
|
||||
public void arrayContainsNull() {
|
||||
functionRegistry.patternDescriptorBuilder( "array_contains_null", "array_contains(?1,null)" )
|
||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.invariant( booleanType ) )
|
||||
.setArgumentsValidator(
|
||||
StandardArgumentsValidators.composite(
|
||||
StandardArgumentsValidators.exactly( 1 ),
|
||||
ArrayArgumentValidator.DEFAULT_INSTANCE
|
||||
)
|
||||
)
|
||||
.setArgumentListSignature( "(ARRAY array)" )
|
||||
.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* CockroachDB and PostgreSQL array contains null emulation
|
||||
*/
|
||||
public void arrayContainsNull_array_position() {
|
||||
functionRegistry.patternDescriptorBuilder( "array_contains_null", "array_position(?1,null) is not null" )
|
||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.invariant( booleanType ) )
|
||||
.setArgumentsValidator(
|
||||
StandardArgumentsValidators.composite(
|
||||
StandardArgumentsValidators.exactly( 1 ),
|
||||
ArrayArgumentValidator.DEFAULT_INSTANCE
|
||||
)
|
||||
)
|
||||
.setArgumentListSignature( "(ARRAY array)" )
|
||||
.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle array_contains() function
|
||||
*/
|
||||
public void arrayContainsNull_oracle() {
|
||||
functionRegistry.register( "array_contains_null", new OracleArrayContainsNullFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* CockroachDB and PostgreSQL array contains null emulation
|
||||
*/
|
||||
public void arrayContainsNull_hsql() {
|
||||
functionRegistry.patternDescriptorBuilder( "array_contains_null", "position_array(null in ?1)>0" )
|
||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.invariant( booleanType ) )
|
||||
.setArgumentsValidator(
|
||||
StandardArgumentsValidators.composite(
|
||||
StandardArgumentsValidators.exactly( 1 ),
|
||||
ArrayArgumentValidator.DEFAULT_INSTANCE
|
||||
)
|
||||
)
|
||||
.setArgumentListSignature( "(ARRAY array)" )
|
||||
.register();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.ReturnableType;
|
||||
import org.hibernate.query.sqm.SqmExpressible;
|
||||
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.SqmFunction;
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
|
||||
/**
|
||||
* A {@link FunctionArgumentTypeResolver} that resolves the array argument type based on the element argument type
|
||||
* or the element argument type based on the array argument type.
|
||||
*/
|
||||
public class ArrayAndElementArgumentTypeResolver implements FunctionArgumentTypeResolver {
|
||||
|
||||
public static final FunctionArgumentTypeResolver DEFAULT_INSTANCE = new ArrayAndElementArgumentTypeResolver( 0, 1 );
|
||||
|
||||
private final int arrayIndex;
|
||||
private final int elementIndex;
|
||||
|
||||
public ArrayAndElementArgumentTypeResolver(int arrayIndex, int elementIndex) {
|
||||
this.arrayIndex = arrayIndex;
|
||||
this.elementIndex = elementIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingModelExpressible<?> resolveFunctionArgumentType(
|
||||
SqmFunction<?> function,
|
||||
int argumentIndex,
|
||||
SqmToSqlAstConverter converter) {
|
||||
if ( argumentIndex == arrayIndex ) {
|
||||
final SqmTypedNode<?> argument = function.getArguments().get( elementIndex );
|
||||
final DomainType<?> sqmType = argument.getExpressible().getSqmType();
|
||||
if ( sqmType instanceof ReturnableType<?> ) {
|
||||
return ArrayTypeHelper.resolveArrayType(
|
||||
sqmType,
|
||||
converter.getCreationContext().getSessionFactory().getTypeConfiguration()
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ( argumentIndex == elementIndex ) {
|
||||
final SqmTypedNode<?> argument = function.getArguments().get( arrayIndex );
|
||||
final SqmExpressible<?> sqmType = argument.getNodeType();
|
||||
if ( sqmType instanceof BasicPluralType<?, ?> ) {
|
||||
return ( (BasicPluralType<?, ?>) sqmType ).getElementType();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.SqmExpressible;
|
||||
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.BasicType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* A {@link ArgumentsValidator} that validates the array type is compatible with the element type.
|
||||
*/
|
||||
public class ArrayAndElementArgumentValidator extends ArrayArgumentValidator {
|
||||
|
||||
public static final ArgumentsValidator DEFAULT_INSTANCE = new ArrayAndElementArgumentValidator( 0, 1 );
|
||||
|
||||
private final int elementIndex;
|
||||
|
||||
public ArrayAndElementArgumentValidator(int arrayIndex, int elementIndex) {
|
||||
super( arrayIndex );
|
||||
this.elementIndex = elementIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
String functionName,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final BasicType<?> expectedElementType = getElementType( arguments, functionName, typeConfiguration );
|
||||
final SqmTypedNode<?> elementArgument = arguments.get( elementIndex );
|
||||
final SqmExpressible<?> elementType = elementArgument.getExpressible().getSqmType();
|
||||
if ( expectedElementType != elementType ) {
|
||||
throw new FunctionArgumentException(
|
||||
String.format(
|
||||
"Parameter %d of function '%s()' has type %s, but argument is of type '%s'",
|
||||
elementIndex,
|
||||
functionName,
|
||||
expectedElementType.getJavaTypeDescriptor().getTypeName(),
|
||||
elementType.getTypeName()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.SqmExpressible;
|
||||
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.BasicType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* A {@link ArgumentsValidator} that validates the array type is compatible with the element type.
|
||||
*/
|
||||
public class ArrayArgumentValidator implements ArgumentsValidator {
|
||||
|
||||
public static final ArgumentsValidator DEFAULT_INSTANCE = new ArrayArgumentValidator( 0 );
|
||||
|
||||
private final int arrayIndex;
|
||||
|
||||
public ArrayArgumentValidator(int arrayIndex) {
|
||||
this.arrayIndex = arrayIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
String functionName,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
getElementType( arguments, functionName, typeConfiguration );
|
||||
}
|
||||
|
||||
protected BasicType<?> getElementType(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
String functionName,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final SqmTypedNode<?> arrayArgument = arguments.get( arrayIndex );
|
||||
final SqmExpressible<?> arrayType = arrayArgument.getExpressible().getSqmType();
|
||||
if ( !( arrayType instanceof BasicPluralType<?, ?> ) ) {
|
||||
throw new FunctionArgumentException(
|
||||
String.format(
|
||||
"Parameter %d of function '%s()' requires an array type, but argument is of type '%s'",
|
||||
arrayIndex,
|
||||
functionName,
|
||||
arrayType.getTypeName()
|
||||
)
|
||||
);
|
||||
}
|
||||
return ( (BasicPluralType<?, ?>) arrayType ).getElementType();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
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 contains function that also applies a cast to the element argument. PostgreSQL needs this,
|
||||
* because by default it assumes a {@code text[]}, which is not compatible with {@code varchar[]}.
|
||||
*/
|
||||
public class ArrayContainsOperatorFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
public ArrayContainsOperatorFunction(TypeConfiguration typeConfiguration) {
|
||||
super(
|
||||
"array_contains",
|
||||
StandardArgumentsValidators.composite(
|
||||
StandardArgumentsValidators.exactly( 2 ),
|
||||
ArrayAndElementArgumentValidator.DEFAULT_INSTANCE
|
||||
),
|
||||
StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.standardBasicTypeForJavaType( Boolean.class ) ),
|
||||
ArrayAndElementArgumentTypeResolver.DEFAULT_INSTANCE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> sqlAstArguments,
|
||||
SqlAstTranslator<?> walker) {
|
||||
final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 );
|
||||
final Expression elementExpression = (Expression) sqlAstArguments.get( 1 );
|
||||
arrayExpression.accept( walker );
|
||||
sqlAppender.append( "@>" );
|
||||
if ( needsArrayCasting( elementExpression ) ) {
|
||||
sqlAppender.append( "cast(array[" );
|
||||
elementExpression.accept( walker );
|
||||
sqlAppender.append( "] as " );
|
||||
sqlAppender.append( ArrayTypeHelper.getArrayTypeName( arrayExpression.getExpressionType(), walker ) );
|
||||
sqlAppender.append( ')' );
|
||||
}
|
||||
else {
|
||||
sqlAppender.append( "array[" );
|
||||
elementExpression.accept( walker );
|
||||
sqlAppender.append( ']' );
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean needsArrayCasting(Expression elementExpression) {
|
||||
// PostgreSQL doesn't do implicit conversion between text[] and varchar[], so we need casting
|
||||
return elementExpression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgumentListSignature() {
|
||||
return "(ARRAY array, OBJECT element)";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.lang.reflect.Array;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||
import org.hibernate.metamodel.model.domain.DomainType;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.sql.DdlType;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
public class ArrayTypeHelper {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static BasicType<?> resolveArrayType(DomainType<?> elementType, TypeConfiguration typeConfiguration) {
|
||||
@SuppressWarnings("unchecked") final BasicPluralJavaType<Object> arrayJavaType = (BasicPluralJavaType<Object>) typeConfiguration.getJavaTypeRegistry()
|
||||
.getDescriptor(
|
||||
Array.newInstance( elementType.getBindableJavaType(), 0 ).getClass()
|
||||
);
|
||||
final Dialect dialect = typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect();
|
||||
return arrayJavaType.resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
(BasicType<Object>) elementType,
|
||||
null,
|
||||
typeConfiguration.getCurrentBaseSqlTypeIndicators()
|
||||
);
|
||||
}
|
||||
|
||||
public static String getArrayTypeName(JdbcMappingContainer arrayType, SqlAstTranslator<?> walker) {
|
||||
if ( arrayType instanceof SqlTypedMapping ) {
|
||||
return AbstractSqlAstTranslator.getSqlTypeName( (SqlTypedMapping) arrayType, walker.getSessionFactory() );
|
||||
}
|
||||
else {
|
||||
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) arrayType.getSingleJdbcMapping();
|
||||
final TypeConfiguration typeConfiguration = walker.getSessionFactory().getTypeConfiguration();
|
||||
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
|
||||
final DdlType ddlType = ddlTypeRegistry.getDescriptor(
|
||||
pluralType.getJdbcType().getDdlTypeCode()
|
||||
);
|
||||
return ddlType.getCastTypeName( Size.nil(), pluralType, ddlTypeRegistry );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,11 +6,9 @@
|
|||
*/
|
||||
package org.hibernate.dialect.function.array;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||
import org.hibernate.metamodel.model.domain.DomainType;
|
||||
|
@ -18,8 +16,6 @@ import org.hibernate.query.ReturnableType;
|
|||
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
|
||||
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -54,7 +50,7 @@ public class ArrayViaElementArgumentReturnTypeResolver implements FunctionReturn
|
|||
for ( SqmTypedNode<?> argument : arguments ) {
|
||||
final DomainType<?> sqmType = argument.getExpressible().getSqmType();
|
||||
if ( sqmType instanceof ReturnableType<?> ) {
|
||||
return resolveArrayType( sqmType, typeConfiguration );
|
||||
return ArrayTypeHelper.resolveArrayType( sqmType, typeConfiguration );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -67,19 +63,4 @@ public class ArrayViaElementArgumentReturnTypeResolver implements FunctionReturn
|
|||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static BasicType<?> resolveArrayType(DomainType<?> elementType, TypeConfiguration typeConfiguration) {
|
||||
@SuppressWarnings("unchecked") final BasicPluralJavaType<Object> arrayJavaType = (BasicPluralJavaType<Object>) typeConfiguration.getJavaTypeRegistry()
|
||||
.getDescriptor(
|
||||
Array.newInstance( elementType.getBindableJavaType(), 0 ).getClass()
|
||||
);
|
||||
final Dialect dialect = typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect();
|
||||
return arrayJavaType.resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
(BasicType<Object>) elementType,
|
||||
null,
|
||||
typeConfiguration.getCurrentBaseSqlTypeIndicators()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
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;
|
||||
|
||||
public class OracleArrayContainsFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
public OracleArrayContainsFunction(TypeConfiguration typeConfiguration) {
|
||||
super(
|
||||
"array_contains",
|
||||
StandardArgumentsValidators.composite(
|
||||
StandardArgumentsValidators.exactly( 2 ),
|
||||
ArrayAndElementArgumentValidator.DEFAULT_INSTANCE
|
||||
),
|
||||
StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.standardBasicTypeForJavaType( Boolean.class ) ),
|
||||
ArrayAndElementArgumentTypeResolver.DEFAULT_INSTANCE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> sqlAstArguments,
|
||||
SqlAstTranslator<?> walker) {
|
||||
final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 );
|
||||
final String arrayTypeName = ArrayTypeHelper.getArrayTypeName( arrayExpression.getExpressionType(), walker );
|
||||
sqlAppender.appendSql( arrayTypeName );
|
||||
sqlAppender.append( "_contains(" );
|
||||
arrayExpression.accept( walker );
|
||||
sqlAppender.append( ',' );
|
||||
sqlAstArguments.get( 1 ).accept( walker );
|
||||
sqlAppender.append( ")=1" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgumentListSignature() {
|
||||
return "(ARRAY array, OBJECT element)";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
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;
|
||||
|
||||
public class OracleArrayContainsNullFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
public OracleArrayContainsNullFunction(TypeConfiguration typeConfiguration) {
|
||||
super(
|
||||
"array_contains_null",
|
||||
StandardArgumentsValidators.composite(
|
||||
StandardArgumentsValidators.exactly( 1 ),
|
||||
ArrayArgumentValidator.DEFAULT_INSTANCE
|
||||
),
|
||||
StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.standardBasicTypeForJavaType( Boolean.class ) ),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> sqlAstArguments,
|
||||
SqlAstTranslator<?> walker) {
|
||||
final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 );
|
||||
final String arrayTypeName = ArrayTypeHelper.getArrayTypeName( arrayExpression.getExpressionType(), walker );
|
||||
sqlAppender.appendSql( arrayTypeName );
|
||||
sqlAppender.append( "_contains(" );
|
||||
arrayExpression.accept( walker );
|
||||
sqlAppender.append( ",null)=1" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgumentListSignature() {
|
||||
return "(ARRAY array)";
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping;
|
||||
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
|
||||
/**
|
||||
* Models the type of a thing that can be used as an expression in a SQL query
|
||||
*
|
||||
|
@ -20,4 +22,12 @@ public interface SqlTypedMapping {
|
|||
return getJdbcMapping().getJdbcType().isLob();
|
||||
}
|
||||
JdbcMapping getJdbcMapping();
|
||||
|
||||
default Size toSize() {
|
||||
final Size size = new Size();
|
||||
size.setLength( getLength() );
|
||||
size.setPrecision( getPrecision() );
|
||||
size.setScale( getScale() );
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6266,14 +6266,14 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
appendSql( getSqlTypeName( castTarget, sessionFactory ) );
|
||||
}
|
||||
|
||||
public static String getSqlTypeName(CastTarget castTarget, SessionFactoryImplementor factory) {
|
||||
if ( castTarget.getSqlType() != null ) {
|
||||
return castTarget.getSqlType();
|
||||
public static String getSqlTypeName(SqlTypedMapping castTarget, SessionFactoryImplementor factory) {
|
||||
if ( castTarget.getColumnDefinition() != null ) {
|
||||
return castTarget.getColumnDefinition();
|
||||
}
|
||||
else {
|
||||
final Size castTargetSize = castTarget.toSize();
|
||||
final DdlTypeRegistry ddlTypeRegistry = factory.getTypeConfiguration().getDdlTypeRegistry();
|
||||
final SqlExpressible expressionType = (SqlExpressible) castTarget.getExpressionType();
|
||||
final SqlExpressible expressionType = (SqlExpressible) castTarget.getJdbcMapping();
|
||||
if ( expressionType instanceof BasicPluralType<?, ?> ) {
|
||||
final BasicPluralType<?, ?> containerType = (BasicPluralType<?, ?>) expressionType;
|
||||
final BasicPluralJavaType<?> javaTypeDescriptor = (BasicPluralJavaType<?>) containerType.getJavaTypeDescriptor();
|
||||
|
|
|
@ -9,13 +9,14 @@ package org.hibernate.sql.ast.tree.expression;
|
|||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class CastTarget implements Expression, SqlAstNode {
|
||||
public class CastTarget implements Expression, SqlAstNode, SqlTypedMapping {
|
||||
private final JdbcMapping type;
|
||||
private final String sqlType;
|
||||
private final Long length;
|
||||
|
@ -42,6 +43,16 @@ public class CastTarget implements Expression, SqlAstNode {
|
|||
return sqlType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnDefinition() {
|
||||
return sqlType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcMapping getJdbcMapping() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
@ -64,11 +75,4 @@ public class CastTarget implements Expression, SqlAstNode {
|
|||
sqlTreeWalker.visitCastTarget( this );
|
||||
}
|
||||
|
||||
public Size toSize() {
|
||||
final Size size = new Size();
|
||||
size.setLength( length );
|
||||
size.setPrecision( precision );
|
||||
size.setScale( scale );
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,8 +73,10 @@ public class ArrayConstructorTest {
|
|||
@Test
|
||||
public void testMultipleArguments(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-array-example[]
|
||||
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where e.theArray is not distinct from array('abc', null, 'def')", EntityWithArrays.class )
|
||||
.getResultList();
|
||||
//end::hql-array-example[]
|
||||
assertEquals( 1, results.size() );
|
||||
assertEquals( 2L, results.get( 0 ).getId() );
|
||||
} );
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.orm.test.function.array;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@DomainModel(annotatedClasses = EntityWithArrays.class)
|
||||
@SessionFactory
|
||||
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsStructuralArrays.class)
|
||||
// Make sure this stuff runs on a dedicated connection pool,
|
||||
// otherwise we might run into ORA-21700: object does not exist or is marked for delete
|
||||
// because the JDBC connection or database session caches something that should have been invalidated
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.CONNECTION_PROVIDER, value = ""))
|
||||
public class ArrayContainsTest {
|
||||
|
||||
@BeforeEach
|
||||
public void prepareData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( em -> {
|
||||
em.persist( new EntityWithArrays( 1L, new String[]{} ) );
|
||||
em.persist( new EntityWithArrays( 2L, new String[]{ "abc", null, "def" } ) );
|
||||
em.persist( new EntityWithArrays( 3L, null ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup(SessionFactoryScope scope) {
|
||||
scope.inTransaction( em -> {
|
||||
em.createMutationQuery( "delete from EntityWithArrays" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContains(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-array-contains-example[]
|
||||
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_contains(e.theArray, 'abc')", EntityWithArrays.class )
|
||||
.getResultList();
|
||||
//end::hql-array-contains-example[]
|
||||
assertEquals( 1, results.size() );
|
||||
assertEquals( 2L, results.get( 0 ).getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotContain(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_contains(e.theArray, 'xyz')", EntityWithArrays.class )
|
||||
.getResultList();
|
||||
assertEquals( 0, results.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(dialectClass = HSQLDialect.class, reason = "See https://sourceforge.net/p/hsqldb/bugs/1692/")
|
||||
public void testContainsNull(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-array-contains-null-example[]
|
||||
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where array_contains_null(e.theArray)", EntityWithArrays.class )
|
||||
.getResultList();
|
||||
//end::hql-array-contains-null-example[]
|
||||
assertEquals( 1, results.size() );
|
||||
assertEquals( 2L, results.get( 0 ).getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue