HHH-18604 Add json_insert and json_replace function
This commit is contained in:
parent
36066a03e6
commit
051bc78ae6
|
@ -2047,6 +2047,42 @@ include::{json-example-dir-hql}/JsonRemoveTest.java[tags=hql-json-remove-example
|
|||
|
||||
WARNING: SAP HANA, DB2, H2 and HSQLDB do not support this function.
|
||||
|
||||
[[hql-json-replace-function]]
|
||||
===== `json_replace()`
|
||||
|
||||
Replaces a value by JSON path within a JSON document.
|
||||
The function takes 3 arguments, the json document, the json path and the new value to set.
|
||||
A value will not be inserted if the key is missing, only the values of existing keys are replaced.
|
||||
|
||||
[[hql-json-replace-example]]
|
||||
====
|
||||
[source, java, indent=0]
|
||||
----
|
||||
include::{json-example-dir-hql}/JsonReplaceTest.java[tags=hql-json-replace-example]
|
||||
----
|
||||
====
|
||||
|
||||
WARNING: SAP HANA, DB2, H2 and HSQLDB do not support this function.
|
||||
|
||||
[[hql-json-insert-function]]
|
||||
===== `json_insert()`
|
||||
|
||||
Inserts a value by JSON path in a JSON document.
|
||||
The function takes 3 arguments, the json document, the json path and the value to insert.
|
||||
When the JSON document contains a value for a JSON path, no insertion happens,
|
||||
unless the value is an array, in which case the value will be appended to that array.
|
||||
If no value exists for a JSON path, the value will be inserted under the key as specified through the JSON path.
|
||||
|
||||
[[hql-json-insert-example]]
|
||||
====
|
||||
[source, java, indent=0]
|
||||
----
|
||||
include::{json-example-dir-hql}/JsonInsertTest.java[tags=hql-json-insert-example]
|
||||
----
|
||||
====
|
||||
|
||||
WARNING: SAP HANA, DB2, H2 and HSQLDB do not support this function.
|
||||
|
||||
[[hql-user-defined-functions]]
|
||||
==== Native and user-defined functions
|
||||
|
||||
|
|
|
@ -509,7 +509,9 @@ public class CockroachLegacyDialect extends Dialect {
|
|||
functionFactory.jsonArrayAgg_postgresql( false );
|
||||
functionFactory.jsonObjectAgg_postgresql( false );
|
||||
functionFactory.jsonSet_postgresql();
|
||||
functionFactory.jsonRemove_postgresql();
|
||||
functionFactory.jsonRemove_cockroachdb();
|
||||
functionFactory.jsonReplace_postgresql();
|
||||
functionFactory.jsonInsert_postgresql();
|
||||
|
||||
// Postgres uses # instead of ^ for XOR
|
||||
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
|
||||
|
|
|
@ -662,6 +662,8 @@ public class MySQLLegacyDialect extends Dialect {
|
|||
functionFactory.jsonObjectAgg_mysql();
|
||||
functionFactory.jsonSet_mysql();
|
||||
functionFactory.jsonRemove_mysql();
|
||||
functionFactory.jsonReplace_mysql();
|
||||
functionFactory.jsonInsert_mysql();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -319,6 +319,8 @@ public class OracleLegacyDialect extends Dialect {
|
|||
functionFactory.jsonObjectAgg_oracle();
|
||||
functionFactory.jsonSet_oracle();
|
||||
functionFactory.jsonRemove_oracle();
|
||||
functionFactory.jsonReplace_oracle();
|
||||
functionFactory.jsonInsert_oracle();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -660,6 +660,8 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
|||
}
|
||||
functionFactory.jsonSet_postgresql();
|
||||
functionFactory.jsonRemove_postgresql();
|
||||
functionFactory.jsonReplace_postgresql();
|
||||
functionFactory.jsonInsert_postgresql();
|
||||
|
||||
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
||||
functionFactory.makeDateTimeTimestamp();
|
||||
|
|
|
@ -408,6 +408,8 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
|||
functionFactory.jsonArray_sqlserver();
|
||||
functionFactory.jsonSet_sqlserver();
|
||||
functionFactory.jsonRemove_sqlserver();
|
||||
functionFactory.jsonReplace_sqlserver();
|
||||
functionFactory.jsonInsert_sqlserver();
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 14 ) ) {
|
||||
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
||||
|
|
|
@ -476,7 +476,9 @@ public class CockroachDialect extends Dialect {
|
|||
functionFactory.jsonArrayAgg_postgresql( false );
|
||||
functionFactory.jsonObjectAgg_postgresql( false );
|
||||
functionFactory.jsonSet_postgresql();
|
||||
functionFactory.jsonRemove_postgresql();
|
||||
functionFactory.jsonRemove_cockroachdb();
|
||||
functionFactory.jsonReplace_postgresql();
|
||||
functionFactory.jsonInsert_postgresql();
|
||||
|
||||
// Postgres uses # instead of ^ for XOR
|
||||
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
|
||||
|
|
|
@ -647,6 +647,8 @@ public class MySQLDialect extends Dialect {
|
|||
functionFactory.jsonObjectAgg_mysql();
|
||||
functionFactory.jsonSet_mysql();
|
||||
functionFactory.jsonRemove_mysql();
|
||||
functionFactory.jsonReplace_mysql();
|
||||
functionFactory.jsonInsert_mysql();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -410,6 +410,8 @@ public class OracleDialect extends Dialect {
|
|||
functionFactory.jsonObjectAgg_oracle();
|
||||
functionFactory.jsonSet_oracle();
|
||||
functionFactory.jsonRemove_oracle();
|
||||
functionFactory.jsonReplace_oracle();
|
||||
functionFactory.jsonInsert_oracle();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -621,6 +621,8 @@ public class PostgreSQLDialect extends Dialect {
|
|||
}
|
||||
functionFactory.jsonSet_postgresql();
|
||||
functionFactory.jsonRemove_postgresql();
|
||||
functionFactory.jsonReplace_postgresql();
|
||||
functionFactory.jsonInsert_postgresql();
|
||||
|
||||
functionFactory.makeDateTimeTimestamp();
|
||||
// Note that PostgreSQL doesn't support the OVER clause for ordered set-aggregate functions
|
||||
|
|
|
@ -426,6 +426,8 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
functionFactory.jsonArray_sqlserver();
|
||||
functionFactory.jsonSet_sqlserver();
|
||||
functionFactory.jsonRemove_sqlserver();
|
||||
functionFactory.jsonReplace_sqlserver();
|
||||
functionFactory.jsonInsert_sqlserver();
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 14 ) ) {
|
||||
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
||||
|
|
|
@ -78,6 +78,7 @@ import org.hibernate.dialect.function.array.PostgreSQLArrayPositionsFunction;
|
|||
import org.hibernate.dialect.function.array.PostgreSQLArrayTrimEmulation;
|
||||
import org.hibernate.dialect.function.json.CockroachDBJsonExistsFunction;
|
||||
import org.hibernate.dialect.function.json.CockroachDBJsonQueryFunction;
|
||||
import org.hibernate.dialect.function.json.CockroachDBJsonRemoveFunction;
|
||||
import org.hibernate.dialect.function.json.CockroachDBJsonValueFunction;
|
||||
import org.hibernate.dialect.function.json.DB2JsonArrayAggFunction;
|
||||
import org.hibernate.dialect.function.json.DB2JsonArrayFunction;
|
||||
|
@ -117,26 +118,32 @@ import org.hibernate.dialect.function.json.MySQLJsonQueryFunction;
|
|||
import org.hibernate.dialect.function.json.MySQLJsonValueFunction;
|
||||
import org.hibernate.dialect.function.json.OracleJsonArrayAggFunction;
|
||||
import org.hibernate.dialect.function.json.OracleJsonArrayFunction;
|
||||
import org.hibernate.dialect.function.json.OracleJsonInsertFunction;
|
||||
import org.hibernate.dialect.function.json.OracleJsonObjectAggFunction;
|
||||
import org.hibernate.dialect.function.json.OracleJsonObjectFunction;
|
||||
import org.hibernate.dialect.function.json.OracleJsonRemoveFunction;
|
||||
import org.hibernate.dialect.function.json.OracleJsonReplaceFunction;
|
||||
import org.hibernate.dialect.function.json.OracleJsonSetFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonArrayAggFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonArrayFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonExistsFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonInsertFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonObjectAggFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonObjectFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonQueryFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonRemoveFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonReplaceFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonSetFunction;
|
||||
import org.hibernate.dialect.function.json.PostgreSQLJsonValueFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonArrayAggFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonArrayFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonExistsFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonInsertFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonObjectAggFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonObjectFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonQueryFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonRemoveFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonReplaceFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonSetFunction;
|
||||
import org.hibernate.dialect.function.json.SQLServerJsonValueFunction;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||
|
@ -3858,6 +3865,13 @@ public class CommonFunctionFactory {
|
|||
functionRegistry.register( "json_remove", new PostgreSQLJsonRemoveFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* CockroachDB json_remove() function
|
||||
*/
|
||||
public void jsonRemove_cockroachdb() {
|
||||
functionRegistry.register( "json_remove", new CockroachDBJsonRemoveFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQL json_remove() function
|
||||
*/
|
||||
|
@ -3887,4 +3901,80 @@ public class CommonFunctionFactory {
|
|||
public void jsonRemove_sqlserver() {
|
||||
functionRegistry.register( "json_remove", new SQLServerJsonRemoveFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* PostgreSQL json_replace() function
|
||||
*/
|
||||
public void jsonReplace_postgresql() {
|
||||
functionRegistry.register( "json_replace", new PostgreSQLJsonReplaceFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQL json_replace() function
|
||||
*/
|
||||
public void jsonReplace_mysql() {
|
||||
functionRegistry.namedDescriptorBuilder( "json_replace" )
|
||||
.setArgumentsValidator( new ArgumentTypesValidator(
|
||||
StandardArgumentsValidators.exactly( 3 ),
|
||||
FunctionParameterType.IMPLICIT_JSON,
|
||||
FunctionParameterType.STRING,
|
||||
FunctionParameterType.ANY
|
||||
) )
|
||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.invariant(
|
||||
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON )
|
||||
) )
|
||||
.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle json_replace() function
|
||||
*/
|
||||
public void jsonReplace_oracle() {
|
||||
functionRegistry.register( "json_replace", new OracleJsonReplaceFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL server json_replace() function
|
||||
*/
|
||||
public void jsonReplace_sqlserver() {
|
||||
functionRegistry.register( "json_replace", new SQLServerJsonReplaceFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* PostgreSQL json_insert() function
|
||||
*/
|
||||
public void jsonInsert_postgresql() {
|
||||
functionRegistry.register( "json_insert", new PostgreSQLJsonInsertFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQL json_insert() function
|
||||
*/
|
||||
public void jsonInsert_mysql() {
|
||||
functionRegistry.namedDescriptorBuilder( "json_insert" )
|
||||
.setArgumentsValidator( new ArgumentTypesValidator(
|
||||
StandardArgumentsValidators.exactly( 3 ),
|
||||
FunctionParameterType.IMPLICIT_JSON,
|
||||
FunctionParameterType.STRING,
|
||||
FunctionParameterType.ANY
|
||||
) )
|
||||
.setReturnTypeResolver( StandardFunctionReturnTypeResolvers.invariant(
|
||||
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON )
|
||||
) )
|
||||
.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle json_insert() function
|
||||
*/
|
||||
public void jsonInsert_oracle() {
|
||||
functionRegistry.register( "json_insert", new OracleJsonInsertFunction( typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL server json_insert() function
|
||||
*/
|
||||
public void jsonInsert_sqlserver() {
|
||||
functionRegistry.register( "json_insert", new SQLServerJsonInsertFunction( typeConfiguration ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.json;
|
||||
|
||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.function.FunctionKind;
|
||||
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
|
||||
import org.hibernate.query.sqm.produce.function.FunctionParameterType;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Standard json_insert function.
|
||||
*/
|
||||
public abstract class AbstractJsonInsertFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
public AbstractJsonInsertFunction(TypeConfiguration typeConfiguration) {
|
||||
super(
|
||||
"json_insert",
|
||||
FunctionKind.NORMAL,
|
||||
new ArgumentTypesValidator(
|
||||
StandardArgumentsValidators.exactly( 3 ),
|
||||
FunctionParameterType.IMPLICIT_JSON,
|
||||
FunctionParameterType.STRING,
|
||||
FunctionParameterType.ANY
|
||||
),
|
||||
StandardFunctionReturnTypeResolvers.invariant(
|
||||
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON )
|
||||
),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.json;
|
||||
|
||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.function.FunctionKind;
|
||||
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
|
||||
import org.hibernate.query.sqm.produce.function.FunctionParameterType;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Standard json_replace function.
|
||||
*/
|
||||
public abstract class AbstractJsonReplaceFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
public AbstractJsonReplaceFunction(TypeConfiguration typeConfiguration) {
|
||||
super(
|
||||
"json_replace",
|
||||
FunctionKind.NORMAL,
|
||||
new ArgumentTypesValidator(
|
||||
StandardArgumentsValidators.exactly( 3 ),
|
||||
FunctionParameterType.IMPLICIT_JSON,
|
||||
FunctionParameterType.STRING,
|
||||
FunctionParameterType.ANY
|
||||
),
|
||||
StandardFunctionReturnTypeResolvers.invariant(
|
||||
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON )
|
||||
),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.json;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
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.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* CockroachDB json_remove function.
|
||||
*/
|
||||
public class CockroachDBJsonRemoveFunction extends AbstractJsonRemoveFunction {
|
||||
|
||||
public CockroachDBJsonRemoveFunction(TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
sqlAppender.appendSql( "json_remove_path(" );
|
||||
final boolean needsCast = !isJsonType( json ) && json instanceof JdbcParameter;
|
||||
if ( needsCast ) {
|
||||
sqlAppender.appendSql( "cast(" );
|
||||
}
|
||||
json.accept( translator );
|
||||
if ( needsCast ) {
|
||||
sqlAppender.appendSql( " as jsonb)" );
|
||||
}
|
||||
sqlAppender.appendSql( ',' );
|
||||
List<JsonPathHelper.JsonPathElement> jsonPathElements =
|
||||
JsonPathHelper.parseJsonPathElements( translator.getLiteralValue( jsonPath ) );
|
||||
sqlAppender.appendSql( "array" );
|
||||
char separator = '[';
|
||||
for ( JsonPathHelper.JsonPathElement pathElement : jsonPathElements ) {
|
||||
sqlAppender.appendSql( separator );
|
||||
if ( pathElement instanceof JsonPathHelper.JsonAttribute attribute ) {
|
||||
sqlAppender.appendSingleQuoteEscapedString( attribute.attribute() );
|
||||
}
|
||||
else if ( pathElement instanceof JsonPathHelper.JsonParameterIndexAccess ) {
|
||||
final String parameterName = ( (JsonPathHelper.JsonParameterIndexAccess) pathElement ).parameterName();
|
||||
throw new QueryException( "JSON path [" + jsonPath + "] uses parameter [" + parameterName + "] that is not passed" );
|
||||
}
|
||||
else {
|
||||
sqlAppender.appendSql( '\'' );
|
||||
sqlAppender.appendSql( ( (JsonPathHelper.JsonIndexAccess) pathElement ).index() + 1 );
|
||||
sqlAppender.appendSql( '\'' );
|
||||
}
|
||||
separator = ',';
|
||||
}
|
||||
sqlAppender.appendSql( "]::text[])" );
|
||||
}
|
||||
|
||||
private boolean isJsonType(Expression expression) {
|
||||
final JdbcMappingContainer expressionType = expression.getExpressionType();
|
||||
return expressionType != null && expressionType.getSingleJdbcMapping().getJdbcType().isJson();
|
||||
}
|
||||
}
|
|
@ -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.json;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Oracle json_insert function.
|
||||
*/
|
||||
public class OracleJsonInsertFunction extends AbstractJsonInsertFunction {
|
||||
|
||||
public OracleJsonInsertFunction(TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
final SqlAstNode value = arguments.get( 2 );
|
||||
sqlAppender.appendSql( "json_transform(" );
|
||||
json.accept( translator );
|
||||
sqlAppender.appendSql( ",insert " );
|
||||
jsonPath.accept( translator );
|
||||
sqlAppender.appendSql( '=' );
|
||||
value.accept( translator );
|
||||
sqlAppender.appendSql( " ignore on existing)" );
|
||||
}
|
||||
}
|
|
@ -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.json;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Oracle json_replace function.
|
||||
*/
|
||||
public class OracleJsonReplaceFunction extends AbstractJsonReplaceFunction {
|
||||
|
||||
public OracleJsonReplaceFunction(TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
final SqlAstNode value = arguments.get( 2 );
|
||||
sqlAppender.appendSql( "json_transform(" );
|
||||
json.accept( translator );
|
||||
sqlAppender.appendSql( ",replace " );
|
||||
jsonPath.accept( translator );
|
||||
sqlAppender.appendSql( '=' );
|
||||
value.accept( translator );
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
}
|
|
@ -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.dialect.function.json;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
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.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* PostgreSQL json_insert function.
|
||||
*/
|
||||
public class PostgreSQLJsonInsertFunction extends AbstractJsonInsertFunction {
|
||||
|
||||
public PostgreSQLJsonInsertFunction(TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
final SqlAstNode value = arguments.get( 2 );
|
||||
sqlAppender.appendSql( "(select case when t.d#>>t.p is not null then t.d else jsonb_insert(t.d,t.p," );
|
||||
if ( value instanceof Literal && ( (Literal) value ).getLiteralValue() == null ) {
|
||||
sqlAppender.appendSql( "null::jsonb" );
|
||||
}
|
||||
else {
|
||||
sqlAppender.appendSql( "to_jsonb(" );
|
||||
value.accept( translator );
|
||||
if ( value instanceof Literal literal && literal.getJdbcMapping().getJdbcType().isString() ) {
|
||||
// PostgreSQL until version 16 is not smart enough to infer the type of a string literal
|
||||
sqlAppender.appendSql( "::text" );
|
||||
}
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
sqlAppender.appendSql( ",true) end from (values(" );
|
||||
final boolean needsCast = !isJsonType( json );
|
||||
if ( needsCast ) {
|
||||
sqlAppender.appendSql( "cast(" );
|
||||
}
|
||||
json.accept( translator );
|
||||
if ( needsCast ) {
|
||||
sqlAppender.appendSql( " as jsonb)" );
|
||||
}
|
||||
sqlAppender.appendSql( ',' );
|
||||
List<JsonPathHelper.JsonPathElement> jsonPathElements =
|
||||
JsonPathHelper.parseJsonPathElements( translator.getLiteralValue( jsonPath ) );
|
||||
sqlAppender.appendSql( "array" );
|
||||
char separator = '[';
|
||||
for ( JsonPathHelper.JsonPathElement pathElement : jsonPathElements ) {
|
||||
sqlAppender.appendSql( separator );
|
||||
if ( pathElement instanceof JsonPathHelper.JsonAttribute attribute ) {
|
||||
sqlAppender.appendSingleQuoteEscapedString( attribute.attribute() );
|
||||
}
|
||||
else if ( pathElement instanceof JsonPathHelper.JsonParameterIndexAccess ) {
|
||||
final String parameterName = ( (JsonPathHelper.JsonParameterIndexAccess) pathElement ).parameterName();
|
||||
throw new QueryException( "JSON path [" + jsonPath + "] uses parameter [" + parameterName + "] that is not passed" );
|
||||
}
|
||||
else {
|
||||
sqlAppender.appendSql( '\'' );
|
||||
sqlAppender.appendSql( ( (JsonPathHelper.JsonIndexAccess) pathElement ).index() );
|
||||
sqlAppender.appendSql( '\'' );
|
||||
}
|
||||
separator = ',';
|
||||
}
|
||||
sqlAppender.appendSql( "]::text[]" );
|
||||
sqlAppender.appendSql( ")) t(d,p))" );
|
||||
}
|
||||
|
||||
private static boolean isJsonType(Expression expression) {
|
||||
final JdbcMappingContainer expressionType = expression.getExpressionType();
|
||||
return expressionType != null && expressionType.getSingleJdbcMapping().getJdbcType().isJson();
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ import org.hibernate.sql.ast.tree.expression.Literal;
|
|||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* PostgreSQL json_set function.
|
||||
* PostgreSQL json_remove function.
|
||||
*/
|
||||
public class PostgreSQLJsonRemoveFunction extends AbstractJsonRemoveFunction {
|
||||
|
||||
|
|
|
@ -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.dialect.function.json;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
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.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* PostgreSQL json_replace function.
|
||||
*/
|
||||
public class PostgreSQLJsonReplaceFunction extends AbstractJsonReplaceFunction {
|
||||
|
||||
public PostgreSQLJsonReplaceFunction(TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
final SqlAstNode value = arguments.get( 2 );
|
||||
sqlAppender.appendSql( "jsonb_set(" );
|
||||
final boolean needsCast = !isJsonType( json ) && json instanceof JdbcParameter;
|
||||
if ( needsCast ) {
|
||||
sqlAppender.appendSql( "cast(" );
|
||||
}
|
||||
json.accept( translator );
|
||||
if ( needsCast ) {
|
||||
sqlAppender.appendSql( " as jsonb)" );
|
||||
}
|
||||
sqlAppender.appendSql( ',' );
|
||||
List<JsonPathHelper.JsonPathElement> jsonPathElements =
|
||||
JsonPathHelper.parseJsonPathElements( translator.getLiteralValue( jsonPath ) );
|
||||
sqlAppender.appendSql( "array" );
|
||||
char separator = '[';
|
||||
for ( JsonPathHelper.JsonPathElement pathElement : jsonPathElements ) {
|
||||
sqlAppender.appendSql( separator );
|
||||
if ( pathElement instanceof JsonPathHelper.JsonAttribute attribute ) {
|
||||
sqlAppender.appendSingleQuoteEscapedString( attribute.attribute() );
|
||||
}
|
||||
else if ( pathElement instanceof JsonPathHelper.JsonParameterIndexAccess ) {
|
||||
final String parameterName = ( (JsonPathHelper.JsonParameterIndexAccess) pathElement ).parameterName();
|
||||
throw new QueryException( "JSON path [" + jsonPath + "] uses parameter [" + parameterName + "] that is not passed" );
|
||||
}
|
||||
else {
|
||||
sqlAppender.appendSql( '\'' );
|
||||
sqlAppender.appendSql( ( (JsonPathHelper.JsonIndexAccess) pathElement ).index() );
|
||||
sqlAppender.appendSql( '\'' );
|
||||
}
|
||||
separator = ',';
|
||||
}
|
||||
sqlAppender.appendSql( "]::text[]," );
|
||||
if ( value instanceof Literal && ( (Literal) value ).getLiteralValue() == null ) {
|
||||
sqlAppender.appendSql( "null::jsonb" );
|
||||
}
|
||||
else {
|
||||
sqlAppender.appendSql( "to_jsonb(" );
|
||||
value.accept( translator );
|
||||
if ( value instanceof Literal literal && literal.getJdbcMapping().getJdbcType().isString() ) {
|
||||
// PostgreSQL until version 16 is not smart enough to infer the type of a string literal
|
||||
sqlAppender.appendSql( "::text" );
|
||||
}
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
sqlAppender.appendSql( ",false)" );
|
||||
}
|
||||
|
||||
private boolean isJsonType(Expression expression) {
|
||||
final JdbcMappingContainer expressionType = expression.getExpressionType();
|
||||
return expressionType != null && expressionType.getSingleJdbcMapping().getJdbcType().isJson();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.json;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* SQL Server json_insert function.
|
||||
*/
|
||||
public class SQLServerJsonInsertFunction extends AbstractJsonInsertFunction {
|
||||
|
||||
public SQLServerJsonInsertFunction(TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
sqlAppender.appendSql( "(select case when coalesce(json_query(t.d,t.p),json_value(t.d,t.p)) is not null then t.d else json_modify(t.d,t.p," );
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
final SqlAstNode value = arguments.get( 2 );
|
||||
renderValue( sqlAppender, value, translator );
|
||||
sqlAppender.appendSql( ") end from (values(");
|
||||
json.accept( translator );
|
||||
sqlAppender.appendSql( ',' );
|
||||
jsonPath.accept( translator );
|
||||
sqlAppender.appendSql( ")) t(d,p))" );
|
||||
}
|
||||
|
||||
protected void renderValue(SqlAppender sqlAppender, SqlAstNode value, SqlAstTranslator<?> translator) {
|
||||
if ( ExpressionTypeHelper.isBoolean( value ) ) {
|
||||
sqlAppender.appendSql( "cast(" );
|
||||
value.accept( translator );
|
||||
sqlAppender.appendSql( " as bit)" );
|
||||
}
|
||||
else {
|
||||
value.accept( translator );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.json;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* SQL Server json_replace function.
|
||||
*/
|
||||
public class SQLServerJsonReplaceFunction extends AbstractJsonReplaceFunction {
|
||||
|
||||
public SQLServerJsonReplaceFunction(TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
sqlAppender.appendSql( "(select case when coalesce(json_query(t.d,t.p),json_value(t.d,t.p)) is null then t.d else json_modify(t.d,t.p," );
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
final SqlAstNode value = arguments.get( 2 );
|
||||
renderValue( sqlAppender, value, translator );
|
||||
sqlAppender.appendSql( ") end from (values(");
|
||||
json.accept( translator );
|
||||
sqlAppender.appendSql( ',' );
|
||||
jsonPath.accept( translator );
|
||||
sqlAppender.appendSql( ")) t(d,p))" );
|
||||
}
|
||||
|
||||
protected void renderValue(SqlAppender sqlAppender, SqlAstNode value, SqlAstTranslator<?> translator) {
|
||||
if ( ExpressionTypeHelper.isBoolean( value ) ) {
|
||||
sqlAppender.appendSql( "cast(" );
|
||||
value.accept( translator );
|
||||
sqlAppender.appendSql( " as bit)" );
|
||||
}
|
||||
else {
|
||||
value.accept( translator );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3959,6 +3959,70 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
|||
@Incubating
|
||||
JpaExpression<String> jsonRemove(Expression<?> jsonDocument, Expression<String> jsonPath);
|
||||
|
||||
/**
|
||||
* Inserts a value by JSON path within a JSON document.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
JpaExpression<String> jsonInsert(Expression<?> jsonDocument, String jsonPath, Expression<?> value);
|
||||
|
||||
/**
|
||||
* Inserts a value by JSON path within a JSON document.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
JpaExpression<String> jsonInsert(Expression<?> jsonDocument, Expression<String> jsonPath, Expression<?> value);
|
||||
|
||||
/**
|
||||
* Inserts a value by JSON path within a JSON document.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
JpaExpression<String> jsonInsert(Expression<?> jsonDocument, String jsonPath, Object value);
|
||||
|
||||
/**
|
||||
* Inserts a value by JSON path within a JSON document.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
JpaExpression<String> jsonInsert(Expression<?> jsonDocument, Expression<String> jsonPath, Object value);
|
||||
|
||||
/**
|
||||
* Replaces a value by JSON path within a JSON document.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
JpaExpression<String> jsonReplace(Expression<?> jsonDocument, String jsonPath, Expression<?> value);
|
||||
|
||||
/**
|
||||
* Replaces a value by JSON path within a JSON document.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
JpaExpression<String> jsonReplace(Expression<?> jsonDocument, Expression<String> jsonPath, Expression<?> value);
|
||||
|
||||
/**
|
||||
* Replaces a value by JSON path within a JSON document.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
JpaExpression<String> jsonReplace(Expression<?> jsonDocument, String jsonPath, Object value);
|
||||
|
||||
/**
|
||||
* Replaces a value by JSON path within a JSON document.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
JpaExpression<String> jsonReplace(Expression<?> jsonDocument, Expression<String> jsonPath, Object value);
|
||||
|
||||
@Override
|
||||
JpaPredicate and(List<Predicate> restrictions);
|
||||
|
||||
|
|
|
@ -3559,4 +3559,58 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
|
|||
public JpaExpression<String> jsonRemove(Expression<?> jsonDocument, Expression<String> jsonPath) {
|
||||
return criteriaBuilder.jsonRemove( jsonDocument, jsonPath );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Incubating
|
||||
public JpaExpression<String> jsonInsert(Expression<?> jsonDocument, String jsonPath, Expression<?> value) {
|
||||
return criteriaBuilder.jsonInsert( jsonDocument, jsonPath, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Incubating
|
||||
public JpaExpression<String> jsonInsert(
|
||||
Expression<?> jsonDocument,
|
||||
Expression<String> jsonPath,
|
||||
Expression<?> value) {
|
||||
return criteriaBuilder.jsonInsert( jsonDocument, jsonPath, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Incubating
|
||||
public JpaExpression<String> jsonInsert(Expression<?> jsonDocument, String jsonPath, Object value) {
|
||||
return criteriaBuilder.jsonInsert( jsonDocument, jsonPath, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Incubating
|
||||
public JpaExpression<String> jsonInsert(Expression<?> jsonDocument, Expression<String> jsonPath, Object value) {
|
||||
return criteriaBuilder.jsonInsert( jsonDocument, jsonPath, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Incubating
|
||||
public JpaExpression<String> jsonReplace(Expression<?> jsonDocument, String jsonPath, Expression<?> value) {
|
||||
return criteriaBuilder.jsonReplace( jsonDocument, jsonPath, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Incubating
|
||||
public JpaExpression<String> jsonReplace(
|
||||
Expression<?> jsonDocument,
|
||||
Expression<String> jsonPath,
|
||||
Expression<?> value) {
|
||||
return criteriaBuilder.jsonReplace( jsonDocument, jsonPath, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Incubating
|
||||
public JpaExpression<String> jsonReplace(Expression<?> jsonDocument, String jsonPath, Object value) {
|
||||
return criteriaBuilder.jsonReplace( jsonDocument, jsonPath, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Incubating
|
||||
public JpaExpression<String> jsonReplace(Expression<?> jsonDocument, Expression<String> jsonPath, Object value) {
|
||||
return criteriaBuilder.jsonReplace( jsonDocument, jsonPath, value );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -721,6 +721,30 @@ public interface NodeBuilder extends HibernateCriteriaBuilder, BindingContext {
|
|||
@Override
|
||||
SqmExpression<String> jsonRemove(Expression<?> jsonDocument, Expression<String> jsonPath);
|
||||
|
||||
@Override
|
||||
SqmExpression<String> jsonInsert(Expression<?> jsonDocument, Expression<String> jsonPath, Object value);
|
||||
|
||||
@Override
|
||||
SqmExpression<String> jsonInsert(Expression<?> jsonDocument, String jsonPath, Object value);
|
||||
|
||||
@Override
|
||||
SqmExpression<String> jsonInsert(Expression<?> jsonDocument, Expression<String> jsonPath, Expression<?> value);
|
||||
|
||||
@Override
|
||||
SqmExpression<String> jsonInsert(Expression<?> jsonDocument, String jsonPath, Expression<?> value);
|
||||
|
||||
@Override
|
||||
SqmExpression<String> jsonReplace(Expression<?> jsonDocument, Expression<String> jsonPath, Object value);
|
||||
|
||||
@Override
|
||||
SqmExpression<String> jsonReplace(Expression<?> jsonDocument, String jsonPath, Object value);
|
||||
|
||||
@Override
|
||||
SqmExpression<String> jsonReplace(Expression<?> jsonDocument, Expression<String> jsonPath, Expression<?> value);
|
||||
|
||||
@Override
|
||||
SqmExpression<String> jsonReplace(Expression<?> jsonDocument, String jsonPath, Expression<?> value);
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Covariant overrides
|
||||
|
||||
|
|
|
@ -5595,4 +5595,60 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, Serializable {
|
|||
queryEngine
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<String> jsonInsert(Expression<?> jsonDocument, Expression<String> jsonPath, Object value) {
|
||||
return jsonInsert( jsonDocument, jsonPath, value( value ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<String> jsonInsert(Expression<?> jsonDocument, String jsonPath, Object value) {
|
||||
return jsonInsert( jsonDocument, value( jsonPath ), value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<String> jsonInsert(Expression<?> jsonDocument, String jsonPath, Expression<?> value) {
|
||||
return jsonInsert( jsonDocument, value( jsonPath ), value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<String> jsonInsert(
|
||||
Expression<?> jsonDocument,
|
||||
Expression<String> jsonPath,
|
||||
Expression<?> value) {
|
||||
//noinspection unchecked
|
||||
return getFunctionDescriptor( "json_insert" ).generateSqmExpression(
|
||||
(List<? extends SqmTypedNode<?>>) (List<?>) asList( jsonDocument, jsonPath, value ),
|
||||
null,
|
||||
queryEngine
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<String> jsonReplace(Expression<?> jsonDocument, Expression<String> jsonPath, Object value) {
|
||||
return jsonReplace( jsonDocument, jsonPath, value( value ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<String> jsonReplace(Expression<?> jsonDocument, String jsonPath, Object value) {
|
||||
return jsonReplace( jsonDocument, value( jsonPath ), value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<String> jsonReplace(Expression<?> jsonDocument, String jsonPath, Expression<?> value) {
|
||||
return jsonReplace( jsonDocument, value( jsonPath ), value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<String> jsonReplace(
|
||||
Expression<?> jsonDocument,
|
||||
Expression<String> jsonPath,
|
||||
Expression<?> value) {
|
||||
//noinspection unchecked
|
||||
return getFunctionDescriptor( "json_replace" ).generateSqmExpression(
|
||||
(List<? extends SqmTypedNode<?>>) (List<?>) asList( jsonDocument, jsonPath, value ),
|
||||
null,
|
||||
queryEngine
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.orm.test.function.json;
|
||||
|
||||
import org.hibernate.cfg.QuerySettings;
|
||||
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
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.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@DomainModel(standardModels = StandardDomainModel.GAMBIT)
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settings = @Setting(name = QuerySettings.JSON_FUNCTIONS_ENABLED, value = "true"))
|
||||
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsJsonInsert.class)
|
||||
public class JsonInsertTest {
|
||||
|
||||
@Test
|
||||
public void testSimple(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-json-insert-example[]
|
||||
em.createQuery( "select json_insert('{\"a\":1}', '$.b', 2)" ).getResultList();
|
||||
//end::hql-json-insert-example[]
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
|
@ -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.orm.test.function.json;
|
||||
|
||||
import org.hibernate.cfg.QuerySettings;
|
||||
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
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.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@DomainModel(standardModels = StandardDomainModel.GAMBIT)
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settings = @Setting(name = QuerySettings.JSON_FUNCTIONS_ENABLED, value = "true"))
|
||||
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsJsonReplace.class)
|
||||
public class JsonReplaceTest {
|
||||
|
||||
@Test
|
||||
public void testSimple(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-json-replace-example[]
|
||||
em.createQuery( "select json_replace('{\"a\":1}', '$.a', 2)" ).getResultList();
|
||||
//end::hql-json-replace-example[]
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
|
@ -544,6 +544,69 @@ public class JsonFunctionTests {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonReplace.class)
|
||||
public void testJsonReplaceNonExisting(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
String json = session.createQuery(
|
||||
"select json_replace('{}', '$.a', 123)",
|
||||
String.class
|
||||
).getSingleResult();
|
||||
Map<String, Object> object = parseObject( json );
|
||||
assertEquals( 0, object.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonReplace.class)
|
||||
public void testJsonReplace(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
String json = session.createQuery(
|
||||
"select json_replace('{\"a\":456}', '$.a', 123)",
|
||||
String.class
|
||||
).getSingleResult();
|
||||
Map<String, Object> object = parseObject( json );
|
||||
assertEquals( 1, object.size() );
|
||||
assertEquals( 123, object.get( "a" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonInsert.class)
|
||||
public void testJsonInsert(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
String json = session.createQuery(
|
||||
"select json_insert('{}', '$.a', 123)",
|
||||
String.class
|
||||
).getSingleResult();
|
||||
Map<String, Object> object = parseObject( json );
|
||||
assertEquals( 1, object.size() );
|
||||
assertEquals( 123, object.get( "a" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonInsert.class)
|
||||
public void testJsonInsertWithExisting(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
String json = session.createQuery(
|
||||
"select json_insert('{\"a\":456}', '$.a', 123)",
|
||||
String.class
|
||||
).getSingleResult();
|
||||
Map<String, Object> object = parseObject( json );
|
||||
assertEquals( 1, object.size() );
|
||||
assertEquals( 456, object.get( "a" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
private static Map<String, Object> parseObject(String json) {
|
||||
|
|
|
@ -805,6 +805,18 @@ abstract public class DialectFeatureChecks {
|
|||
}
|
||||
}
|
||||
|
||||
public static class SupportsJsonReplace implements DialectFeatureCheck {
|
||||
public boolean apply(Dialect dialect) {
|
||||
return definesFunction( dialect, "json_replace" );
|
||||
}
|
||||
}
|
||||
|
||||
public static class SupportsJsonInsert implements DialectFeatureCheck {
|
||||
public boolean apply(Dialect dialect) {
|
||||
return definesFunction( dialect, "json_insert" );
|
||||
}
|
||||
}
|
||||
|
||||
public static class IsJtds implements DialectFeatureCheck {
|
||||
public boolean apply(Dialect dialect) {
|
||||
return dialect instanceof SybaseDialect && ( (SybaseDialect) dialect ).getDriverKind() == SybaseDriverKind.JTDS;
|
||||
|
|
Loading…
Reference in New Issue