복붙노트

[SPRING] 스프링 데이터 jpa를 사용하여 jsonb 열을 쿼리하려면 어떻게합니까?

SPRING

스프링 데이터 jpa를 사용하여 jsonb 열을 쿼리하려면 어떻게합니까?

postgres 9.4 인스턴스에 대해이 원시 쿼리를 올바르게 가져 오는 데 문제가 있습니다.

내 저장소에는 다음과 같은 방법이 있습니다.

 @Query(value = "SELECT t.* " +
            "FROM my_table t " +
            "WHERE t.field_1 = ?1 " +
            "AND t.field_2 = 1 " +
            "AND t.field_3 IN ?2 " +
            "AND t.jsonb_field #>> '{key,subkey}' = ?3",
            nativeQuery = true)
    List<Entity> getEntities(String field1Value,
                                   Collection<Integer> field3Values,
                                   String jsonbFieldValue);

그러나 로그에 다음과 같이 표시됩니다.

SELECT t.* FROM my_table t 
WHERE t.field_1 = ?1 
  AND t.field_2 = 1 
  AND t.field_3 IN ?2 
  AND t.jsonb_field ? '{key,subkey}' = ?3

그리고 나는이 예외를 얻는다.

메서드 호출 전에 매개 변수를 직접 기록했으며 모두 제공되었습니다.

# >> 왜 보여줄지 모르겠다. 로그에. # >> 탈출해야하나요? IN을 위해 컬렉션을 포맷해야합니까? json 경로에서 벗어나야합니까?

db에 대해 직접 쿼리를 실행하면 작동합니다. 예:

SELECT *
FROM my_table t
WHERE t.field_1 = 'xxxx'
  AND t.field_2 = 1
  AND t.field_3 IN (13)
  AND t.jsonb_field #>> '{key,subkey}' = 'value'

