web-dev-qa-db-ja.com

Spring Data 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

そして、私はこの例外を受け取ります:

内部例外:org.postgresql.util.PSQLException:パラメーター2に値が指定されていません.

メソッドを呼び出す直前にパラメーターをログに記録しましたが、すべて提供されています。

ログに#>>?を表示する理由がわかりません。 #>>をエスケープする必要がありますか?コレクションを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'
5
Josh C.

春のデータから Specification apiが非常に役に立ちました。
名前がProductのエンティティと名前がtitleのJSON(B)タイプのプロパティがあるとします。
このプロパティには、製品のタイトルがさまざまな言語で含まれていると思います。たとえば、{"EN":"Multicolor LED light", "EL":"Πολύχρωμο 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);
//      });
    }
}

Spring Data here の使用方法に関する別の答えを見つけることができます。
それが役立つことを願っています。

14

これは古いトピックかもしれませんが、ここではjsonbでスプリング仕様を使用してフィールドごとに検索しています。

「LIKE」で検索する場合は、次のコードを使用して分離のように作成する必要があります。

final Predicate likeSearch = cb.disjunction();

その後、あなたのオブジェクトにアドレスであるjsonbフィールドがあり、アドレスには5つのフィールドがあると仮定しましょう。これらすべてのフィールドを検索するには、すべてのフィールドに「LIKE」式を追加する必要があります。

for (String field : ADDRESS_SEARCH_FIELDS) {
                likeSearch.getExpressions().add(cb.like(cb.lower(cb.function("json_extract_path_text", String.class,
                        root.get("address"), cb.literal(field))), %searchKey%));
            }

ここで、cbは同じcriteriaBuilderです。 %searchKey%は、住所フィールドで検索するものです。

お役に立てれば。

3
Arman Tumanyan

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);
1

演算子が何らかの理由で疑問符に変換されている場合は、代わりに関数を使用してみてください。 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)
1
coladict

この方法に従わないことをお勧めします。一般的なCRUD方法に従うことをお勧めします(Spring Data Rest Mavenプラグインの場合、StrongLoop Loopbackの方法で高度な自動生成DAOメソッドにも取り組んでいますが、現時点では実験的なものです)。しかし、このJSONで、次に何をすべきか... @Documentアノテーションを介してSpring Dataで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

0
kensai