From 49ceab1b6003c08ef78c26f7a79ec6210af4696f Mon Sep 17 00:00:00 2001 From: gorkemduman Date: Fri, 26 May 2023 02:41:23 +0300 Subject: [PATCH 1/2] Upgrade QueryDsl 5.0.0-jakarta Hibernate 6.1.7.Final --- pom.xml | 150 +++++++++++++++++- .../querydsl/jpa/hibernate/JsonPath.java | 15 +- .../jpa/hibernate/PostgreSQLJsonDialect.java | 89 ++++------- .../functions/AbstractJsonSQLFunction.java | 136 +++++++++++----- .../functions/AbstractTypedJsonFunction.java | 54 +++---- .../jpa/hibernate/functions/JsonFunction.java | 32 +--- .../functions/types/BoolJsonSQLFunction.java | 3 +- .../types/DoubleJsonSQLFunction.java | 3 +- .../functions/types/FloatJsonSQLFunction.java | 3 +- .../functions/types/IntJsonSQLFunction.java | 3 +- .../types/JsonContainsSQLFunction.java | 28 ++-- .../functions/types/LongJsonSQLFunction.java | 3 +- .../functions/types/ShortJsonSQLFunction.java | 3 +- .../functions/types/TextJsonSQLFunction.java | 3 +- .../querydsl/jpa/hibernate/EnumTest.java | 7 + .../jpa/hibernate/IntegrationTest.java | 148 +++++++++++++++++ .../jpa/hibernate/TestApplication.java | 25 +++ .../jpa/hibernate/dto/ChildSampleData.java | 20 +++ .../jpa/hibernate/dto/SampleData.java | 16 ++ .../jpa/hibernate/entity/TestEntity.java | 44 +++++ .../hibernate/initializer/Initializer.java | 25 +++ .../hibernate/repository/TestRepository.java | 12 ++ src/test/resources/application.properties | 18 +++ 23 files changed, 644 insertions(+), 196 deletions(-) create mode 100644 src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/EnumTest.java create mode 100644 src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/IntegrationTest.java create mode 100644 src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/TestApplication.java create mode 100644 src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/dto/ChildSampleData.java create mode 100644 src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/dto/SampleData.java create mode 100644 src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/entity/TestEntity.java create mode 100644 src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/initializer/Initializer.java create mode 100644 src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/repository/TestRepository.java create mode 100644 src/test/resources/application.properties diff --git a/pom.xml b/pom.xml index f87c1f5..ce9219d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,21 +4,23 @@ com.github.alexliesenfeld querydsl-jpa-postgres-json - 0.0.7-SNAPSHOT + 0.0.8-SNAPSHOT ${project.groupId}:${project.artifactId} Querydsl extension for using PostgreSQL JSON types with JPA https://github.com/alexliesenfeld/querydsl-jpa-postgres-json - 1.8 + 17 UTF-8 UTF-8 - 4.2.1 - 5.2.10.Final - 1.18.8 - 2.10.0 + 5.0.0 + + 6.1.7.Final + 1.18.26 + 2.14.2 + 3.3.2 @@ -34,14 +36,19 @@ com.querydsl querydsl-jpa ${querydsl.version} + jakarta provided org.hibernate - hibernate-entitymanager + hibernate-core ${hibernate.version} - provided + + + io.hypersistence + hypersistence-utils-hibernate-60 + ${hypersistence.version} @@ -51,11 +58,90 @@ true + + + + + + + + org.springframework.boot + spring-boot-starter-test + 3.0.6 + test + + + org.springframework.boot + spring-boot-starter-data-jpa + 3.0.6 + test + + + + org.springframework.data + spring-data-jpa + 3.0.6 + test + + + org.springframework.boot + spring-boot-starter-web + 3.0.6 + test + + + org.testcontainers + postgresql + 1.17.3 + test + + + + org.postgresql + postgresql + 42.3.8 + test + + + + + + + + + + + + + + + + + + + + + + + + com.querydsl + querydsl-apt + ${querydsl.version} + jakarta + test + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.0 + maven-compiler-plugin 3.8.1 @@ -130,6 +216,54 @@ + + + com.mysema.maven + apt-maven-plugin + 1.1.3 + + + + + + + + + + + + + + generate-test-entities + generate-test-sources + + process + + + target/generated-test-sources/java + com.querydsl.apt.jpa.JPAAnnotationProcessor + + + + + + org.hibernate.orm.tooling + hibernate-enhance-maven-plugin + ${hibernate.version} + + + enhance + + enhance + + + true + true + true + + + + diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/JsonPath.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/JsonPath.java index adaea8b..120012d 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/JsonPath.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/JsonPath.java @@ -2,19 +2,18 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Preconditions; import com.querydsl.core.types.Path; import com.querydsl.core.types.PathMetadata; import com.querydsl.core.types.PathMetadataFactory; import com.querydsl.core.types.Visitor; import com.querydsl.core.types.dsl.*; +import lombok.Getter; +import org.hibernate.AssertionFailure; +import org.hibernate.annotations.Type; import java.lang.reflect.AnnotatedElement; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nullable; -import lombok.Getter; -import org.hibernate.annotations.Type; /** * @author wener @@ -50,7 +49,7 @@ public static boolean isJsonb(Path path) { } Type type = path.getAnnotatedElement().getAnnotation(Type.class); if (type != null) { - return type.type().contains("jsonb"); + return type.value().getName().contains("JsonBinaryType"); } return false; } @@ -82,9 +81,8 @@ public AnnotatedElement getAnnotatedElement() { return parent.getAnnotatedElement(); } - @Nullable @Override - public R accept(Visitor v, @Nullable C context) { + public R accept(Visitor v, C context) { // NOTE HQL does not support nested functions, so hql_json_path(hql_json_path(?,?),?) will fail throw new AssertionError("This should not happen"); } @@ -247,6 +245,7 @@ private String toJsonString(Object value){ } } protected void checkJsonb() { - Preconditions.checkArgument(isJsonb(), "This function required jsonb type"); + if(!isJsonb()) + throw new AssertionFailure("This function required jsonb type"); } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/PostgreSQLJsonDialect.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/PostgreSQLJsonDialect.java index 505df68..e6c0259 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/PostgreSQLJsonDialect.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/PostgreSQLJsonDialect.java @@ -1,86 +1,57 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate; -import java.sql.Types; -import java.util.function.BiConsumer; - - -import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.JsonFunction; - import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types.*; -import org.hibernate.dialect.PostgreSQL95Dialect; -import org.hibernate.dialect.function.SQLFunction; -import org.hibernate.type.IntegerType; -import org.hibernate.type.StringType; -import org.hibernate.type.Type; +import io.hypersistence.utils.hibernate.type.json.internal.JsonBinaryJdbcTypeDescriptor; +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.dialect.PostgresPlusDialect; +import org.hibernate.query.spi.QueryEngine; +import org.hibernate.service.ServiceRegistry; + +import java.sql.Types; /** * @author wener * @author Alexander Liesenfeld * @see functions-json */ -public class PostgreSQLJsonDialect extends PostgreSQL95Dialect { - private final BiConsumer registerColumnTypeConsumer; - private final BiConsumer registerFunctionConsumer; +public class PostgreSQLJsonDialect extends PostgresPlusDialect { /** * You can either directly use it or derive your custom dialect by using this constructor. */ public PostgreSQLJsonDialect() { super(); - - this.registerColumnTypeConsumer = this::registerColumnType; - this.registerFunctionConsumer = this::registerFunction; - - register(); } - /** - * Use this constructor if you cannot use or derive from this dialect class. You can create a new instance by - * using this constructor and register the provided functionality by calling the - * {@link PostgreSQLJsonDialect#register()} function. This will still allow you to use the functionality provided - * by this dialect but you do not need to use or derive from this dialect class. - * - * @param registerColumnTypeConsumer A reference to the {@link PostgreSQL95Dialect#registerColumnType(int, String)} - * function this class should be using. If you do not have any custom functions - * for this, just pass this::registerColumnType, where - * this refers to your dialect class calling this constructor. - * - * @param registerFunctionConsumer A reference to the - * {@link PostgreSQL95Dialect#registerFunction(String, SQLFunction)} (int, String)} - * function this class should be using. If you do not have any custom functions for - * this, just pass this::registerFunction, where this - * refers to your dialect class calling this constructor. - */ - public PostgreSQLJsonDialect(BiConsumer registerColumnTypeConsumer, BiConsumer registerFunctionConsumer) { - super(); - this.registerColumnTypeConsumer = registerColumnTypeConsumer; - this.registerFunctionConsumer = registerFunctionConsumer; + @Override + public void initializeFunctionRegistry(QueryEngine queryEngine) { + super.initializeFunctionRegistry(queryEngine); - register(); - } + queryEngine.getSqmFunctionRegistry().register("hql_json_text", new TextJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_json_int", new IntJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_json_float", new FloatJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_json_double", new DoubleJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_json_long", new LongJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_json_short", new ShortJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_json_bool", new BoolJsonSQLFunction()); - public void register() { - this.registerColumnTypeConsumer.accept(Types.JAVA_OBJECT, "jsonb"); - this.registerColumnTypeConsumer.accept(Types.JAVA_OBJECT, "json"); + queryEngine.getSqmFunctionRegistry().register("hql_json_" + "array_length", new LongJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_jsonb_" + "array_length", new LongJsonSQLFunction().setJsonb(true)); - this.registerFunctionConsumer.accept("hql_json_text", new TextJsonSQLFunction()); - this.registerFunctionConsumer.accept("hql_json_int", new IntJsonSQLFunction()); - this.registerFunctionConsumer.accept("hql_json_float", new FloatJsonSQLFunction()); - this.registerFunctionConsumer.accept("hql_json_double", new DoubleJsonSQLFunction()); - this.registerFunctionConsumer.accept("hql_json_long", new LongJsonSQLFunction()); - this.registerFunctionConsumer.accept("hql_json_short", new ShortJsonSQLFunction()); - this.registerFunctionConsumer.accept("hql_json_bool", new BoolJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_json_" + "typeof", new LongJsonSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_jsonb_" + "typeof", new LongJsonSQLFunction().setJsonb(true)); - this.registerJsonFunction("array_length", IntegerType.INSTANCE); - this.registerJsonFunction("typeof", StringType.INSTANCE); - this.registerFunctionConsumer.accept("hql_jsonb_contains", new JsonContainsSQLFunction().setJsonb(true)); - } + queryEngine.getSqmFunctionRegistry().register("hql_json_contains", new JsonContainsSQLFunction()); + queryEngine.getSqmFunctionRegistry().register("hql_jsonb_contains", new JsonContainsSQLFunction().setJsonb(true)); - private void registerJsonFunction(String name, Type type) { - this.registerFunctionConsumer.accept("hql_json_" + name, new JsonFunction(type, name)); - this.registerFunctionConsumer.accept("hql_jsonb_" + name, new JsonFunction(type, name).setJsonb(true)); } + @Override + public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { + super.contributeTypes(typeContributions, serviceRegistry); + typeContributions.getTypeConfiguration().getJdbcTypeRegistry().addDescriptor(Types.JAVA_OBJECT, new JsonBinaryJdbcTypeDescriptor()); + typeContributions.getTypeConfiguration().getJdbcTypeRegistry().addDescriptor(Types.JAVA_OBJECT, new JsonBinaryJdbcTypeDescriptor()); + } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractJsonSQLFunction.java index 2509d7f..a3a10b1 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractJsonSQLFunction.java @@ -1,14 +1,25 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions; -import com.google.common.base.Preconditions; -import java.util.List; import lombok.Getter; import lombok.Setter; -import org.hibernate.dialect.function.SQLFunction; -import org.hibernate.engine.spi.Mapping; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.type.BooleanType; -import org.hibernate.type.Type; +import org.hibernate.AssertionFailure; +import org.hibernate.query.spi.QueryParameterBinding; +import org.hibernate.query.spi.QueryParameterImplementor; +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.query.sqm.sql.internal.SqmParameterInterpretation; +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.ColumnReference; +import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.ast.tree.update.Assignable; +import org.hibernate.type.JavaObjectType; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.function.Function; /** * @author wener @@ -17,49 +28,100 @@ */ @Setter @Getter -public abstract class AbstractJsonSQLFunction implements SQLFunction { - protected int minimalArgumentCount = 2; - protected int maximalArgumentCount = 64; - protected boolean jsonb; +public abstract class AbstractJsonSQLFunction + extends AbstractSqmSelfRenderingFunctionDescriptor { - @Override - public boolean hasArguments() { - return true; - } + static int minimalArgumentCount = 2; + static int maximalArgumentCount = 64; + protected boolean jsonb = true; - @Override - public boolean hasParenthesesIfNoArguments() { - return false; + static Field queryParamField; + static Field queryParamBindingResolverField; + + static { + try { + queryParamField = SqmParameterInterpretation.class.getDeclaredField("queryParameter"); + queryParamField.setAccessible(true); + queryParamBindingResolverField = SqmParameterInterpretation.class.getDeclaredField("queryParameterBindingResolver"); + queryParamBindingResolverField.setAccessible(true); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } } - @Override - public Type getReturnType(Type firstArgumentType, Mapping mapping) { - return BooleanType.INSTANCE; + + protected Object getSqmParameterInterpretation(Object arg) { + if (!(arg instanceof SqmParameterInterpretation)) + throw new AssertionFailure("Not an implemented parameter type"); + try { + Object queryParam = queryParamField.get(arg); + Object queryParamBindingResolver = queryParamBindingResolverField.get(arg); + + QueryParameterBinding binding = ((Function, QueryParameterBinding>) queryParamBindingResolver).apply((QueryParameterImplementor) queryParam); + return binding.getBindValue(); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } } - public StringBuilder buildPath(StringBuilder sb, List arguments) { - return buildPath(sb, arguments, 0, arguments.size()); + public void buildPath(SqlAppender sb, List arguments) { + buildPath(sb, arguments, 0, arguments.size()); } - public StringBuilder buildPath(StringBuilder sb, List arguments, int n) { - return buildPath(sb, arguments, 0, n < 0 ? arguments.size() + n : n); + public void buildPath(SqlAppender sb, List arguments, int n) { + buildPath(sb, arguments, 0, n < 0 ? arguments.size() + n : n); } - public StringBuilder buildPath(StringBuilder sb, List arguments, int from, int to) { - sb.append(arguments.get(from)); - for (int i = from + 1; i < to; i++) { - sb.append("->").append(arguments.get(i)); + public void buildPath(SqlAppender sb, List arguments, int from, int to) { + Object arg = arguments.get(from); + + if (!(arg instanceof Assignable)) + throw new AssertionFailure("Not assignable"); + + ColumnReference columnReference = ((Assignable) arg).getColumnReferences().get(0); + sb.append(columnReference.getExpressionText()); + + for (int i = to - 1; i >= from + 1; i--) { + sb.append("->"); + + sb.append("'"); + sb.append(arg == null ? "null" : getSqmParameterInterpretation(arguments.get(i)).toString()); + sb.append("'"); } - return sb; + } + + public AbstractJsonSQLFunction() { + super( + "sql", + StandardArgumentsValidators.between(minimalArgumentCount, maximalArgumentCount), + StandardFunctionReturnTypeResolvers.invariant(JavaObjectType.INSTANCE), + null + ); } @Override - public final String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory){ - int argc = arguments.size(); - Preconditions.checkArgument(argc >= minimalArgumentCount, "At least %s arguments got %s", minimalArgumentCount, argc); - Preconditions.checkArgument(argc <= maximalArgumentCount, "At most %s arguments got %s", maximalArgumentCount, argc); - return doRender(firstArgumentType, arguments, factory).toString(); + + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + Predicate filter, + Boolean respectNulls, + Boolean fromFirst, + SqlAstTranslator walker) { + doRender(sqlAppender, sqlAstArguments); + } + + public void render( + SqlAppender sqlAppender, + List arguments, + SqlAstTranslator walker) { + doRender(sqlAppender, arguments); } - protected abstract CharSequence doRender(Type firstArgumentType, List arguments, SessionFactoryImplementor factory); -} + protected abstract void doRender(SqlAppender sb, List arguments); + + @Override + public String getArgumentListSignature() { + return ""; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractTypedJsonFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractTypedJsonFunction.java index 05f1e20..33aec6b 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractTypedJsonFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractTypedJsonFunction.java @@ -1,43 +1,37 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions; -import java.util.List; +import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.engine.spi.Mapping; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.type.Type; +import java.util.List; /** - * @author wener - * @author Alexander Liesenfeld - * @see functions-json - */ +* @author wener +* @author Alexander Liesenfeld +* @see functions-json +*/ public abstract class AbstractTypedJsonFunction extends AbstractJsonSQLFunction { - private final String conversion; - private final Type type; - - public AbstractTypedJsonFunction(Type type, String conversion) { - this.conversion = conversion; - this.type = type; - } + private final String conversion; - @Override - public Type getReturnType(Type firstArgumentType, Mapping mapping) { - return type; - } + public AbstractTypedJsonFunction(String conversion) { + this.conversion = conversion; + } - protected CharSequence doRender(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) { - StringBuilder sb = new StringBuilder(); + protected void doRender(SqlAppender sb, List arguments) { + if (conversion != null) { + sb.append('('); + } - if (conversion != null) { - sb.append('('); - } + Object arg = arguments.get(arguments.size() - 1); + buildPath(sb, arguments, -1); + sb.append("->>"); - buildPath(sb, arguments, -1).append("->>").append(arguments.get(arguments.size() - 1)); + sb.append("'"); + sb.append(arg == null ? "null" : getSqmParameterInterpretation(arg).toString()); + sb.append("'"); - if (conversion != null) { - sb.append(")::").append(conversion); + if (conversion != null) { + sb.append(")::"); + sb.append(conversion); + } } - - return sb; - } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/JsonFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/JsonFunction.java index c03bd50..d492389 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/JsonFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/JsonFunction.java @@ -1,13 +1,12 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions; -import java.util.List; - import lombok.Getter; import lombok.Setter; -import org.hibernate.engine.spi.Mapping; -import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.Type; +import java.util.List; + /** * @author wener * @author Alexander Liesenfeld @@ -23,32 +22,13 @@ public class JsonFunction extends AbstractJsonSQLFunction { JsonFunction() { super(); - setMinimalArgumentCount(1); - } - - public JsonFunction(Type type, String functionName) { - this(); - setFunctionName(functionName); - } - - @Override - public Type getReturnType(Type firstArgumentType, Mapping mapping) { - return type; } - protected String doRender(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) { - StringBuilder sb = new StringBuilder(); - sb.append(isJsonb() ? jsonbFunctionName : jsonFunctionName).append('('); + protected void doRender(SqlAppender sb, List arguments) { + sb.append(isJsonb() ? jsonbFunctionName : jsonFunctionName); + sb.append('('); buildPath(sb, arguments); sb.append(')'); - return sb.toString(); - } - - public JsonFunction setFunctionName(String functionName) { - this.functionName = functionName; - jsonFunctionName = "json_" + functionName; - jsonbFunctionName = "jsonb_" + functionName; - return this; } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/BoolJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/BoolJsonSQLFunction.java index fff47a0..50c430b 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/BoolJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/BoolJsonSQLFunction.java @@ -1,7 +1,6 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractTypedJsonFunction; -import org.hibernate.type.BooleanType; /** * @author Alexander Liesenfeld @@ -9,6 +8,6 @@ */ public class BoolJsonSQLFunction extends AbstractTypedJsonFunction { public BoolJsonSQLFunction() { - super(BooleanType.INSTANCE, "boolean"); + super("boolean"); } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/DoubleJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/DoubleJsonSQLFunction.java index 9b95e03..b54fa36 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/DoubleJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/DoubleJsonSQLFunction.java @@ -1,7 +1,6 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractTypedJsonFunction; -import org.hibernate.type.DoubleType; /** * @author wener @@ -10,6 +9,6 @@ */ public class DoubleJsonSQLFunction extends AbstractTypedJsonFunction { public DoubleJsonSQLFunction() { - super(DoubleType.INSTANCE, "float8"); + super("float8"); } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/FloatJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/FloatJsonSQLFunction.java index d928e07..68b4ac2 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/FloatJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/FloatJsonSQLFunction.java @@ -1,7 +1,6 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractTypedJsonFunction; -import org.hibernate.type.FloatType; /** * @author wener @@ -10,6 +9,6 @@ */ public class FloatJsonSQLFunction extends AbstractTypedJsonFunction { public FloatJsonSQLFunction() { - super(FloatType.INSTANCE, "float4"); + super("float4"); } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/IntJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/IntJsonSQLFunction.java index a655266..4cdf87e 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/IntJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/IntJsonSQLFunction.java @@ -1,7 +1,6 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractTypedJsonFunction; -import org.hibernate.type.IntegerType; /** * @author wener @@ -10,6 +9,6 @@ */ public class IntJsonSQLFunction extends AbstractTypedJsonFunction { public IntJsonSQLFunction() { - super(IntegerType.INSTANCE, "int"); + super("int"); } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/JsonContainsSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/JsonContainsSQLFunction.java index f189cc3..e0c9440 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/JsonContainsSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/JsonContainsSQLFunction.java @@ -1,28 +1,28 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; -import java.util.List; - import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractJsonSQLFunction; -import org.hibernate.dialect.function.SQLFunction; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.type.Type; +import org.hibernate.sql.ast.spi.SqlAppender; + +import java.util.List; /** * @author wener * @author Alexander Liesenfeld * @see functions-json */ -public class JsonContainsSQLFunction extends AbstractJsonSQLFunction implements SQLFunction { +public class JsonContainsSQLFunction extends AbstractJsonSQLFunction { @Override - protected String doRender(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) { - StringBuilder sb = new StringBuilder(); - super.buildPath(sb, arguments, -1) - .append("@>") - .append(arguments.get(arguments.size() - 1)) - .append("::") - .append(isJsonb() ? "jsonb" : "json"); - return sb.toString(); + protected void doRender(SqlAppender sb, List arguments) { + + super.buildPath(sb, arguments, -1); + Object arg = arguments.get(arguments.size() - 1); + sb.append("@>"); + sb.append("'"); + sb.append(arg == null ? "null" : getSqmParameterInterpretation(arg).toString()); + sb.append("'"); + sb.append("::"); + sb.append(isJsonb() ? "jsonb" : "json"); } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/LongJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/LongJsonSQLFunction.java index 544a96f..41f8f57 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/LongJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/LongJsonSQLFunction.java @@ -1,7 +1,6 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractTypedJsonFunction; -import org.hibernate.type.LongType; /** * @author wener @@ -10,6 +9,6 @@ */ public class LongJsonSQLFunction extends AbstractTypedJsonFunction { public LongJsonSQLFunction() { - super(LongType.INSTANCE, "bigint"); + super("bigint"); } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/ShortJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/ShortJsonSQLFunction.java index ea1e3f8..8da2f5d 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/ShortJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/ShortJsonSQLFunction.java @@ -1,7 +1,6 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractTypedJsonFunction; -import org.hibernate.type.ShortType; /** * @author wener @@ -10,6 +9,6 @@ */ public class ShortJsonSQLFunction extends AbstractTypedJsonFunction { public ShortJsonSQLFunction() { - super(ShortType.INSTANCE, "smallint"); + super("smallint"); } } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/TextJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/TextJsonSQLFunction.java index c542c7f..8771a03 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/TextJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/TextJsonSQLFunction.java @@ -1,7 +1,6 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractTypedJsonFunction; -import org.hibernate.type.StringType; /** * @author Alexander Liesenfeld @@ -9,6 +8,6 @@ */ public class TextJsonSQLFunction extends AbstractTypedJsonFunction { public TextJsonSQLFunction() { - super(StringType.INSTANCE, null); + super("text"); } } diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/EnumTest.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/EnumTest.java new file mode 100644 index 0000000..81dc23b --- /dev/null +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/EnumTest.java @@ -0,0 +1,7 @@ +package com.github.alexliesenfeld.querydsl.jpa.hibernate; + +public enum EnumTest { + TEST1, + TEST2, + TEST3 +} diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/IntegrationTest.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/IntegrationTest.java new file mode 100644 index 0000000..bd2de2c --- /dev/null +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/IntegrationTest.java @@ -0,0 +1,148 @@ +package com.github.alexliesenfeld.querydsl.jpa.hibernate; + +import com.github.alexliesenfeld.querydsl.jpa.hibernate.dto.ChildSampleData; +import com.github.alexliesenfeld.querydsl.jpa.hibernate.dto.SampleData; +import com.github.alexliesenfeld.querydsl.jpa.hibernate.entity.QTestEntity; +import com.github.alexliesenfeld.querydsl.jpa.hibernate.entity.TestEntity; +import com.github.alexliesenfeld.querydsl.jpa.hibernate.initializer.Initializer; +import com.github.alexliesenfeld.querydsl.jpa.hibernate.repository.TestRepository; +import com.querydsl.core.types.dsl.BooleanExpression; +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.StreamSupport; + +//@ExtendWith({SpringExtension.class}) +@ExtendWith(SpringExtension.class) +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@TestPropertySource("classpath:application.properties") +@ContextConfiguration(classes = {IntegrationTest.TestConfig.class}, initializers ={Initializer.class}) +public class IntegrationTest { + + @SpringBootApplication + @TestPropertySource("classpath:application.properties") + @ContextConfiguration(initializers ={ Initializer.class}) + static class TestConfig {} + + @Autowired + TestRepository testRepository; + + @Test + public void testContet() { + TestEntity testEntity = new TestEntity(); + testEntity.setFileId(UUID.randomUUID()); + testEntity.setExtension("txt"); + testEntity.setFileName("filename"); + testEntity.setLength(10l); + testEntity.setEnumList(List.of(EnumTest.TEST1)); + testEntity.setChildParam(SampleData.builder() + .idField(0) + .stringField("0") + .child(ChildSampleData.builder() + .intField(1) + .integerField(2) + .longField(3L) + .longClsField(4L) + .fieldString("5") + .build()) + .build()); + Map tags = new LinkedHashMap<>(); + tags.put("key1", "val1"); + tags.put("key2", "val2"); + tags.put("key3", "val3"); + testEntity.setTags(tags); + + testRepository.saveAndFlush(testEntity); + + testEntity = new TestEntity(); + testEntity.setFileId(UUID.randomUUID()); + testEntity.setExtension("txt"); + testEntity.setFileName("filename"); + testEntity.setLength(10L); + testEntity.setEnumList(List.of(EnumTest.TEST1, EnumTest.TEST3)); + testEntity.setChildParam(SampleData.builder() + .idField(6) + .stringField("6") + .child(ChildSampleData.builder() + .intField(7) + .integerField(8) + .longField(9L) + .longClsField(10L) + .fieldString("11") + .build()) + .build()); + + tags = new LinkedHashMap<>(); + tags.put("key1", "val21"); + tags.put("key3", "val23"); + testEntity.setTags(tags); + + testRepository.saveAndFlush(testEntity); + + QTestEntity q = QTestEntity.testEntity; + BooleanExpression expression = q.fileName.eq("filename"); + JsonPath jsonPath = JsonPath.of(q.tags); + BooleanExpression exp = expression.and(jsonPath.get("key1").asText().eq("val21")); + + JsonPath childCheck = JsonPath.of(q.childParam); + exp = exp.and(childCheck.get("child").get("longField").asLong().eq(9L)); + + JsonPath listPath = JsonPath.of(q.enumList); + exp = exp.and(listPath.contains(EnumTest.TEST3)); + Iterable entities = testRepository.findAll(exp); + + Assert.assertEquals(1, StreamSupport.stream(entities.spliterator(), false).count()); + + } + + @Test + public void testContet2() { + TestEntity testEntity = new TestEntity(); + testEntity.setFileId(UUID.randomUUID()); + testEntity.setExtension("txt"); + testEntity.setFileName("filename"); + testEntity.setLength(10L); + testEntity.setEnumList(List.of(EnumTest.TEST1, EnumTest.TEST3)); + testEntity.setChildParam(SampleData.builder() + .idField(6) + .stringField("6") + .child(ChildSampleData.builder() + .intField(7) + .integerField(8) + .longField(9L) + .longClsField(10L) + .fieldString("11") + .build()) + .build()); + + Map tags = new LinkedHashMap<>(); + tags.put("key1", "val21"); + tags.put("key3", "val23"); + testEntity.setTags(tags); + + testRepository.saveAndFlush(testEntity); + + QTestEntity q = QTestEntity.testEntity; + JsonPath listPath = JsonPath.of(q.enumList); + BooleanExpression exp = listPath.contains(EnumTest.TEST3); + Iterable entities = testRepository.findAll(exp); + + Assert.assertEquals(1, StreamSupport.stream(entities.spliterator(), false).count()); + + } + // tests +} \ No newline at end of file diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/TestApplication.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/TestApplication.java new file mode 100644 index 0000000..5b2b32f --- /dev/null +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/TestApplication.java @@ -0,0 +1,25 @@ +package com.github.alexliesenfeld.querydsl.jpa.hibernate; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.TestPropertySource; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@SpringBootApplication +@Import(value = {} +) +@Configuration +@EntityScan("com.github.alexliesenfeld.querydsl.jpa.hibernate") +@EnableJpaRepositories(basePackages = "com.github.alexliesenfeld.querydsl.jpa.hibernate") +@EnableTransactionManagement +@TestPropertySource +//@EnableAspectJAutoProxy +public class TestApplication { + public static void main(String[] args) { + SpringApplication.run(TestApplication.class, args); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/dto/ChildSampleData.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/dto/ChildSampleData.java new file mode 100644 index 0000000..224a28e --- /dev/null +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/dto/ChildSampleData.java @@ -0,0 +1,20 @@ +package com.github.alexliesenfeld.querydsl.jpa.hibernate.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ChildSampleData { + private int intField; + private Integer integerField; + + private long longField; + private Long longClsField; + + private String fieldString; +} diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/dto/SampleData.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/dto/SampleData.java new file mode 100644 index 0000000..3ea9b39 --- /dev/null +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/dto/SampleData.java @@ -0,0 +1,16 @@ +package com.github.alexliesenfeld.querydsl.jpa.hibernate.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SampleData { + private int idField; + private String stringField; + private ChildSampleData child; +} diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/entity/TestEntity.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/entity/TestEntity.java new file mode 100644 index 0000000..925790c --- /dev/null +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/entity/TestEntity.java @@ -0,0 +1,44 @@ +package com.github.alexliesenfeld.querydsl.jpa.hibernate.entity; + +import com.github.alexliesenfeld.querydsl.jpa.hibernate.EnumTest; +import com.github.alexliesenfeld.querydsl.jpa.hibernate.dto.SampleData; +import io.hypersistence.utils.hibernate.type.json.JsonBinaryType; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.Type; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Entity +@Table(name = "document") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class TestEntity { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID fileId; + private String fileName; + private String extension; + private long length; + + @Column(columnDefinition = "jsonb") + @Type(JsonBinaryType.class) +// @JdbcTypeCode(SqlTypes.JSON) + private Map tags; + + @Column(columnDefinition = "jsonb") + @Type(JsonBinaryType.class) +// @JdbcTypeCode(SqlTypes.JSON) + private SampleData childParam; + + @Column(columnDefinition = "jsonb") + @Type(JsonBinaryType.class) + private List enumList; +} \ No newline at end of file diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/initializer/Initializer.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/initializer/Initializer.java new file mode 100644 index 0000000..5ffa424 --- /dev/null +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/initializer/Initializer.java @@ -0,0 +1,25 @@ +package com.github.alexliesenfeld.querydsl.jpa.hibernate.initializer; + +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.testcontainers.containers.PostgreSQLContainer; + +public class Initializer implements ApplicationContextInitializer { + @Override + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + PostgreSQLContainer dbContainer = new PostgreSQLContainer<>("postgres:10.6-alpine"); + dbContainer.start(); + + TestPropertyValues.of( + "spring.datasource.url=" + dbContainer.getJdbcUrl(), + "spring.datasource.username=" + dbContainer.getUsername(), + "spring.datasource.password=" + dbContainer.getPassword(), + "spring.datasource.driverClassName=org.postgresql.Driver", + "spring.jpa.database-platform=org.hibernate.dialect.PostgresPlusDialect", + "spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true", + "spring.jpa.properties.hibernate.dialect=com.github.alexliesenfeld.querydsl.jpa.hibernate.PostgreSQLJsonDialect", + "spring.jpa.open-in-view=false") + .applyTo(configurableApplicationContext.getEnvironment()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/repository/TestRepository.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/repository/TestRepository.java new file mode 100644 index 0000000..b4f78aa --- /dev/null +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/repository/TestRepository.java @@ -0,0 +1,12 @@ +package com.github.alexliesenfeld.querydsl.jpa.hibernate.repository; + +import com.github.alexliesenfeld.querydsl.jpa.hibernate.entity.TestEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface TestRepository extends JpaRepository, QuerydslPredicateExecutor { +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..d6c0075 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,18 @@ +spring.datasource.driver-class-name= org.postgresql.Driver +spring.jpa.hibernate.ddl-auto=create + +spring.datasource.driverClassName=org.postgresql.Driver +spring.jpa.database-platform=org.hibernate.dialect.PostgresPlusDialect +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true +spring.jpa.properties.hibernate.dialect=com.github.alexliesenfeld.querydsl.jpa.hibernate.PostgreSQLJsonDialect +spring.jpa.open-in-view=false + +spring.datasource.url=${DB_URL} +spring.datasource.username=${DB_USERNAME} +spring.datasource.password=${DB_PASSWORD} + +#Debug SQL +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.hibernate.orm.jdbc.bind=TRACE \ No newline at end of file From 024731d3c799012a2caba62473dfce5f73122065 Mon Sep 17 00:00:00 2001 From: Sebastiano Suraci Date: Mon, 14 Aug 2023 18:24:07 +0200 Subject: [PATCH 2/2] upgrade to spring-boot 3.1 full parametric queries sonarlint fixes --- CHANGELOG | 4 ++ pom.xml | 60 ++++--------------- .../jpa/hibernate/PostgreSQLJsonDialect.java | 33 +++++----- .../functions/AbstractJsonSQLFunction.java | 56 ++++------------- .../functions/AbstractTypedJsonFunction.java | 13 ++-- .../jpa/hibernate/functions/JsonFunction.java | 7 ++- .../types/JsonContainsSQLFunction.java | 12 ++-- .../hibernate/initializer/Initializer.java | 2 +- 8 files changed, 61 insertions(+), 126 deletions(-) create mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..9fe4252 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,4 @@ +0.0.9-SNAPSHOT: +- updated to spring-boot 3.1 and hibernate 6.2 +- generation of full-parametric SQL queries (previous version generated mixed parametric / static queries, which required messing with Hibernate internals and moreover could lead to SQL injections) +- updated postgresql testcontainer to version 15 diff --git a/pom.xml b/pom.xml index ce9219d..2d031d7 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.github.alexliesenfeld querydsl-jpa-postgres-json - 0.0.8-SNAPSHOT + 0.0.9-SNAPSHOT ${project.groupId}:${project.artifactId} Querydsl extension for using PostgreSQL JSON types with JPA @@ -16,11 +16,12 @@ UTF-8 5.0.0 - - 6.1.7.Final - 1.18.26 - 2.14.2 - 3.3.2 + + 6.2.6.Final + 1.18.28 + 2.15.2 + 3.5.1 + 3.1.2 @@ -58,35 +59,29 @@ true - - - - - - org.springframework.boot spring-boot-starter-test - 3.0.6 + ${spring-boot.version} test org.springframework.boot spring-boot-starter-data-jpa - 3.0.6 + ${spring-boot.version} test org.springframework.data spring-data-jpa - 3.0.6 + ${spring-boot.version} test org.springframework.boot spring-boot-starter-web - 3.0.6 + ${spring-boot.version} test @@ -103,26 +98,6 @@ test - - - - - - - - - - - - - - - - - - - - com.querydsl querydsl-apt @@ -153,7 +128,7 @@ maven-deploy-plugin - 2.8.2 + 3.1.1 default-deploy @@ -222,17 +197,6 @@ apt-maven-plugin 1.1.3 - - - - - - - - - - - generate-test-entities generate-test-sources diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/PostgreSQLJsonDialect.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/PostgreSQLJsonDialect.java index e6c0259..480dfdf 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/PostgreSQLJsonDialect.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/PostgreSQLJsonDialect.java @@ -2,9 +2,10 @@ import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types.*; import io.hypersistence.utils.hibernate.type.json.internal.JsonBinaryJdbcTypeDescriptor; + +import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.PostgresPlusDialect; -import org.hibernate.query.spi.QueryEngine; import org.hibernate.service.ServiceRegistry; import java.sql.Types; @@ -25,26 +26,26 @@ public PostgreSQLJsonDialect() { @Override - public void initializeFunctionRegistry(QueryEngine queryEngine) { - super.initializeFunctionRegistry(queryEngine); + public void initializeFunctionRegistry(FunctionContributions functionContributions) { + super.initializeFunctionRegistry(functionContributions); - queryEngine.getSqmFunctionRegistry().register("hql_json_text", new TextJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_json_int", new IntJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_json_float", new FloatJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_json_double", new DoubleJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_json_long", new LongJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_json_short", new ShortJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_json_bool", new BoolJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_json_text", new TextJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_json_int", new IntJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_json_float", new FloatJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_json_double", new DoubleJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_json_long", new LongJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_json_short", new ShortJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_json_bool", new BoolJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_json_" + "array_length", new LongJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_jsonb_" + "array_length", new LongJsonSQLFunction().setJsonb(true)); + functionContributions.getFunctionRegistry().register("hql_json_" + "array_length", new LongJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_jsonb_" + "array_length", new LongJsonSQLFunction().setJsonb(true)); - queryEngine.getSqmFunctionRegistry().register("hql_json_" + "typeof", new LongJsonSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_jsonb_" + "typeof", new LongJsonSQLFunction().setJsonb(true)); + functionContributions.getFunctionRegistry().register("hql_json_" + "typeof", new LongJsonSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_jsonb_" + "typeof", new LongJsonSQLFunction().setJsonb(true)); - queryEngine.getSqmFunctionRegistry().register("hql_json_contains", new JsonContainsSQLFunction()); - queryEngine.getSqmFunctionRegistry().register("hql_jsonb_contains", new JsonContainsSQLFunction().setJsonb(true)); + functionContributions.getFunctionRegistry().register("hql_json_contains", new JsonContainsSQLFunction()); + functionContributions.getFunctionRegistry().register("hql_jsonb_contains", new JsonContainsSQLFunction().setJsonb(true)); } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractJsonSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractJsonSQLFunction.java index a3a10b1..b2c24ef 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractJsonSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractJsonSQLFunction.java @@ -3,12 +3,9 @@ import lombok.Getter; import lombok.Setter; import org.hibernate.AssertionFailure; -import org.hibernate.query.spi.QueryParameterBinding; -import org.hibernate.query.spi.QueryParameterImplementor; 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.query.sqm.sql.internal.SqmParameterInterpretation; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.SqlAstNode; @@ -17,9 +14,7 @@ import org.hibernate.sql.ast.tree.update.Assignable; import org.hibernate.type.JavaObjectType; -import java.lang.reflect.Field; import java.util.List; -import java.util.function.Function; /** * @author wener @@ -35,44 +30,16 @@ public abstract class AbstractJsonSQLFunction static int maximalArgumentCount = 64; protected boolean jsonb = true; - static Field queryParamField; - static Field queryParamBindingResolverField; - - static { - try { - queryParamField = SqmParameterInterpretation.class.getDeclaredField("queryParameter"); - queryParamField.setAccessible(true); - queryParamBindingResolverField = SqmParameterInterpretation.class.getDeclaredField("queryParameterBindingResolver"); - queryParamBindingResolverField.setAccessible(true); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } - } - - - protected Object getSqmParameterInterpretation(Object arg) { - if (!(arg instanceof SqmParameterInterpretation)) - throw new AssertionFailure("Not an implemented parameter type"); - try { - Object queryParam = queryParamField.get(arg); - Object queryParamBindingResolver = queryParamBindingResolverField.get(arg); - QueryParameterBinding binding = ((Function, QueryParameterBinding>) queryParamBindingResolver).apply((QueryParameterImplementor) queryParam); - return binding.getBindValue(); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public void buildPath(SqlAppender sb, List arguments) { - buildPath(sb, arguments, 0, arguments.size()); + public void buildPath(SqlAppender sb, List arguments, SqlAstTranslator walker) { + buildPath(sb, arguments, 0, arguments.size(), walker); } - public void buildPath(SqlAppender sb, List arguments, int n) { - buildPath(sb, arguments, 0, n < 0 ? arguments.size() + n : n); + public void buildPath(SqlAppender sb, List arguments, int n, SqlAstTranslator walker) { + buildPath(sb, arguments, 0, n < 0 ? arguments.size() + n : n, walker); } - public void buildPath(SqlAppender sb, List arguments, int from, int to) { + public void buildPath(SqlAppender sb, List arguments, int from, int to, SqlAstTranslator walker) { Object arg = arguments.get(from); if (!(arg instanceof Assignable)) @@ -83,14 +50,11 @@ public void buildPath(SqlAppender sb, List arguments, int from, int to) { for (int i = to - 1; i >= from + 1; i--) { sb.append("->"); - - sb.append("'"); - sb.append(arg == null ? "null" : getSqmParameterInterpretation(arguments.get(i)).toString()); - sb.append("'"); + arguments.get(i).accept(walker); } } - public AbstractJsonSQLFunction() { + protected AbstractJsonSQLFunction() { super( "sql", StandardArgumentsValidators.between(minimalArgumentCount, maximalArgumentCount), @@ -108,17 +72,17 @@ public void render( Boolean respectNulls, Boolean fromFirst, SqlAstTranslator walker) { - doRender(sqlAppender, sqlAstArguments); + doRender(sqlAppender, sqlAstArguments, walker); } public void render( SqlAppender sqlAppender, List arguments, SqlAstTranslator walker) { - doRender(sqlAppender, arguments); + doRender(sqlAppender, arguments, walker); } - protected abstract void doRender(SqlAppender sb, List arguments); + protected abstract void doRender(SqlAppender sb, List arguments, SqlAstTranslator walker); @Override public String getArgumentListSignature() { diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractTypedJsonFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractTypedJsonFunction.java index 33aec6b..7a0ef63 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractTypedJsonFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/AbstractTypedJsonFunction.java @@ -1,6 +1,8 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions; +import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; import java.util.List; @@ -12,22 +14,19 @@ public abstract class AbstractTypedJsonFunction extends AbstractJsonSQLFunction { private final String conversion; - public AbstractTypedJsonFunction(String conversion) { + protected AbstractTypedJsonFunction(String conversion) { this.conversion = conversion; } - protected void doRender(SqlAppender sb, List arguments) { + protected void doRender(SqlAppender sb, List arguments, SqlAstTranslator walker) { if (conversion != null) { sb.append('('); } - Object arg = arguments.get(arguments.size() - 1); - buildPath(sb, arguments, -1); + buildPath(sb, arguments, -1, walker); sb.append("->>"); - sb.append("'"); - sb.append(arg == null ? "null" : getSqmParameterInterpretation(arg).toString()); - sb.append("'"); + arguments.get(arguments.size() - 1).accept(walker); if (conversion != null) { sb.append(")::"); diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/JsonFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/JsonFunction.java index d492389..55a7f09 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/JsonFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/JsonFunction.java @@ -2,7 +2,10 @@ import lombok.Getter; import lombok.Setter; + +import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.type.Type; import java.util.List; @@ -24,10 +27,10 @@ public class JsonFunction extends AbstractJsonSQLFunction { super(); } - protected void doRender(SqlAppender sb, List arguments) { + protected void doRender(SqlAppender sb, List arguments, SqlAstTranslator walker) { sb.append(isJsonb() ? jsonbFunctionName : jsonFunctionName); sb.append('('); - buildPath(sb, arguments); + buildPath(sb, arguments, walker); sb.append(')'); } diff --git a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/JsonContainsSQLFunction.java b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/JsonContainsSQLFunction.java index e0c9440..c50ce1a 100644 --- a/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/JsonContainsSQLFunction.java +++ b/src/main/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/functions/types/JsonContainsSQLFunction.java @@ -1,7 +1,10 @@ package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types; import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.AbstractJsonSQLFunction; + +import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; import java.util.List; @@ -13,14 +16,11 @@ public class JsonContainsSQLFunction extends AbstractJsonSQLFunction { @Override - protected void doRender(SqlAppender sb, List arguments) { + protected void doRender(SqlAppender sb, List arguments, SqlAstTranslator walker) { - super.buildPath(sb, arguments, -1); - Object arg = arguments.get(arguments.size() - 1); + super.buildPath(sb, arguments, -1, walker); sb.append("@>"); - sb.append("'"); - sb.append(arg == null ? "null" : getSqmParameterInterpretation(arg).toString()); - sb.append("'"); + arguments.get(arguments.size() - 1).accept(walker); sb.append("::"); sb.append(isJsonb() ? "jsonb" : "json"); } diff --git a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/initializer/Initializer.java b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/initializer/Initializer.java index 5ffa424..0f2fd9a 100644 --- a/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/initializer/Initializer.java +++ b/src/test/java/com/github/alexliesenfeld/querydsl/jpa/hibernate/initializer/Initializer.java @@ -8,7 +8,7 @@ public class Initializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { - PostgreSQLContainer dbContainer = new PostgreSQLContainer<>("postgres:10.6-alpine"); + PostgreSQLContainer dbContainer = new PostgreSQLContainer<>("postgres:15.3-alpine"); dbContainer.start(); TestPropertyValues.of(