해결법

  1. ==============================

    1.스프링 데이터에서 매우 유용한 사양 API를 발견했습니다. Product라는 이름의 엔티티와 JSON (B) 유형의 title이라는 이름의 속성이 있다고 가정 해 보겠습니다. 이 속성에는 제품의 제목이 다른 언어로 포함되어 있다고 가정합니다. 예를 들면 다음과 같습니다. { "EN": "여러 색 LED 라이트", "GR": "ψολύχρωμο LED φώς"}. 아래의 소스 코드는 제목과 로캘이 인수로 전달 된 (또는 고유 한 필드가 아닌 경우 더 많은) 제품을 찾습니다.

    스프링 데이터에서 매우 유용한 사양 API를 발견했습니다. Product라는 이름의 엔티티와 JSON (B) 유형의 title이라는 이름의 속성이 있다고 가정 해 보겠습니다. 이 속성에는 제품의 제목이 다른 언어로 포함되어 있다고 가정합니다. 예를 들면 다음과 같습니다. { "EN": "여러 색 LED 라이트", "GR": "ψολύχρωμο LED φώς"}. 아래의 소스 코드는 제목과 로캘이 인수로 전달 된 (또는 고유 한 필드가 아닌 경우 더 많은) 제품을 찾습니다.

    @Repository
    public interface ProductRepository extends JpaRepository<Product, Integer>, JpaSpecificationExecutor<Product> {
    }
    
    
    public class ProductSpecification implements Specification<Product> {
    
        private String locale;
        private String titleToSearch;
    
        public ProductSpecification(String locale, String titleToSearch) {
            this.locale = locale;
            this.titleToSearch = titleToSearch;
        }
    
        @Override
        public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
            return builder.equal(builder.function("jsonb_extract_path_text", String.class, root.<String>get("title"), builder.literal(this.locale)), this.titleToSearch);
        }
    }
    
    
    @Service
    public class ProductService {
    
        @Autowired
        private ProductRepository productRepository;
    
        public List<Product> findByTitle(String locale, String titleToSearch) {
            ProductSpecification cs = new ProductSpecification(locale, titleToSearch);
            return productRepository.find(cs);
            // Or using lambda expression - without the need of ProductSpecification class.
    //      return productRepository.find((Root<ProductCategory> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> {
    //          return builder.equal(builder.function("jsonb_extract_path_text", String.class, root.<String>get("title"), builder.literal(locale)), titleToSearch);
    //      });
        }
    }
    

    여기 스프링 데이터를 사용해야하는 방법에 대한 또 다른 대답을 찾을 수 있습니다. 희망이 도움이됩니다.

  2. ==============================

    2.운영자가 한 가지 이유 또는 다른 이유로 물음표로 변환되는 경우 함수를 대신 사용해보십시오. psql 콘솔에서 \ doS + # >>를 사용하여 해당 기능을 찾을 수 있습니다. 호출 된 함수가 jsonb_extract_path_text임을 알려줍니다. 이렇게하면 다음과 같은 질문을하게됩니다.

    운영자가 한 가지 이유 또는 다른 이유로 물음표로 변환되는 경우 함수를 대신 사용해보십시오. psql 콘솔에서 \ doS + # >>를 사용하여 해당 기능을 찾을 수 있습니다. 호출 된 함수가 jsonb_extract_path_text임을 알려줍니다. 이렇게하면 다음과 같은 질문을하게됩니다.

    @Query(value = "SELECT t.* " +
            "FROM my_table t " +
            "WHERE t.field_1 = ?1 " +
            "AND t.field_2 = 1 " +
            "AND t.field_3 IN ?2 " +
            "AND jsonb_extract_path_text(t.jsonb_field, '{key,subkey}') = ?3",
            nativeQuery = true)
    
  3. ==============================

    3.나는이 방법을 따르지 말고, 일반적인 CLUD 방법을 따르도록 제안한다. (StrongLoop Loopback의 방법으로 고급 자동 생성 DAO 방법을 사용한다. 스프링 데이터 레스트 메이븐 플러그인을위한 것이지만 지금은 실험적이다.) 하지만이 JSON을 사용하여 무엇을 해야할지 ... @Document 주석을 통해 Spring 데이터에서 MongoDB JSON 처리와 비슷한 것을 찾고 있는데 아직 사용할 수 없습니다. 하지만 다른 방법이 있습니다 :-)

    나는이 방법을 따르지 말고, 일반적인 CLUD 방법을 따르도록 제안한다. (StrongLoop Loopback의 방법으로 고급 자동 생성 DAO 방법을 사용한다. 스프링 데이터 레스트 메이븐 플러그인을위한 것이지만 지금은 실험적이다.) 하지만이 JSON을 사용하여 무엇을 해야할지 ... @Document 주석을 통해 Spring 데이터에서 MongoDB JSON 처리와 비슷한 것을 찾고 있는데 아직 사용할 수 없습니다. 하지만 다른 방법이 있습니다 :-)

    일반적으로 JSON 사용자 유형 (UserType 인터페이스)을 구현하는 방법은 다음과 같습니다.

    public class YourJSONBType implements UserType {
    

    마지막으로 구현 된 사용자 유형을 지정하여 JPA 클래스를 향상시켜야합니다.

    @Entity
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @TypeDef(name = "JsonbType", typeClass = YourJSONBType.class)
    public class Person {
        @Id
        @GeneratedValue
        private Long id;
    
        @Column(columnDefinition = "jsonb")
        @Type(type = "JsonbType")
        private Map<String,Object> info;
    }
    

    여기에서 다른 관련 기사를 살펴보십시오. postgreSQL JSON 열을 Hibernate 값 유형에 매핑

    전체 구현 예제는 다음에서 사용할 수 있습니다.

    유사하지만 약간 다른 예가 여기에 있습니다. http://www.wisely.top/2017/06/27/spring-data-jpa-postgresql-jsonb/?d=1

  4. ==============================

    4.사용자 정의 함수를 호출하고 원시 조회를 사용하지 않으려면 FUCT JPQL 키워드를 사용할 수도 있습니다. 이 같은,

    사용자 정의 함수를 호출하고 원시 조회를 사용하지 않으려면 FUCT JPQL 키워드를 사용할 수도 있습니다. 이 같은,

    @Query(value = "SELECT t FROM my_table t "
            + "WHERE t.field_1=:field_1 AND t.field_2=1 AND t.field_3 IN :field_3 "
            + "AND FUNC('jsonb_extract_path_text', 'key', 'subkey')=:value")
    List<Entity> getEntities(@Param("field_1") String field_1, @Param("field_3") Collection<Integer> field_3, @Param("value") String value);
    
  5. from https://stackoverflow.com/questions/43900457/how-do-i-use-spring-data-jpa-to-query-jsonb-column by cc-by-sa and MIT license