Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import static io.github.perplexhub.rsql.RSQLVisitorBase.getEntityManagerMap;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.EnumSet;
import java.util.Map;
Expand All @@ -19,13 +20,22 @@
import jakarta.persistence.criteria.Path;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.ManagedType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.util.ClassUtils;

/**
* Support for jsonb expression.
*/
public class JsonbSupport {

/**
* is annotation present on classpath ?
*/
private static final boolean isHibernatePresent = ClassUtils.isPresent(
"org.hibernate.annotations.JdbcTypeCode", JsonbSupport.class.getClassLoader());

private static final Set<Database> JSON_SUPPORT = EnumSet.of(Database.POSTGRESQL);

private static final Map<ComparisonOperator, ComparisonOperator> NEGATE_OPERATORS =
Expand Down Expand Up @@ -99,15 +109,43 @@ public static boolean isJsonType(String mappedProperty, ManagedType<?> classMeta
* @return true if the attribute is a jsonb attribute
*/
private static boolean isJsonColumn(Attribute<?, ?> attribute) {
return isJsonbColumn(attribute) || isJdbcTypeCodeJSON(attribute);
}

/**
* Returns whether the given attribute is a jsonb column.
*
* @param attribute the attribute
* @return true if the column is a jsonb column
*/
private static boolean isJsonbColumn(Attribute<?, ?> attribute) {
return getFieldAnnotation(attribute, Column.class)
.map(Column::columnDefinition)
.map(s -> s.toLowerCase().startsWith("jsonb"))
.orElse(false);
}

/**
* Returns whether the given attribute is annotated with {@link JdbcTypeCode} and {@code value == SqlTypes.JSON}.
*
* @param attribute the attribute
* @return true if the column is a jsonb column
*/
private static boolean isJdbcTypeCodeJSON(Attribute<?, ?> attribute) {
return isHibernatePresent && getFieldAnnotation(attribute, JdbcTypeCode.class)
.map(JdbcTypeCode::value)
.map(code -> SqlTypes.JSON == code)
.orElse(false);
}

private static <T extends Annotation> Optional<T> getFieldAnnotation(Attribute<?, ?> attribute, Class<T> annotationClass) {
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);
.map(field -> field.getAnnotation(annotationClass));
}


/**
* Returns the database of the given attribute.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package io.github.perplexhub.rsql;

import io.github.perplexhub.rsql.jsonb.JsonbConfiguration;
import io.github.perplexhub.rsql.model.AnotherJsonbEntity;
import io.github.perplexhub.rsql.model.EntityWithJsonb;
import io.github.perplexhub.rsql.model.JsonbEntity;
import io.github.perplexhub.rsql.model.PostgresJsonEntity;
import io.github.perplexhub.rsql.repository.jpa.postgres.AnotherJsonbEntityRepository;
import io.github.perplexhub.rsql.repository.jpa.postgres.EntityWithJsonbRepository;
import io.github.perplexhub.rsql.repository.jpa.postgres.JsonbEntityRepository;
import io.github.perplexhub.rsql.repository.jpa.postgres.PostgresJsonEntityRepository;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
Expand Down Expand Up @@ -44,6 +47,9 @@ class RSQLJPASupportPostgresJsonTest {
@Autowired
private JsonbEntityRepository jsonbEntityRepository;

@Autowired
private AnotherJsonbEntityRepository anotherJsonbEntityRepository;

@BeforeEach
void setup(@Autowired EntityManager em) {
RSQLVisitorBase.setEntityManagerDatabase(Map.of(em, Database.POSTGRESQL));
Expand Down Expand Up @@ -737,4 +743,16 @@ void testJsonSearchCustomFunction(List<PostgresJsonEntity> entities, String rsql

entities.forEach(e -> e.setId(null));
}

@Test
void testAlternateJsonColumnDefinitions() {
anotherJsonbEntityRepository.saveAllAndFlush(List.of(
AnotherJsonbEntity.builder().id(UUID.randomUUID()).data("{\"a\":\"b\",\"c\":1}").other("{\"d\":\"e\"}").build(),
AnotherJsonbEntity.builder().id(UUID.randomUUID()).data("{\"a\":\"q\",\"c\":2}").other("{\"d\":\"h\"}").build()
));
assertThat(anotherJsonbEntityRepository.findAll(toSpecification("data.a==b"))).hasSize(1);
assertThat(anotherJsonbEntityRepository.findAll(toSpecification("other.d==h"))).hasSize(1);
assertThat(anotherJsonbEntityRepository.findAll(toSpecification("generated.a==b"))).hasSize(1);
assertThat(anotherJsonbEntityRepository.findAll(toSpecification("formula.c==1"))).hasSize(1);;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
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.Id;
import lombok.*;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Type;
import org.hibernate.type.SqlTypes;

import java.util.UUID;

@Getter
@Setter
@EqualsAndHashCode(of = "id")
@ToString
@Entity
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class AnotherJsonbEntity {

@Id
private UUID id;

@JdbcTypeCode(SqlTypes.JSON)
private String data;

@Type(JsonType.class)
@Column(columnDefinition = "jsonb")
private String other;

@Column(columnDefinition = "jsonb generated always as (data) stored", insertable = false, updatable = false)
private String generated;

@JdbcTypeCode(SqlTypes.JSON)
@Formula("jsonb_set(data, '{f}','\"r\"', true)")
private String formula;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.github.perplexhub.rsql.repository.jpa.postgres;

import io.github.perplexhub.rsql.model.AnotherJsonbEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import java.util.UUID;

public interface AnotherJsonbEntityRepository extends JpaRepository<AnotherJsonbEntity, UUID>,
JpaSpecificationExecutor<AnotherJsonbEntity> {
}