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 f87c1f5..2d031d7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,21 +4,24 @@
com.github.alexliesenfeld
querydsl-jpa-postgres-json
- 0.0.7-SNAPSHOT
+ 0.0.9-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.2.6.Final
+ 1.18.28
+ 2.15.2
+ 3.5.1
+ 3.1.2
@@ -34,14 +37,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 +59,64 @@
true
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot.version}
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ ${spring-boot.version}
+ test
+
+
+
+ org.springframework.data
+ spring-data-jpa
+ ${spring-boot.version}
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot.version}
+ 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
@@ -67,7 +128,7 @@
maven-deploy-plugin
- 2.8.2
+ 3.1.1
default-deploy
@@ -130,6 +191,43 @@
+
+
+ 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..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
@@ -1,86 +1,58 @@
package com.github.alexliesenfeld.querydsl.jpa.hibernate;
-import java.sql.Types;
-import java.util.function.BiConsumer;
-
+import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.types.*;
+import io.hypersistence.utils.hibernate.type.json.internal.JsonBinaryJdbcTypeDescriptor;
-import com.github.alexliesenfeld.querydsl.jpa.hibernate.functions.JsonFunction;
+import org.hibernate.boot.model.FunctionContributions;
+import org.hibernate.boot.model.TypeContributions;
+import org.hibernate.dialect.PostgresPlusDialect;
+import org.hibernate.service.ServiceRegistry;
-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 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(FunctionContributions functionContributions) {
+ super.initializeFunctionRegistry(functionContributions);
- register();
- }
+ 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());
- public void register() {
- this.registerColumnTypeConsumer.accept(Types.JAVA_OBJECT, "jsonb");
- this.registerColumnTypeConsumer.accept(Types.JAVA_OBJECT, "json");
+ functionContributions.getFunctionRegistry().register("hql_json_" + "array_length", new LongJsonSQLFunction());
+ functionContributions.getFunctionRegistry().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());
+ functionContributions.getFunctionRegistry().register("hql_json_" + "typeof", new LongJsonSQLFunction());
+ functionContributions.getFunctionRegistry().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));
- }
+ functionContributions.getFunctionRegistry().register("hql_json_contains", new JsonContainsSQLFunction());
+ functionContributions.getFunctionRegistry().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..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
@@ -1,14 +1,20 @@
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.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
+import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
+import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
+import org.hibernate.sql.ast.SqlAstTranslator;
+import org.hibernate.sql.ast.spi.SqlAppender;
+import org.hibernate.sql.ast.tree.SqlAstNode;
+import org.hibernate.sql.ast.tree.expression.ColumnReference;
+import org.hibernate.sql.ast.tree.predicate.Predicate;
+import org.hibernate.sql.ast.tree.update.Assignable;
+import org.hibernate.type.JavaObjectType;
+
+import java.util.List;
/**
* @author wener
@@ -17,49 +23,69 @@
*/
@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;
+
+
+ public void buildPath(SqlAppender sb, List extends SqlAstNode> arguments, SqlAstTranslator> walker) {
+ buildPath(sb, arguments, 0, arguments.size(), walker);
}
- @Override
- public boolean hasParenthesesIfNoArguments() {
- return false;
+ public void buildPath(SqlAppender sb, List extends SqlAstNode> arguments, int n, SqlAstTranslator> walker) {
+ buildPath(sb, arguments, 0, n < 0 ? arguments.size() + n : n, walker);
}
- @Override
- public Type getReturnType(Type firstArgumentType, Mapping mapping) {
- return BooleanType.INSTANCE;
+ public void buildPath(SqlAppender sb, List extends SqlAstNode> arguments, int from, int to, SqlAstTranslator> walker) {
+ 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("->");
+ arguments.get(i).accept(walker);
+ }
}
- public StringBuilder buildPath(StringBuilder sb, List arguments) {
- return buildPath(sb, arguments, 0, arguments.size());
+ protected AbstractJsonSQLFunction() {
+ super(
+ "sql",
+ StandardArgumentsValidators.between(minimalArgumentCount, maximalArgumentCount),
+ StandardFunctionReturnTypeResolvers.invariant(JavaObjectType.INSTANCE),
+ null
+ );
}
- public StringBuilder buildPath(StringBuilder sb, List arguments, int n) {
- return buildPath(sb, arguments, 0, n < 0 ? arguments.size() + n : n);
+ @Override
+
+ public void render(
+ SqlAppender sqlAppender,
+ List extends SqlAstNode> sqlAstArguments,
+ Predicate filter,
+ Boolean respectNulls,
+ Boolean fromFirst,
+ SqlAstTranslator> walker) {
+ doRender(sqlAppender, sqlAstArguments, walker);
}
- 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));
- }
- return sb;
+ public void render(
+ SqlAppender sqlAppender,
+ List extends SqlAstNode> arguments,
+ SqlAstTranslator> walker) {
+ doRender(sqlAppender, arguments, walker);
}
+ protected abstract void doRender(SqlAppender sb, List extends SqlAstNode> arguments, SqlAstTranslator> walker);
+
@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 String getArgumentListSignature() {
+ return "";
}
-
- protected abstract CharSequence doRender(Type firstArgumentType, List arguments, SessionFactoryImplementor factory);
-}
+}
\ 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..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,43 +1,36 @@
package com.github.alexliesenfeld.querydsl.jpa.hibernate.functions;
-import java.util.List;
+import org.hibernate.sql.ast.SqlAstTranslator;
+import org.hibernate.sql.ast.spi.SqlAppender;
+import org.hibernate.sql.ast.tree.SqlAstNode;
-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;
- }
+ protected 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 extends SqlAstNode> arguments, SqlAstTranslator> walker) {
+ if (conversion != null) {
+ sb.append('(');
+ }
- if (conversion != null) {
- sb.append('(');
- }
+ buildPath(sb, arguments, -1, walker);
+ sb.append("->>");
- buildPath(sb, arguments, -1).append("->>").append(arguments.get(arguments.size() - 1));
+ arguments.get(arguments.size() - 1).accept(walker);
- 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..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
@@ -1,13 +1,15 @@
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.SqlAstTranslator;
+import org.hibernate.sql.ast.spi.SqlAppender;
+import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.Type;
+import java.util.List;
+
/**
* @author wener
* @author Alexander Liesenfeld
@@ -23,32 +25,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('(');
- buildPath(sb, arguments);
+ protected void doRender(SqlAppender sb, List extends SqlAstNode> arguments, SqlAstTranslator> walker) {
+ sb.append(isJsonb() ? jsonbFunctionName : jsonFunctionName);
+ sb.append('(');
+ buildPath(sb, arguments, walker);
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..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,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.SqlAstTranslator;
+import org.hibernate.sql.ast.spi.SqlAppender;
+import org.hibernate.sql.ast.tree.SqlAstNode;
+
+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 extends SqlAstNode> arguments, SqlAstTranslator> walker) {
+
+ super.buildPath(sb, arguments, -1, walker);
+ sb.append("@>");
+ arguments.get(arguments.size() - 1).accept(walker);
+ 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..0f2fd9a
--- /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:15.3-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