From 8af6a541c935b3bfccfa9e73fdd0e0d854346873 Mon Sep 17 00:00:00 2001 From: A F <18417689+perplexhub@users.noreply.github.com> Date: Tue, 29 Aug 2023 01:22:15 +0800 Subject: [PATCH 1/4] Merge pull request #119 from nstdio/gh114 Basic support for queries against Postgres jsonb type. --- rsql-common/pom.xml | 6 + .../perplexhub/rsql/RSQLVisitorBase.java | 4 + .../rsql/RSQLJPAAutoConfiguration.java | 100 +++++++++- rsql-jpa/pom.xml | 17 ++ .../rsql/RSQLJPAPredicateConverter.java | 118 +++++++++--- .../perplexhub/rsql/RSQLJPASupport.java | 6 + .../rsql/RSQLJPASupportPostgresJsonTest.java | 173 ++++++++++++++++++ .../rsql/model/PostgresJsonEntity.java | 44 +++++ .../PostgresJsonEntityRepository.java | 11 ++ .../test/resources/application-postgres.yml | 7 + 10 files changed, 448 insertions(+), 38 deletions(-) create mode 100644 rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java create mode 100644 rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java create mode 100644 rsql-jpa/src/test/java/io/github/perplexhub/rsql/repository/jpa/postgres/PostgresJsonEntityRepository.java create mode 100644 rsql-jpa/src/test/resources/application-postgres.yml diff --git a/rsql-common/pom.xml b/rsql-common/pom.xml index 3e2e2151..cc2e5c3d 100644 --- a/rsql-common/pom.xml +++ b/rsql-common/pom.xml @@ -37,6 +37,12 @@ hamcrest test + + io.hypersistence + hypersistence-utils-hibernate-62 + 3.5.1 + test + com.h2database h2 diff --git a/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java b/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java index 2f78f765..413e4ce7 100644 --- a/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java +++ b/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java @@ -12,7 +12,10 @@ import javax.persistence.metamodel.ManagedType; import javax.persistence.metamodel.PluralAttribute; +import lombok.Getter; +import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.orm.jpa.vendor.Database; import org.springframework.util.StringUtils; import cz.jirutka.rsql.parser.ast.RSQLVisitor; @@ -26,6 +29,7 @@ public abstract class RSQLVisitorBase implements RSQLVisitor { protected static volatile @Setter Map managedTypeMap; protected static volatile @Setter Map entityManagerMap; + protected static volatile @Setter @Getter Map entityManagerDatabase = Map.of(); protected static final Map primitiveToWrapper; protected static volatile @Setter Map, Map> propertyRemapping; protected static volatile @Setter Map, List> globalPropertyWhitelist; diff --git a/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java b/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java index d4ada2e1..29f7ba2c 100644 --- a/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java +++ b/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java @@ -1,24 +1,104 @@ package io.github.perplexhub.rsql; -import java.util.Map; - -import javax.persistence.EntityManager; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toMap; +import io.github.perplexhub.rsql.RSQLJPAAutoConfiguration.HibernateEntityManagerDatabaseConfiguration; +import jakarta.persistence.EntityManager; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.Session; +import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.dialect.HSQLDialect; +import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.dialect.SybaseDialect; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.internal.SessionFactoryImpl; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - -import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Import; +import org.springframework.orm.jpa.vendor.Database; @Slf4j @Configuration @ConditionalOnClass(EntityManager.class) +@Import(HibernateEntityManagerDatabaseConfiguration.class) public class RSQLJPAAutoConfiguration { - @Bean - public RSQLCommonSupport rsqlCommonSupport(Map entityManagerMap) { - log.info("RSQLJPAAutoConfiguration.rsqlCommonSupport(entityManagerMap:{})", entityManagerMap.size()); - return new RSQLCommonSupport(entityManagerMap); - } + @Bean + public RSQLCommonSupport rsqlCommonSupport(Map entityManagerMap, + ObjectProvider entityManagerDatabaseProvider) { + log.info("RSQLJPAAutoConfiguration.rsqlCommonSupport(entityManagerMap:{})", entityManagerMap.size()); + var entityManagerDatabase = entityManagerDatabaseProvider.getIfAvailable(() -> new EntityManagerDatabase(Map.of())); + + return new RSQLJPASupport(entityManagerMap, entityManagerDatabase.value()); + } + + @Configuration + @ConditionalOnClass(SessionImplementor.class) + static + class HibernateEntityManagerDatabaseConfiguration { + + @Bean + public EntityManagerDatabase entityManagerDatabase(ObjectProvider entityManagers) { + return entityManagers.stream() + .map(entityManager -> { + var sessionFactory = entityManager.unwrap(Session.class).getSessionFactory(); + var dialect = ((SessionFactoryImpl) sessionFactory).getJdbcServices().getDialect(); + + return Optional.ofNullable(toDatabase(dialect)) + .map(db -> Map.entry(entityManager, db)) + .orElse(null); + }) + .filter(Objects::nonNull) + .collect(collectingAndThen( + toMap(Entry::getKey, Entry::getValue, (db1, db2) -> db1, IdentityHashMap::new), + EntityManagerDatabase::new + )); + } + + private Database toDatabase(Dialect dialect) { + if (dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect) { + return Database.POSTGRESQL; + } else if (dialect instanceof MySQLDialect) { + return Database.MYSQL; + } else if (dialect instanceof SQLServerDialect) { + return Database.SQL_SERVER; + } else if (dialect instanceof OracleDialect) { + return Database.ORACLE; + } else if (dialect instanceof DerbyDialect) { + return Database.DERBY; + } else if (dialect instanceof DB2Dialect) { + return Database.DB2; + } else if (dialect instanceof H2Dialect) { + return Database.H2; + } else if (dialect instanceof AbstractHANADialect) { + return Database.HANA; + } else if (dialect instanceof HSQLDialect) { + return Database.HSQL; + } else if (dialect instanceof SybaseDialect) { + return Database.SQL_SERVER; + } + + return null; + } + } + + record EntityManagerDatabase(Map value) { + } } diff --git a/rsql-jpa/pom.xml b/rsql-jpa/pom.xml index e21f5f55..ef99d05b 100644 --- a/rsql-jpa/pom.xml +++ b/rsql-jpa/pom.xml @@ -41,6 +41,23 @@ h2 test + + org.postgresql + postgresql + test + + + org.testcontainers + postgresql + 1.19.0 + test + + + io.hypersistence + hypersistence-utils-hibernate-60 + 3.5.2 + test + org.projectlombok lombok diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java index 6b587f96..43cc5d74 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java @@ -2,6 +2,8 @@ import static io.github.perplexhub.rsql.RSQLOperators.*; +import jakarta.persistence.Column; +import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; import java.util.function.Function; @@ -18,14 +20,18 @@ import cz.jirutka.rsql.parser.ast.ComparisonNode; import cz.jirutka.rsql.parser.ast.ComparisonOperator; import cz.jirutka.rsql.parser.ast.OrNode; +import java.util.stream.Stream; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.reflections.Reflections; +import org.springframework.orm.jpa.vendor.Database; @Slf4j @SuppressWarnings({ "rawtypes", "unchecked" }) public class RSQLJPAPredicateConverter extends RSQLVisitorBase { + private static final Set JSON_SUPPORT = EnumSet.of(Database.POSTGRESQL); + private final CriteriaBuilder builder; private final Map cachedJoins = new HashMap<>(); private final @Getter Map propertyPathMapper; @@ -134,6 +140,10 @@ RSQLJPAContext findPropertyPath(String propertyPath, Path startRoot) { String keyJoin = getKeyJoin(root, mappedProperty); log.debug("Create a element collection join between [{}] and [{}] using key [{}]", previousClass, classMetadata.getJavaType().getName(), keyJoin); root = join(keyJoin, root, mappedProperty); + } else if (isJsonType(mappedProperty, classMetadata)) { + root = root.get(mappedProperty); + attribute = classMetadata.getAttribute(mappedProperty); + break; } else { log.debug("Create property path for type [{}] property [{}]", classMetadata.getJavaType().getName(), mappedProperty); root = root.get(mappedProperty); @@ -157,6 +167,35 @@ RSQLJPAContext findPropertyPath(String propertyPath, Path startRoot) { return RSQLJPAContext.of(root, attribute, classMetadata); } + private boolean isJsonType(String mappedProperty, ManagedType classMetadata) { + return Optional.ofNullable(classMetadata.getAttribute(mappedProperty)) + .map(this::isJsonType) + .orElse(false); + } + + private boolean isJsonType(Attribute attribute) { + return isJsonColumn(attribute) && getDatabase(attribute).map(JSON_SUPPORT::contains).orElse(false); + } + + private boolean isJsonColumn(Attribute attribute) { + return Optional.ofNullable(attribute) + .filter(attr -> attr.getJavaMember() instanceof Field) + .map(attr -> ((Field) attr.getJavaMember())) + .map(field -> field.getAnnotation(Column.class)) + .map(Column::columnDefinition) + .map("jsonb"::equalsIgnoreCase) + .orElse(false); + } + + private Optional getDatabase(Attribute attribute) { + return getEntityManagerMap() + .values() + .stream() + .filter(em -> em.getMetamodel().getManagedTypes().contains(attribute.getDeclaringType())) + .findFirst() + .map(em -> getEntityManagerDatabase().get(em)); + } + private String getKeyJoin(Path root, String mappedProperty) { return root.getJavaType().getSimpleName().concat(".").concat(mappedProperty); } @@ -195,7 +234,9 @@ public Predicate visit(ComparisonNode node, From root) { return customPredicate.getConverter().apply(RSQLCustomPredicateInput.of(builder, attrPath, attribute, arguments, root)); } - Class type = attribute != null ? attribute.getJavaType() : null; + final boolean json = isJsonType(attribute); + Class type = json ? String.class : attribute != null ? attribute.getJavaType() : null; + if (attribute != null) { if (attribute.getPersistentAttributeType() == PersistentAttributeType.ELEMENT_COLLECTION) { type = getElementCollectionGenericType(type, attribute); @@ -206,6 +247,7 @@ public Predicate visit(ComparisonNode node, From root) { type = RSQLJPASupport.getValueTypeMap().get(type); // if you want to treat Enum as String and apply like search, etc } } + Expression expr = json ? getJsonExpression(attrPath, attribute, node) : attrPath; if (node.getArguments().size() > 1) { List listObject = new ArrayList<>(); @@ -213,51 +255,52 @@ public Predicate visit(ComparisonNode node, From root) { listObject.add(convert(argument, type)); } if (op.equals(IN)) { - return attrPath.in(listObject); + return expr.in(listObject); } if (op.equals(NOT_IN)) { - return attrPath.in(listObject).not(); + return expr.in(listObject).not(); } if (op.equals(BETWEEN) && listObject.size() == 2 && listObject.get(0) instanceof Comparable && listObject.get(1) instanceof Comparable) { - return builder.between(attrPath, (Comparable) listObject.get(0), (Comparable) listObject.get(1)); + return builder.between(expr, (Comparable) listObject.get(0), (Comparable) listObject.get(1)); } if (op.equals(NOT_BETWEEN) && listObject.size() == 2 && listObject.get(0) instanceof Comparable && listObject.get(1) instanceof Comparable) { - return builder.between(attrPath, (Comparable) listObject.get(0), (Comparable) listObject.get(1)).not(); + return builder.between(expr, (Comparable) listObject.get(0), (Comparable) listObject.get(1)).not(); } } else { + if (op.equals(IS_NULL)) { - return builder.isNull(attrPath); + return builder.isNull(expr); } if (op.equals(NOT_NULL)) { - return builder.isNotNull(attrPath); + return builder.isNotNull(expr); } Object argument = convert(node.getArguments().get(0), type); if (op.equals(IN)) { - return builder.equal(attrPath, argument); + return builder.equal(expr, argument); } if (op.equals(NOT_IN)) { - return builder.notEqual(attrPath, argument); + return builder.notEqual(expr, argument); } if (op.equals(LIKE)) { - return builder.like(attrPath, "%" + argument.toString() + "%"); + return builder.like(expr, "%" + argument.toString() + "%"); } if (op.equals(NOT_LIKE)) { - return builder.like(attrPath, "%" + argument.toString() + "%").not(); + return builder.like(expr, "%" + argument.toString() + "%").not(); } if (op.equals(IGNORE_CASE)) { - return builder.equal(builder.upper(attrPath), argument.toString().toUpperCase()); + return builder.equal(builder.upper(expr), argument.toString().toUpperCase()); } if (op.equals(IGNORE_CASE_LIKE)) { - return builder.like(builder.upper(attrPath), "%" + argument.toString().toUpperCase() + "%"); + return builder.like(builder.upper(expr), "%" + argument.toString().toUpperCase() + "%"); } if (op.equals(IGNORE_CASE_NOT_LIKE)) { - return builder.like(builder.upper(attrPath), "%" + argument.toString().toUpperCase() + "%").not(); + return builder.like(builder.upper(expr), "%" + argument.toString().toUpperCase() + "%").not(); } if (op.equals(EQUAL)) { - return equalPredicate(attrPath, type, argument); + return equalPredicate(expr, type, argument); } if (op.equals(NOT_EQUAL)) { - return equalPredicate(attrPath, type, argument).not(); + return equalPredicate(expr, type, argument).not(); } if (!Comparable.class.isAssignableFrom(type)) { log.error("Operator {} can be used only for Comparables", op); @@ -266,43 +309,62 @@ public Predicate visit(ComparisonNode node, From root) { Comparable comparable = (Comparable) argument; if (op.equals(GREATER_THAN)) { - return builder.greaterThan(attrPath, comparable); + return builder.greaterThan(expr, comparable); } if (op.equals(GREATER_THAN_OR_EQUAL)) { - return builder.greaterThanOrEqualTo(attrPath, comparable); + return builder.greaterThanOrEqualTo(expr, comparable); } if (op.equals(LESS_THAN)) { - return builder.lessThan(attrPath, comparable); + return builder.lessThan(expr, comparable); } if (op.equals(LESS_THAN_OR_EQUAL)) { - return builder.lessThanOrEqualTo(attrPath, comparable); + return builder.lessThanOrEqualTo(expr, comparable); } } log.error("Unknown operator: {}", op); throw new RSQLException("Unknown operator: " + op); } - private Predicate equalPredicate(Path attrPath, Class type, Object argument) { + private Expression getJsonExpression(Path path, Attribute attribute, ComparisonNode node) { + var database = getDatabase(attribute).orElse(null); + + if (database == Database.POSTGRESQL) { + var args = new ArrayList>(); + args.add(path); + + Stream.of(node.getSelector().split("\\.")) + .skip(1) // skip root + .map(builder::literal) + .map(expr -> expr.as(String.class)) + .forEach(args::add); + + return builder.function("jsonb_extract_path_text", String.class, args.toArray(Expression[]::new)); + } + + return path; + } + + private Predicate equalPredicate(Expression expr, Class type, Object argument) { if (type.equals(String.class)) { String argStr = argument.toString(); if (strictEquality) { - return builder.equal(attrPath, argument); + return builder.equal(expr, argument); } else { if (argStr.contains("*") && argStr.contains("^")) { - return builder.like(builder.upper(attrPath), argStr.replace('*', '%').replace("^", "").toUpperCase()); + return builder.like(builder.upper(expr), argStr.replace('*', '%').replace("^", "").toUpperCase()); } else if (argStr.contains("*")) { - return builder.like(attrPath, argStr.replace('*', '%')); + return builder.like(expr, argStr.replace('*', '%')); } else if (argStr.contains("^")) { - return builder.equal(builder.upper(attrPath), argStr.replace("^", "").toUpperCase()); + return builder.equal(builder.upper(expr), argStr.replace("^", "").toUpperCase()); } else { - return builder.equal(attrPath, argument); + return builder.equal(expr, argument); } } } else if (argument == null) { - return builder.isNull(attrPath); + return builder.isNull(expr); } else { - return builder.equal(attrPath, argument); + return builder.equal(expr, argument); } } diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java index 9dfd31ed..435e52ac 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java @@ -15,6 +15,7 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.lang.Nullable; +import org.springframework.orm.jpa.vendor.Database; import org.springframework.util.StringUtils; import cz.jirutka.rsql.parser.RSQLParser; @@ -31,7 +32,12 @@ public RSQLJPASupport() { } public RSQLJPASupport(Map entityManagerMap) { + this(entityManagerMap, Map.of()); + } + + public RSQLJPASupport(Map entityManagerMap, Map entityManagerDatabase) { super(entityManagerMap); + RSQLVisitorBase.setEntityManagerDatabase(entityManagerDatabase); } public static Specification rsql(final String rsqlQuery) { diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java new file mode 100644 index 00000000..0f42a842 --- /dev/null +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java @@ -0,0 +1,173 @@ +package io.github.perplexhub.rsql; + +import static io.github.perplexhub.rsql.RSQLJPASupport.toSpecification; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import io.github.perplexhub.rsql.model.PostgresJsonEntity; +import io.github.perplexhub.rsql.repository.jpa.postgres.PostgresJsonEntityRepository; +import jakarta.persistence.EntityManager; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest +@ActiveProfiles("postgres") +class RSQLJPASupportPostgresJsonTest { + + @Autowired + private PostgresJsonEntityRepository repository; + + @BeforeEach + void setup(@Autowired EntityManager em) { + RSQLVisitorBase.setEntityManagerDatabase(Map.of(em, Database.POSTGRESQL)); + } + + @AfterEach + void tearDown() { + repository.deleteAll(); + repository.flush(); + RSQLVisitorBase.setEntityManagerDatabase(Map.of()); + } + + @ParameterizedTest + @MethodSource("data") + void testJson(List users, String rsql, List expected) { + //given + repository.saveAllAndFlush(users); + + //when + List result = repository.findAll(toSpecification(rsql)); + + //then + assertThat(result) + .hasSameSizeAs(expected) + .containsExactlyInAnyOrderElementsOf(expected); + + users.forEach(e -> e.setId(null)); + } + + static Stream data() { + return Stream.of( + equalsData(), + inData(), + betweenData(), + likeData(), + gtLtData(), + miscData() + ) + .flatMap(s -> s); + } + + private static Stream equalsData() { + var e1 = new PostgresJsonEntity(Map.of("a1", "b1")); + var e2 = new PostgresJsonEntity(Map.of("a1", Map.of("a11", Map.of("a111", "b1")))); + + var e3 = new PostgresJsonEntity(e1); + var e4 = new PostgresJsonEntity(e2); + + var e5 = new PostgresJsonEntity(Map.of("a", "b1")); + var e6 = new PostgresJsonEntity(Map.of("a", "b2")); + var e7 = new PostgresJsonEntity(Map.of("a", "c1")); + + return Stream.of( + arguments(List.of(e1, e2), "properties.a1==b1", List.of(e1)), + arguments(List.of(e1, e2), "properties.a1!=b1", List.of(e2)), + arguments(List.of(e1, e2), "properties.a1=ic=B1", List.of(e1)), + arguments(List.of(e1, e2), "properties.a1==b2", List.of()), + arguments(List.of(e3, e4), "properties.a1.a11.a111==b1", List.of(e4)), + arguments(List.of(e3, e4), "properties.a1.a11.a111==b2", List.of()), + + arguments(List.of(e5, e6, e7), "properties.a==b*", List.of(e5, e6)), + arguments(List.of(e5, e6, e7), "properties.a==c*", List.of(e7)), + arguments(List.of(e5, e6, e7), "properties.a==*1", List.of(e5, e7)) + ); + } + + private static Stream inData() { + var e1 = new PostgresJsonEntity(Map.of("a", "b1")); + var e2 = new PostgresJsonEntity(Map.of("a", "b2")); + var e3 = new PostgresJsonEntity(Map.of("a", "c1")); + var e4 = new PostgresJsonEntity(Map.of("a", "d1")); + + return Stream.of( + arguments(List.of(e1, e2, e3, e4), "properties.a=in=(b1, c1)", List.of(e1, e3)), + arguments(List.of(e1, e2, e3, e4), "properties.a=out=(b1, c1)", List.of(e2, e4)), + arguments(List.of(e1, e2, e3, e4), "properties.a=in=(b1)", List.of(e1)), + arguments(List.of(e1, e2, e3, e4), "properties.a=out=(b1)", List.of(e2, e3, e4)) + ); + } + + private static Stream betweenData() { + var e1 = new PostgresJsonEntity(Map.of("a", "a")); + var e2 = new PostgresJsonEntity(Map.of("a", "b")); + var e3 = new PostgresJsonEntity(Map.of("a", "c")); + var e4 = new PostgresJsonEntity(Map.of("a", "d")); + + return Stream.of( + arguments(List.of(e1, e2, e3, e4), "properties.a=bt=(a, c)", List.of(e1, e2, e3)), + arguments(List.of(e1, e2, e3, e4), "properties.a=nb=(b, d)", List.of(e1)) + ); + } + + private static Stream likeData() { + var e1 = new PostgresJsonEntity(Map.of("a", "a b c")); + var e2 = new PostgresJsonEntity(Map.of("a", "b c d")); + var e3 = new PostgresJsonEntity(Map.of("a", "c d e")); + + return Stream.of( + arguments(List.of(e1, e2, e3), "properties.a=ke='a b'", List.of(e1)), + arguments(List.of(e1, e2, e3), "properties.a=ke='b c'", List.of(e1, e2)), + arguments(List.of(e1, e2, e3), "properties.a=ke='c d'", List.of(e2, e3)), + arguments(List.of(e1, e2, e3), "properties.a=ke='d e'", List.of(e3)), + + arguments(List.of(e1, e2, e3), "properties.a=ik='A B'", List.of(e1)), + arguments(List.of(e1, e2, e3), "properties.a=ik='B C'", List.of(e1, e2)), + arguments(List.of(e1, e2, e3), "properties.a=ik='C D'", List.of(e2, e3)), + arguments(List.of(e1, e2, e3), "properties.a=ik='D E'", List.of(e3)), + + arguments(List.of(e1, e2, e3), "properties.a=nk='a b'", List.of(e2, e3)), + arguments(List.of(e1, e2, e3), "properties.a=ni='A B'", List.of(e2, e3)) + ); + } + + private static Stream gtLtData() { + var e1 = new PostgresJsonEntity(Map.of("a", "a")); + var e2 = new PostgresJsonEntity(Map.of("a", "b")); + var e3 = new PostgresJsonEntity(Map.of("a", "c")); + var e4 = new PostgresJsonEntity(Map.of("a", "d")); + + return Stream.of( + arguments(List.of(e1, e2, e3, e4), "properties.a>=a", List.of(e1, e2, e3, e4)), + arguments(List.of(e1, e2, e3, e4), "properties.a>a", List.of(e2, e3, e4)), + arguments(List.of(e1, e2, e3, e4), "properties.a miscData() { + var e1 = new PostgresJsonEntity(Map.of("a", "b1")); + var e2 = new PostgresJsonEntity(Map.of("a", "b2")); + var e3 = new PostgresJsonEntity(Map.of("b", "c1")); + var e4 = new PostgresJsonEntity(Map.of("b", "d1")); + + return Stream.of( + arguments(List.of(e1, e2, e3, e4), "properties.a=nn=''", List.of(e1, e2)), + arguments(List.of(e1, e2, e3, e4), "properties.a=na=''", List.of(e3, e4)), + + arguments(List.of(e1, e2, e3, e4), "properties.b=nn=''", List.of(e3, e4)), + arguments(List.of(e1, e2, e3, e4), "properties.b=na=''", List.of(e1, e2)) + ); + } +} diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java new file mode 100644 index 00000000..ee0379a7 --- /dev/null +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java @@ -0,0 +1,44 @@ +package io.github.perplexhub.rsql.model; + +import io.hypersistence.utils.hibernate.type.json.JsonType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.annotations.Type; + +@Getter +@Setter +@EqualsAndHashCode(of = "id") +@ToString +@Entity +@NoArgsConstructor +public class PostgresJsonEntity { + + @Id + @GeneratedValue + private UUID id; + + @Type(JsonType.class) + @Column(columnDefinition = "jsonb") + private Map properties = new HashMap<>(); + + public PostgresJsonEntity(Map properties) { + this.properties = Objects.requireNonNull(properties); + } + + public PostgresJsonEntity(PostgresJsonEntity other) { + this(other.getProperties()); + } +} diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/repository/jpa/postgres/PostgresJsonEntityRepository.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/repository/jpa/postgres/PostgresJsonEntityRepository.java new file mode 100644 index 00000000..69606335 --- /dev/null +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/repository/jpa/postgres/PostgresJsonEntityRepository.java @@ -0,0 +1,11 @@ +package io.github.perplexhub.rsql.repository.jpa.postgres; + +import io.github.perplexhub.rsql.model.PostgresJsonEntity; +import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface PostgresJsonEntityRepository extends JpaRepository, + JpaSpecificationExecutor { + +} diff --git a/rsql-jpa/src/test/resources/application-postgres.yml b/rsql-jpa/src/test/resources/application-postgres.yml new file mode 100644 index 00000000..a457b142 --- /dev/null +++ b/rsql-jpa/src/test/resources/application-postgres.yml @@ -0,0 +1,7 @@ +spring: + datasource: + driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver + url: jdbc:tc:postgresql:12://localhost/test + jpa: + hibernate: + ddl-auto: create-drop \ No newline at end of file From ad9765916f051d9a30f97402193f5e28f2be6f59 Mon Sep 17 00:00:00 2001 From: Harinandan Chintamreddy Date: Thu, 5 Jun 2025 03:24:19 +0530 Subject: [PATCH 2/4] Fix jsonb support to work with 5.x --- pom.xml | 12 +++---- .../rsql/UnknownPropertyExceptionTest.java | 2 +- .../rsql/RSQLJPAAutoConfiguration.java | 35 ++++++++++++++++--- .../rsql/RSQLSpecificationSupportTest.java | 6 ++-- rsql-jpa/pom.xml | 4 +-- .../rsql/RSQLJPAPredicateConverter.java | 2 +- .../rsql/RSQLJPASupportPostgresJsonTest.java | 9 +++-- .../rsql/model/PostgresJsonEntity.java | 12 ++++--- .../rsql/RSQLQueryDslSupportTest.java | 6 ++-- .../rsql/RSQLQueryDslSupportTest.java | 7 ++-- 10 files changed, 60 insertions(+), 35 deletions(-) diff --git a/pom.xml b/pom.xml index 68165cfb..eda67edc 100644 --- a/pom.xml +++ b/pom.xml @@ -20,11 +20,11 @@ - 1.8 - 1.8 + 11 + 11 UTF-8 - 2.0.0.RELEASE - Kay-RELEASE + 2.7.18 + 2021.2.18 4.1.4 2.1.0 1.18.10 @@ -54,8 +54,8 @@ org.springframework.data - spring-data-releasetrain - ${spring-data-releasetrain.version} + spring-data-bom + ${spring-data.version} pom import diff --git a/rsql-common/src/test/java/io/github/perplexhub/rsql/UnknownPropertyExceptionTest.java b/rsql-common/src/test/java/io/github/perplexhub/rsql/UnknownPropertyExceptionTest.java index cf4c598f..ed0727a5 100644 --- a/rsql-common/src/test/java/io/github/perplexhub/rsql/UnknownPropertyExceptionTest.java +++ b/rsql-common/src/test/java/io/github/perplexhub/rsql/UnknownPropertyExceptionTest.java @@ -3,7 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import io.github.perplexhub.rsql.model.User; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class UnknownPropertyExceptionTest { diff --git a/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java b/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java index 29f7ba2c..1825e784 100644 --- a/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java +++ b/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java @@ -4,7 +4,7 @@ import static java.util.stream.Collectors.toMap; import io.github.perplexhub.rsql.RSQLJPAAutoConfiguration.HibernateEntityManagerDatabaseConfiguration; -import jakarta.persistence.EntityManager; +import javax.persistence.EntityManager; import java.util.IdentityHashMap; import java.util.Map; import java.util.Map.Entry; @@ -13,7 +13,6 @@ import lombok.extern.slf4j.Slf4j; import org.hibernate.Session; import org.hibernate.dialect.AbstractHANADialect; -import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; @@ -67,12 +66,12 @@ public EntityManagerDatabase entityManagerDatabase(ObjectProvider .filter(Objects::nonNull) .collect(collectingAndThen( toMap(Entry::getKey, Entry::getValue, (db1, db2) -> db1, IdentityHashMap::new), - EntityManagerDatabase::new + m -> new EntityManagerDatabase(m) )); } private Database toDatabase(Dialect dialect) { - if (dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect) { + if (dialect instanceof PostgreSQLDialect) { return Database.POSTGRESQL; } else if (dialect instanceof MySQLDialect) { return Database.MYSQL; @@ -98,7 +97,33 @@ private Database toDatabase(Dialect dialect) { } } - record EntityManagerDatabase(Map value) { + public static final class EntityManagerDatabase { + private final Map value; + public EntityManagerDatabase(Map value) { + this.value = value; + } + + public Map value() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + EntityManagerDatabase that = (EntityManagerDatabase) obj; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "EntityManagerDatabase[value=" + value + "]"; + } } } diff --git a/rsql-jpa-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLSpecificationSupportTest.java b/rsql-jpa-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLSpecificationSupportTest.java index 8269d571..4bf75bb7 100644 --- a/rsql-jpa-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLSpecificationSupportTest.java +++ b/rsql-jpa-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLSpecificationSupportTest.java @@ -2,12 +2,11 @@ import static io.github.perplexhub.rsql.RSQLJPASupport.*; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -20,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; @Slf4j -@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.NONE) public class RSQLSpecificationSupportTest { diff --git a/rsql-jpa/pom.xml b/rsql-jpa/pom.xml index ef99d05b..ebd61eee 100644 --- a/rsql-jpa/pom.xml +++ b/rsql-jpa/pom.xml @@ -54,8 +54,8 @@ io.hypersistence - hypersistence-utils-hibernate-60 - 3.5.2 + hypersistence-utils-hibernate-55 + 3.9.5 test diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java index 43cc5d74..1a80af52 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java @@ -2,7 +2,7 @@ import static io.github.perplexhub.rsql.RSQLOperators.*; -import jakarta.persistence.Column; +import javax.persistence.Column; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java index 0f42a842..cdbf7747 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java @@ -6,7 +6,7 @@ import io.github.perplexhub.rsql.model.PostgresJsonEntity; import io.github.perplexhub.rsql.repository.jpa.postgres.PostgresJsonEntityRepository; -import jakarta.persistence.EntityManager; +import javax.persistence.EntityManager; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -26,9 +26,11 @@ class RSQLJPASupportPostgresJsonTest { @Autowired private PostgresJsonEntityRepository repository; + @Autowired + private EntityManager em; @BeforeEach - void setup(@Autowired EntityManager em) { + void setup() { RSQLVisitorBase.setEntityManagerDatabase(Map.of(em, Database.POSTGRESQL)); } @@ -43,7 +45,8 @@ void tearDown() { @MethodSource("data") void testJson(List users, String rsql, List expected) { //given - repository.saveAllAndFlush(users); + repository.saveAll(users); + repository.flush(); //when List result = repository.findAll(toSpecification(rsql)); diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java index ee0379a7..4bfced1c 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java @@ -1,10 +1,10 @@ package io.github.perplexhub.rsql.model; import io.hypersistence.utils.hibernate.type.json.JsonType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -17,6 +17,7 @@ import lombok.Setter; import lombok.ToString; import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; @Getter @Setter @@ -24,13 +25,14 @@ @ToString @Entity @NoArgsConstructor +@TypeDef(name="jsonb", typeClass=JsonType.class) public class PostgresJsonEntity { @Id @GeneratedValue private UUID id; - @Type(JsonType.class) + @Type(type="jsonb") @Column(columnDefinition = "jsonb") private Map properties = new HashMap<>(); diff --git a/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java b/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java index 1690f9f7..258af206 100644 --- a/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java +++ b/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java @@ -3,12 +3,11 @@ import static io.github.perplexhub.rsql.RSQLJPASupport.*; import static io.github.perplexhub.rsql.RSQLQueryDslSupport.*; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -23,7 +22,6 @@ import lombok.extern.slf4j.Slf4j; @Slf4j -@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.NONE) public class RSQLQueryDslSupportTest { diff --git a/rsql-querydsl/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java b/rsql-querydsl/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java index a3d0d24f..d3d48014 100644 --- a/rsql-querydsl/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java +++ b/rsql-querydsl/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java @@ -4,14 +4,14 @@ import static io.github.perplexhub.rsql.RSQLQueryDslSupport.*; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -25,7 +25,6 @@ import lombok.extern.slf4j.Slf4j; @Slf4j -@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.NONE) public class RSQLQueryDslSupportTest { From 1aba52beba79bac99070161987fef532fb86e313 Mon Sep 17 00:00:00 2001 From: Harinandan Chintamreddy Date: Sun, 8 Jun 2025 21:28:43 +0530 Subject: [PATCH 3/4] Revert the java update from 1.8 to 11 --- pom.xml | 4 +- .../rsql/RSQLJPAAutoConfiguration.java | 7 +-- .../rsql/RSQLJPAPredicateConverter.java | 4 +- .../rsql/RSQLJPASupportPostgresJsonTest.java | 52 +++++++++---------- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/pom.xml b/pom.xml index eda67edc..6514c558 100644 --- a/pom.xml +++ b/pom.xml @@ -20,8 +20,8 @@ - 11 - 11 + 1.8 + 1.8 UTF-8 2.7.18 2021.2.18 diff --git a/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java b/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java index 1825e784..51f0cc3a 100644 --- a/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java +++ b/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java @@ -12,6 +12,7 @@ import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.hibernate.Session; +import org.hibernate.SessionFactory; import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DerbyDialect; @@ -42,7 +43,7 @@ public class RSQLJPAAutoConfiguration { public RSQLCommonSupport rsqlCommonSupport(Map entityManagerMap, ObjectProvider entityManagerDatabaseProvider) { log.info("RSQLJPAAutoConfiguration.rsqlCommonSupport(entityManagerMap:{})", entityManagerMap.size()); - var entityManagerDatabase = entityManagerDatabaseProvider.getIfAvailable(() -> new EntityManagerDatabase(Map.of())); + EntityManagerDatabase entityManagerDatabase = entityManagerDatabaseProvider.getIfAvailable(() -> new EntityManagerDatabase(Map.of())); return new RSQLJPASupport(entityManagerMap, entityManagerDatabase.value()); } @@ -56,8 +57,8 @@ class HibernateEntityManagerDatabaseConfiguration { public EntityManagerDatabase entityManagerDatabase(ObjectProvider entityManagers) { return entityManagers.stream() .map(entityManager -> { - var sessionFactory = entityManager.unwrap(Session.class).getSessionFactory(); - var dialect = ((SessionFactoryImpl) sessionFactory).getJdbcServices().getDialect(); + SessionFactory sessionFactory = entityManager.unwrap(Session.class).getSessionFactory(); + Dialect dialect = ((SessionFactoryImpl) sessionFactory).getJdbcServices().getDialect(); return Optional.ofNullable(toDatabase(dialect)) .map(db -> Map.entry(entityManager, db)) diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java index 1a80af52..d7228e37 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java @@ -326,10 +326,10 @@ public Predicate visit(ComparisonNode node, From root) { } private Expression getJsonExpression(Path path, Attribute attribute, ComparisonNode node) { - var database = getDatabase(attribute).orElse(null); + Database database = getDatabase(attribute).orElse(null); if (database == Database.POSTGRESQL) { - var args = new ArrayList>(); + List> args = new ArrayList>(); args.add(path); Stream.of(node.getSelector().split("\\.")) diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java index cdbf7747..d1dc4399 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java @@ -72,15 +72,15 @@ static Stream data() { } private static Stream equalsData() { - var e1 = new PostgresJsonEntity(Map.of("a1", "b1")); - var e2 = new PostgresJsonEntity(Map.of("a1", Map.of("a11", Map.of("a111", "b1")))); + PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a1", "b1")); + PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a1", Map.of("a11", Map.of("a111", "b1")))); - var e3 = new PostgresJsonEntity(e1); - var e4 = new PostgresJsonEntity(e2); + PostgresJsonEntity e3 = new PostgresJsonEntity(e1); + PostgresJsonEntity e4 = new PostgresJsonEntity(e2); - var e5 = new PostgresJsonEntity(Map.of("a", "b1")); - var e6 = new PostgresJsonEntity(Map.of("a", "b2")); - var e7 = new PostgresJsonEntity(Map.of("a", "c1")); + PostgresJsonEntity e5 = new PostgresJsonEntity(Map.of("a", "b1")); + PostgresJsonEntity e6 = new PostgresJsonEntity(Map.of("a", "b2")); + PostgresJsonEntity e7 = new PostgresJsonEntity(Map.of("a", "c1")); return Stream.of( arguments(List.of(e1, e2), "properties.a1==b1", List.of(e1)), @@ -97,10 +97,10 @@ private static Stream equalsData() { } private static Stream inData() { - var e1 = new PostgresJsonEntity(Map.of("a", "b1")); - var e2 = new PostgresJsonEntity(Map.of("a", "b2")); - var e3 = new PostgresJsonEntity(Map.of("a", "c1")); - var e4 = new PostgresJsonEntity(Map.of("a", "d1")); + PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "b1")); + PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b2")); + PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("a", "c1")); + PostgresJsonEntity e4 = new PostgresJsonEntity(Map.of("a", "d1")); return Stream.of( arguments(List.of(e1, e2, e3, e4), "properties.a=in=(b1, c1)", List.of(e1, e3)), @@ -111,10 +111,10 @@ private static Stream inData() { } private static Stream betweenData() { - var e1 = new PostgresJsonEntity(Map.of("a", "a")); - var e2 = new PostgresJsonEntity(Map.of("a", "b")); - var e3 = new PostgresJsonEntity(Map.of("a", "c")); - var e4 = new PostgresJsonEntity(Map.of("a", "d")); + PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "a")); + PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b")); + PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("a", "c")); + PostgresJsonEntity e4 = new PostgresJsonEntity(Map.of("a", "d")); return Stream.of( arguments(List.of(e1, e2, e3, e4), "properties.a=bt=(a, c)", List.of(e1, e2, e3)), @@ -123,9 +123,9 @@ private static Stream betweenData() { } private static Stream likeData() { - var e1 = new PostgresJsonEntity(Map.of("a", "a b c")); - var e2 = new PostgresJsonEntity(Map.of("a", "b c d")); - var e3 = new PostgresJsonEntity(Map.of("a", "c d e")); + PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "a b c")); + PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b c d")); + PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("a", "c d e")); return Stream.of( arguments(List.of(e1, e2, e3), "properties.a=ke='a b'", List.of(e1)), @@ -144,10 +144,10 @@ private static Stream likeData() { } private static Stream gtLtData() { - var e1 = new PostgresJsonEntity(Map.of("a", "a")); - var e2 = new PostgresJsonEntity(Map.of("a", "b")); - var e3 = new PostgresJsonEntity(Map.of("a", "c")); - var e4 = new PostgresJsonEntity(Map.of("a", "d")); + PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "a")); + PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b")); + PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("a", "c")); + PostgresJsonEntity e4 = new PostgresJsonEntity(Map.of("a", "d")); return Stream.of( arguments(List.of(e1, e2, e3, e4), "properties.a>=a", List.of(e1, e2, e3, e4)), @@ -160,10 +160,10 @@ private static Stream gtLtData() { } private static Stream miscData() { - var e1 = new PostgresJsonEntity(Map.of("a", "b1")); - var e2 = new PostgresJsonEntity(Map.of("a", "b2")); - var e3 = new PostgresJsonEntity(Map.of("b", "c1")); - var e4 = new PostgresJsonEntity(Map.of("b", "d1")); + PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "b1")); + PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b2")); + PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("b", "c1")); + PostgresJsonEntity e4 = new PostgresJsonEntity(Map.of("b", "d1")); return Stream.of( arguments(List.of(e1, e2, e3, e4), "properties.a=nn=''", List.of(e1, e2)), From 5a33e631d132c123ea49e9f350538e4a1d4a7638 Mon Sep 17 00:00:00 2001 From: Harinandan Chintamreddy Date: Mon, 9 Jun 2025 00:30:23 +0530 Subject: [PATCH 4/4] Revert the sprint boot version changes and junit4 to junit5 test changes --- pom.xml | 8 +- .../perplexhub/rsql/RSQLVisitorBase.java | 3 +- .../rsql/UnknownPropertyExceptionTest.java | 2 +- .../rsql/RSQLJPAAutoConfiguration.java | 38 +-- .../rsql/RSQLSpecificationSupportTest.java | 6 +- rsql-jpa/pom.xml | 15 +- .../rsql/RSQLJPAPredicateConverter.java | 4 +- .../perplexhub/rsql/RSQLJPASupport.java | 2 +- .../rsql/RSQLJPASupportPostgresJsonTest.java | 323 ++++++++++++------ .../perplexhub/rsql/RSQLJPASupportTest.java | 1 - .../rsql/model/PostgresJsonEntity.java | 2 - .../rsql/RSQLQueryDslSupportTest.java | 7 +- .../rsql/RSQLQueryDslSupportTest.java | 11 +- 13 files changed, 261 insertions(+), 161 deletions(-) diff --git a/pom.xml b/pom.xml index 6514c558..68165cfb 100644 --- a/pom.xml +++ b/pom.xml @@ -23,8 +23,8 @@ 1.8 1.8 UTF-8 - 2.7.18 - 2021.2.18 + 2.0.0.RELEASE + Kay-RELEASE 4.1.4 2.1.0 1.18.10 @@ -54,8 +54,8 @@ org.springframework.data - spring-data-bom - ${spring-data.version} + spring-data-releasetrain + ${spring-data-releasetrain.version} pom import diff --git a/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java b/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java index 413e4ce7..8391b7d9 100644 --- a/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java +++ b/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java @@ -13,7 +13,6 @@ import javax.persistence.metamodel.PluralAttribute; import lombok.Getter; -import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.orm.jpa.vendor.Database; import org.springframework.util.StringUtils; @@ -29,7 +28,7 @@ public abstract class RSQLVisitorBase implements RSQLVisitor { protected static volatile @Setter Map managedTypeMap; protected static volatile @Setter Map entityManagerMap; - protected static volatile @Setter @Getter Map entityManagerDatabase = Map.of(); + protected static volatile @Setter @Getter Map entityManagerDatabase = new HashMap(); protected static final Map primitiveToWrapper; protected static volatile @Setter Map, Map> propertyRemapping; protected static volatile @Setter Map, List> globalPropertyWhitelist; diff --git a/rsql-common/src/test/java/io/github/perplexhub/rsql/UnknownPropertyExceptionTest.java b/rsql-common/src/test/java/io/github/perplexhub/rsql/UnknownPropertyExceptionTest.java index ed0727a5..cf4c598f 100644 --- a/rsql-common/src/test/java/io/github/perplexhub/rsql/UnknownPropertyExceptionTest.java +++ b/rsql-common/src/test/java/io/github/perplexhub/rsql/UnknownPropertyExceptionTest.java @@ -3,7 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import io.github.perplexhub.rsql.model.User; -import org.junit.jupiter.api.Test; +import org.junit.Test; public class UnknownPropertyExceptionTest { diff --git a/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java b/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java index 51f0cc3a..644883ea 100644 --- a/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java +++ b/rsql-jpa-spring-boot-starter/src/main/java/io/github/perplexhub/rsql/RSQLJPAAutoConfiguration.java @@ -1,19 +1,14 @@ package io.github.perplexhub.rsql; -import static java.util.stream.Collectors.collectingAndThen; -import static java.util.stream.Collectors.toMap; import io.github.perplexhub.rsql.RSQLJPAAutoConfiguration.HibernateEntityManagerDatabaseConfiguration; import javax.persistence.EntityManager; -import java.util.IdentityHashMap; +import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; -import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; @@ -32,6 +27,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.orm.jpa.vendor.Database; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Configuration @@ -43,7 +39,7 @@ public class RSQLJPAAutoConfiguration { public RSQLCommonSupport rsqlCommonSupport(Map entityManagerMap, ObjectProvider entityManagerDatabaseProvider) { log.info("RSQLJPAAutoConfiguration.rsqlCommonSupport(entityManagerMap:{})", entityManagerMap.size()); - EntityManagerDatabase entityManagerDatabase = entityManagerDatabaseProvider.getIfAvailable(() -> new EntityManagerDatabase(Map.of())); + EntityManagerDatabase entityManagerDatabase = entityManagerDatabaseProvider.getIfAvailable(() -> new EntityManagerDatabase(new HashMap())); return new RSQLJPASupport(entityManagerMap, entityManagerDatabase.value()); } @@ -53,22 +49,20 @@ public RSQLCommonSupport rsqlCommonSupport(Map entityMana static class HibernateEntityManagerDatabaseConfiguration { + @Transactional @Bean public EntityManagerDatabase entityManagerDatabase(ObjectProvider entityManagers) { - return entityManagers.stream() - .map(entityManager -> { - SessionFactory sessionFactory = entityManager.unwrap(Session.class).getSessionFactory(); - Dialect dialect = ((SessionFactoryImpl) sessionFactory).getJdbcServices().getDialect(); - - return Optional.ofNullable(toDatabase(dialect)) - .map(db -> Map.entry(entityManager, db)) - .orElse(null); - }) - .filter(Objects::nonNull) - .collect(collectingAndThen( - toMap(Entry::getKey, Entry::getValue, (db1, db2) -> db1, IdentityHashMap::new), - m -> new EntityManagerDatabase(m) - )); + Map value = new HashMap<>(); + EntityManager entityManager = entityManagers.getIfAvailable(); + SessionFactory sessionFactory = entityManager.unwrap(Session.class).getSessionFactory(); + Dialect dialect = ((SessionFactoryImpl) sessionFactory).getJdbcServices().getDialect(); + + Database db = toDatabase(dialect); + if (db != null) { + value.put(entityManager, db); + } + + return new EntityManagerDatabase(value); } private Database toDatabase(Dialect dialect) { @@ -86,8 +80,6 @@ private Database toDatabase(Dialect dialect) { return Database.DB2; } else if (dialect instanceof H2Dialect) { return Database.H2; - } else if (dialect instanceof AbstractHANADialect) { - return Database.HANA; } else if (dialect instanceof HSQLDialect) { return Database.HSQL; } else if (dialect instanceof SybaseDialect) { diff --git a/rsql-jpa-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLSpecificationSupportTest.java b/rsql-jpa-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLSpecificationSupportTest.java index 4bf75bb7..8269d571 100644 --- a/rsql-jpa-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLSpecificationSupportTest.java +++ b/rsql-jpa-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLSpecificationSupportTest.java @@ -2,11 +2,12 @@ import static io.github.perplexhub.rsql.RSQLJPASupport.*; import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.*; import java.util.List; -import org.junit.jupiter.api.Test; +import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -19,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j +@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.NONE) public class RSQLSpecificationSupportTest { diff --git a/rsql-jpa/pom.xml b/rsql-jpa/pom.xml index ebd61eee..dd804dd0 100644 --- a/rsql-jpa/pom.xml +++ b/rsql-jpa/pom.xml @@ -52,10 +52,21 @@ 1.19.0 test + + org.hibernate.common + hibernate-commons-annotations + 5.1.2.Final + io.hypersistence - hypersistence-utils-hibernate-55 - 3.9.5 + hypersistence-utils-hibernate-52 + 3.7.6 + test + + + net.java.dev.jna + jna + 5.14.0 test diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java index d7228e37..be73a97f 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java @@ -141,7 +141,7 @@ RSQLJPAContext findPropertyPath(String propertyPath, Path startRoot) { log.debug("Create a element collection join between [{}] and [{}] using key [{}]", previousClass, classMetadata.getJavaType().getName(), keyJoin); root = join(keyJoin, root, mappedProperty); } else if (isJsonType(mappedProperty, classMetadata)) { - root = root.get(mappedProperty); + root = root.get(mappedProperty); attribute = classMetadata.getAttribute(mappedProperty); break; } else { @@ -338,7 +338,7 @@ private Expression getJsonExpression(Path path, Attribute attribute, Compa .map(expr -> expr.as(String.class)) .forEach(args::add); - return builder.function("jsonb_extract_path_text", String.class, args.toArray(Expression[]::new)); + return builder.function("jsonb_extract_path_text", String.class, args.toArray(new Expression[0])); } return path; diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java index 435e52ac..6f1d8d3b 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java @@ -32,7 +32,7 @@ public RSQLJPASupport() { } public RSQLJPASupport(Map entityManagerMap) { - this(entityManagerMap, Map.of()); + this(entityManagerMap, new HashMap<>()); } public RSQLJPASupport(Map entityManagerMap, Map entityManagerDatabase) { diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java index d1dc4399..c0a5c569 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java @@ -2,48 +2,72 @@ import static io.github.perplexhub.rsql.RSQLJPASupport.toSpecification; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.params.provider.Arguments.arguments; import io.github.perplexhub.rsql.model.PostgresJsonEntity; import io.github.perplexhub.rsql.repository.jpa.postgres.PostgresJsonEntityRepository; import javax.persistence.EntityManager; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Stream; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.orm.jpa.vendor.Database; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.rules.SpringClassRule; +import org.springframework.test.context.junit4.rules.SpringMethodRule; @SpringBootTest @ActiveProfiles("postgres") -class RSQLJPASupportPostgresJsonTest { +@RunWith(Parameterized.class) +public class RSQLJPASupportPostgresJsonTest { + @ClassRule + public static final SpringClassRule scr = new SpringClassRule(); + + @Rule + public final SpringMethodRule smr = new SpringMethodRule(); @Autowired private PostgresJsonEntityRepository repository; @Autowired private EntityManager em; + + private List users; + private String rsql; + private List expected; + + public RSQLJPASupportPostgresJsonTest(List users, String rsql, List expected) { + this.users = users; + this.rsql = rsql; + this.expected = expected; + } - @BeforeEach - void setup() { - RSQLVisitorBase.setEntityManagerDatabase(Map.of(em, Database.POSTGRESQL)); + @Before + public void setup() { + Map map = new HashMap<>(); + map.put(em, Database.POSTGRESQL); + RSQLVisitorBase.setEntityManagerDatabase(map); } - @AfterEach - void tearDown() { + @After + public void tearDown() { repository.deleteAll(); repository.flush(); - RSQLVisitorBase.setEntityManagerDatabase(Map.of()); + RSQLVisitorBase.setEntityManagerDatabase(new HashMap<>()); } - @ParameterizedTest - @MethodSource("data") - void testJson(List users, String rsql, List expected) { + @Test + public void testJson() { //given repository.saveAll(users); repository.flush(); @@ -59,118 +83,193 @@ void testJson(List users, String rsql, List e.setId(null)); } - static Stream data() { - return Stream.of( - equalsData(), - inData(), - betweenData(), - likeData(), - gtLtData(), - miscData() - ) - .flatMap(s -> s); + @Parameters + public static Collection data() { + List data = new ArrayList<>(); + data.addAll(equalsData()); + data.addAll(inData()); + data.addAll(betweenData()); + data.addAll(likeData()); + data.addAll(gtLtData()); + data.addAll(miscData()); + return data; } - private static Stream equalsData() { - PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a1", "b1")); - PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a1", Map.of("a11", Map.of("a111", "b1")))); + private static Collection equalsData() { + Map map1 = new HashMap<>(); + map1.put("a1", "b1"); + PostgresJsonEntity e1 = new PostgresJsonEntity(map1); + + Map innerMap = new HashMap<>(); + Map innerInnerMap = new HashMap<>(); + innerInnerMap.put("a111", "b1"); + innerMap.put("a11", innerInnerMap); + Map map2 = new HashMap<>(); + map2.put("a1", innerMap); + PostgresJsonEntity e2 = new PostgresJsonEntity(map2); PostgresJsonEntity e3 = new PostgresJsonEntity(e1); PostgresJsonEntity e4 = new PostgresJsonEntity(e2); - PostgresJsonEntity e5 = new PostgresJsonEntity(Map.of("a", "b1")); - PostgresJsonEntity e6 = new PostgresJsonEntity(Map.of("a", "b2")); - PostgresJsonEntity e7 = new PostgresJsonEntity(Map.of("a", "c1")); - - return Stream.of( - arguments(List.of(e1, e2), "properties.a1==b1", List.of(e1)), - arguments(List.of(e1, e2), "properties.a1!=b1", List.of(e2)), - arguments(List.of(e1, e2), "properties.a1=ic=B1", List.of(e1)), - arguments(List.of(e1, e2), "properties.a1==b2", List.of()), - arguments(List.of(e3, e4), "properties.a1.a11.a111==b1", List.of(e4)), - arguments(List.of(e3, e4), "properties.a1.a11.a111==b2", List.of()), - - arguments(List.of(e5, e6, e7), "properties.a==b*", List.of(e5, e6)), - arguments(List.of(e5, e6, e7), "properties.a==c*", List.of(e7)), - arguments(List.of(e5, e6, e7), "properties.a==*1", List.of(e5, e7)) - ); + Map map5 = new HashMap<>(); + map5.put("a", "b1"); + PostgresJsonEntity e5 = new PostgresJsonEntity(map5); + + Map map6 = new HashMap<>(); + map6.put("a", "b2"); + PostgresJsonEntity e6 = new PostgresJsonEntity(map6); + + Map map7 = new HashMap<>(); + map7.put("a", "c1"); + PostgresJsonEntity e7 = new PostgresJsonEntity(map7); + + List data = new ArrayList<>(); + data.add(new Object[] {Arrays.asList(e1, e2), "properties.a1==b1", Arrays.asList(e1)}); + data.add(new Object[] {Arrays.asList(e1, e2), "properties.a1!=b1", Arrays.asList(e2)}); + data.add(new Object[] {Arrays.asList(e1, e2), "properties.a1=ic=B1", Arrays.asList(e1)}); + data.add(new Object[] {Arrays.asList(e1, e2), "properties.a1==b2", Arrays.asList()}); + data.add(new Object[] {Arrays.asList(e3, e4), "properties.a1.a11.a111==b1", Arrays.asList(e4)}); + data.add(new Object[] {Arrays.asList(e3, e4), "properties.a1.a11.a111==b2", Arrays.asList()}); + + data.add(new Object[] {Arrays.asList(e5, e6, e7), "properties.a==b*", Arrays.asList(e5, e6)}); + data.add(new Object[] {Arrays.asList(e5, e6, e7), "properties.a==c*", Arrays.asList(e7)}); + data.add(new Object[] {Arrays.asList(e5, e6, e7), "properties.a==*1", Arrays.asList(e5, e7)}); + + return data; } - private static Stream inData() { - PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "b1")); - PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b2")); - PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("a", "c1")); - PostgresJsonEntity e4 = new PostgresJsonEntity(Map.of("a", "d1")); - - return Stream.of( - arguments(List.of(e1, e2, e3, e4), "properties.a=in=(b1, c1)", List.of(e1, e3)), - arguments(List.of(e1, e2, e3, e4), "properties.a=out=(b1, c1)", List.of(e2, e4)), - arguments(List.of(e1, e2, e3, e4), "properties.a=in=(b1)", List.of(e1)), - arguments(List.of(e1, e2, e3, e4), "properties.a=out=(b1)", List.of(e2, e3, e4)) - ); + private static Collection inData() { + Map map1 = new HashMap<>(); + map1.put("a", "b1"); + PostgresJsonEntity e1 = new PostgresJsonEntity(map1); + + Map map2 = new HashMap<>(); + map2.put("a", "b2"); + PostgresJsonEntity e2 = new PostgresJsonEntity(map2); + + Map map3 = new HashMap<>(); + map3.put("a", "c1"); + PostgresJsonEntity e3 = new PostgresJsonEntity(map3); + + Map map4 = new HashMap<>(); + map4.put("a", "d1"); + PostgresJsonEntity e4 = new PostgresJsonEntity(map4); + + List data = new ArrayList<>(); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a=in=(b1, c1)", Arrays.asList(e1, e3)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a=out=(b1, c1)", Arrays.asList(e2, e4)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a=in=(b1)", Arrays.asList(e1)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a=out=(b1)", Arrays.asList(e2, e3, e4)}); + + return data; } - private static Stream betweenData() { - PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "a")); - PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b")); - PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("a", "c")); - PostgresJsonEntity e4 = new PostgresJsonEntity(Map.of("a", "d")); + private static Collection betweenData() { + Map map1 = new HashMap<>(); + map1.put("a", "a"); + PostgresJsonEntity e1 = new PostgresJsonEntity(map1); + + Map map2 = new HashMap<>(); + map2.put("a", "b"); + PostgresJsonEntity e2 = new PostgresJsonEntity(map2); + + Map map3 = new HashMap<>(); + map3.put("a", "c"); + PostgresJsonEntity e3 = new PostgresJsonEntity(map3); + + Map map4 = new HashMap<>(); + map4.put("a", "d"); + PostgresJsonEntity e4 = new PostgresJsonEntity(map4); - return Stream.of( - arguments(List.of(e1, e2, e3, e4), "properties.a=bt=(a, c)", List.of(e1, e2, e3)), - arguments(List.of(e1, e2, e3, e4), "properties.a=nb=(b, d)", List.of(e1)) - ); + List data = new ArrayList<>(); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a=bt=(a, c)", Arrays.asList(e1, e2, e3)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a=nb=(b, d)", Arrays.asList(e1)}); + + return data; } - private static Stream likeData() { - PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "a b c")); - PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b c d")); - PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("a", "c d e")); - - return Stream.of( - arguments(List.of(e1, e2, e3), "properties.a=ke='a b'", List.of(e1)), - arguments(List.of(e1, e2, e3), "properties.a=ke='b c'", List.of(e1, e2)), - arguments(List.of(e1, e2, e3), "properties.a=ke='c d'", List.of(e2, e3)), - arguments(List.of(e1, e2, e3), "properties.a=ke='d e'", List.of(e3)), - - arguments(List.of(e1, e2, e3), "properties.a=ik='A B'", List.of(e1)), - arguments(List.of(e1, e2, e3), "properties.a=ik='B C'", List.of(e1, e2)), - arguments(List.of(e1, e2, e3), "properties.a=ik='C D'", List.of(e2, e3)), - arguments(List.of(e1, e2, e3), "properties.a=ik='D E'", List.of(e3)), - - arguments(List.of(e1, e2, e3), "properties.a=nk='a b'", List.of(e2, e3)), - arguments(List.of(e1, e2, e3), "properties.a=ni='A B'", List.of(e2, e3)) - ); + private static Collection likeData() { + Map map1 = new HashMap<>(); + map1.put("a", "a b c"); + PostgresJsonEntity e1 = new PostgresJsonEntity(map1); + + Map map2 = new HashMap<>(); + map2.put("a", "b c d"); + PostgresJsonEntity e2 = new PostgresJsonEntity(map2); + + Map map3 = new HashMap<>(); + map3.put("a", "c d e"); + PostgresJsonEntity e3 = new PostgresJsonEntity(map3); + + List data = new ArrayList<>(); + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ke='a b'", Arrays.asList(e1)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ke='b c'", Arrays.asList(e1, e2)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ke='c d'", Arrays.asList(e2, e3)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ke='d e'", Arrays.asList(e3)}); + + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ik='A B'", Arrays.asList(e1)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ik='B C'", Arrays.asList(e1, e2)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ik='C D'", Arrays.asList(e2, e3)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ik='D E'", Arrays.asList(e3)}); + + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=nk='a b'", Arrays.asList(e2, e3)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3), "properties.a=ni='A B'", Arrays.asList(e2, e3)}); + + return data; } - private static Stream gtLtData() { - PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "a")); - PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b")); - PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("a", "c")); - PostgresJsonEntity e4 = new PostgresJsonEntity(Map.of("a", "d")); - - return Stream.of( - arguments(List.of(e1, e2, e3, e4), "properties.a>=a", List.of(e1, e2, e3, e4)), - arguments(List.of(e1, e2, e3, e4), "properties.a>a", List.of(e2, e3, e4)), - arguments(List.of(e1, e2, e3, e4), "properties.a gtLtData() { + Map map1 = new HashMap<>(); + map1.put("a", "a"); + PostgresJsonEntity e1 = new PostgresJsonEntity(map1); + + Map map2 = new HashMap<>(); + map2.put("a", "b"); + PostgresJsonEntity e2 = new PostgresJsonEntity(map2); + + Map map3 = new HashMap<>(); + map3.put("a", "c"); + PostgresJsonEntity e3 = new PostgresJsonEntity(map3); + + Map map4 = new HashMap<>(); + map4.put("a", "d"); + PostgresJsonEntity e4 = new PostgresJsonEntity(map4); + + List data = new ArrayList<>(); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a>=a", Arrays.asList(e1, e2, e3, e4)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a>a", Arrays.asList(e2, e3, e4)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a miscData() { - PostgresJsonEntity e1 = new PostgresJsonEntity(Map.of("a", "b1")); - PostgresJsonEntity e2 = new PostgresJsonEntity(Map.of("a", "b2")); - PostgresJsonEntity e3 = new PostgresJsonEntity(Map.of("b", "c1")); - PostgresJsonEntity e4 = new PostgresJsonEntity(Map.of("b", "d1")); + private static Collection miscData() { + Map map1 = new HashMap<>(); + map1.put("a", "b1"); + PostgresJsonEntity e1 = new PostgresJsonEntity(map1); + + Map map2 = new HashMap<>(); + map2.put("a", "b2"); + PostgresJsonEntity e2 = new PostgresJsonEntity(map2); + + Map map3 = new HashMap<>(); + map3.put("b", "c1"); + PostgresJsonEntity e3 = new PostgresJsonEntity(map3); + + Map map4 = new HashMap<>(); + map4.put("b", "d1"); + PostgresJsonEntity e4 = new PostgresJsonEntity(map4); - return Stream.of( - arguments(List.of(e1, e2, e3, e4), "properties.a=nn=''", List.of(e1, e2)), - arguments(List.of(e1, e2, e3, e4), "properties.a=na=''", List.of(e3, e4)), + List data = new ArrayList<>(); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a=nn=''", Arrays.asList(e1, e2)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.a=na=''", Arrays.asList(e3, e4)}); - arguments(List.of(e1, e2, e3, e4), "properties.b=nn=''", List.of(e3, e4)), - arguments(List.of(e1, e2, e3, e4), "properties.b=na=''", List.of(e1, e2)) - ); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.b=nn=''", Arrays.asList(e3, e4)}); + data.add(new Object[] {Arrays.asList(e1, e2, e3, e4), "properties.b=na=''", Arrays.asList(e1, e2)}); + + return data; } -} +} \ No newline at end of file diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java index 59f8d21f..bdb26e72 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java @@ -1,6 +1,5 @@ package io.github.perplexhub.rsql; -import static io.github.perplexhub.rsql.RSQLCommonSupport.*; import static io.github.perplexhub.rsql.RSQLJPASupport.*; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.hamcrest.CoreMatchers.*; diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java index 4bfced1c..2e01e710 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/model/PostgresJsonEntity.java @@ -5,9 +5,7 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; diff --git a/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java b/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java index 258af206..dbb86cc9 100644 --- a/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java +++ b/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java @@ -1,13 +1,13 @@ package io.github.perplexhub.rsql; -import static io.github.perplexhub.rsql.RSQLJPASupport.*; import static io.github.perplexhub.rsql.RSQLQueryDslSupport.*; import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.*; import java.util.List; -import org.junit.jupiter.api.Test; +import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j +@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.NONE) public class RSQLQueryDslSupportTest { diff --git a/rsql-querydsl/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java b/rsql-querydsl/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java index d3d48014..d2d6952f 100644 --- a/rsql-querydsl/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java +++ b/rsql-querydsl/src/test/java/io/github/perplexhub/rsql/RSQLQueryDslSupportTest.java @@ -1,21 +1,19 @@ package io.github.perplexhub.rsql; -import static io.github.perplexhub.rsql.RSQLJPASupport.*; import static io.github.perplexhub.rsql.RSQLQueryDslSupport.*; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.Assert.*; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.jupiter.api.Test; +import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.junit4.SpringRunner; import io.github.perplexhub.rsql.model.*; @@ -25,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j +@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.NONE) public class RSQLQueryDslSupportTest { @@ -47,7 +46,7 @@ public final void testQueryMultiLevelAttribute() { assertThat(rsql, users.get(0).getId(), equalTo(1)); } - @Test + @Test public final void testEnumILike() { RSQLJPASupport.addEntityAttributeTypeMap(Status.class, String.class); String rsql = "status==*A*";