PostgresとHibernate 6でJSONデータを修正する

これは、 Postgres JSON関数のサポートに関連したシリーズの別の記事です。記事の主题は、JSONレコードの修正操作です。前記の記事と同様に、Postgresは他のNoSQLデータベースとして MongoDBなどに比べて、JSON修正にはそれほど完全な操作を持っていない可能性があることに留意してください(しかし、適切な関数構築を行うことで、同じ効果を得ることは可能です)。しかしながら、JSON修正が必要なほとんどのプロジェクトにはまだ適しています。また、トランザクションサポート(NoSQLデータベースのそのレベルではサポートしていません)があり、JSONデータを使用したPostgresを使用することは、とても良い選択です。もちろん、NoSQLデータベースには他にもより适したプロジェクトにとって優れた利点があります。

Postgresが JSONをサポートすることについては、一般的に多くの記事があります。この記事では、Hibernate 6ライブラリとこのサポートを統合することを焦点にしています。

JSONデータやテキスト検索にPostgresとHibernateを使用したクエリに興味を持つ人は、以下のリンクをご参照ください。

テストデータ

この記事のために、データベースにitemというテーブルがあり、JSON形式のコラムがあることを想定します。以下の例のようにです。

SQL

 

また、テストデータもあります:

SQL

 

ネイティブSQL実行

Javaフレームワークの他のものと同様に、HibernateではネイティブSQLクエリを実行できます。これは充実したドキュメントがあり、インターネット上に多くの例がありますので、この記事ではネイティブSQL操作の実行に焦点を当てません。しかし、JPA操作が生成するSQLの例を示す内容は含まれます。HibernateはJPA実装であり、JPA APIを使用してPostgresデータベース内のJSONデータを変更する方法を示すことは意味があります。

JSONオブジェクトの属性を変更し、JSONオブジェクト全体を変更しない(パス)

1つの列にJSONのすべてのデータを設定するのは簡単であり、多くの説明を必要としません。JSON内容の列を表現するEntityクラスにある属性に値を設定するだけです。

1つのデータベース行にJSONの1つまたは複数の属性を設定するのも类似です。テーブル行を読み取り、JSON値をPOJOに Deserialize し、特定の属性に値を設定し、データベースレコードを更新し、整个のペイロードを適用します。しかし、複数のデータベース行のJSON属性を変更するためにJSON属性を設定する方法は、実用的ではありません。

假に特定のJSONプロパティに対する一括更新を行う必要があるとします。データベースからフェッチすることとそれぞれのレコードを更新することは、効果的な方法ではありません。

そのような更新を1つのupdateステートメントで行うことは、特定のJSONプロパティに値を設定するためにより良い方法です。幸运にも、PostgresはJSONコンテンツを変更する関数を持っていて、SQL更新ステートメントで使用できます。

Posjsonhelper

Hibernateには、この記事で提起された函数およびオペレータの多くによるJSON変更のサポートがバージョン7でより良くなっています。しかし、バージョン6にはそのようなサポートを追加する計画はありません。幸运にも、PosjsonhelperプロジェクトはHibernateのバージョン6にこのようなサポートを追加しています。以下のすべての例では、Posjsonhelperライブラリを使用します。FunctionContributor

すべての例は、上記に述べたitemテーブルを表すJavaエンティティクラスを使用しています。

Java

 

jsonb_set 関数ラッパー

jsonb_set関数は、JSONデータの修正が必要である場合におそらく最も有用な関数です。これにより、JSONオブジェクトの特定のプロパティと特定の配列要素に基づいて、配列インデックスによって設定することができます。

例えば、以下のコードは内部プロパティ"child"にプロパティ"birthday"を追加します。

Java

 

このコードは、以下のようなSQLステートメントを生成します。

SQL

 

結合演算子(||)ラッパー

結合演算子(||)のラッパーは、2つのJSONB値を新しいJSONB値に結合します。

Postgresの文書に基づいて、この演算子の動作は以下の通りです。

2つの配列を結合すると、各入力を含む全ての要素を持つ新しい配列が生成されます。2つのオブジェクトを結合すると、それぞれのキーのユニオンを持つ新しいオブジェクトが生成されます。重複するキーがある場合は、2番目のオブジェクトの値が使用されます。すべての他のケースは、非配列の入力を単一要素の配列に変換し、2つの配列として処理します。再帰的に操作しない:結合はただの阶层の配列またはオブジェクト構造のみを統合します。

このラッパーをコードで使用する例を以下に示します。

Java

 

このコードは、JSONオブジェクトとchildプロパティを、データベースに既に保存されたJSONオブジェクトに結合します。

このコードは、以下のようなSQLクエリを生成します。

SQL

 

指定したパスのインデックスに基づいてフィールドまたは配列要素を削除する#-

Posjsonhelperには、削除操作(#-)のためのラッパーがあり、指定したパスのインデックスに基づいてフィールドまたは配列要素を削除します。パス要素はフィールドキーまたは配列インデックスとして指定できます。たとえば、以下のコードは、"child.pets" JSONパスに基づいてJSONオブジェクトのプロパティから削除します。

Java

 

生成されるSQLは以下の通りです:

SQL

 

指定したパスで複数の配列要素を削除する

デフォルトでは、Postgres(少なくともバージョン16で)配列要素の値に基づいて削除する内置関数を持っていません。しかし、上記に提及した内置オペレータ-#が配列要素をインデックスに基づいて削除することができ、その値に基づいて削除することはできません。

このために、Posjsonhelperは、DDL操作に追加してデータベース上で実行する必要がある関数を生成することができます。

SQL

 

そのようなラッパーの1つが、この関数を使用してJSON配列から複数の値を削除することを許可します。このコードは"child.inventory"プロパティの"mask""compass"要素を削除します。

Java

 

上記のコードによって生成されるSQLは以下の通りです:

SQL

 

Hibernate6JsonUpdateStatementBuilder: 1つの更新ステートメントで複数の変更操作を結合する方法

上記のすべての例は、JSONデータを変更する单个の操作の実行を示しています。もちろん、この記事で触れたいろいろな包装器を複数使用した更新ステートメントをコードに書くこともできます。しかし、これらの操作や関数がどのように実行されるかを理解することは、最初のJSON操作の結果が次のJSON変更操作の入力として使われる場合に、最も意味のあることであることを認識するために非常に重要です。この操作の出力は次の操作の入力となり、その後、最後のJSON変更操作までになります。

これをより明确にするために、SQLコードをご確認ください。

SQL

 

ここでは、4つのjsonb_set functionの実行と2つのdelete操作を想定しています。最も内部的なdelete操作は、JSONデータを格納する列の元の値がパラメータとして渡されるため、最初のJSON変更操作としてなされます。

これは正しい方法であり、既存の包装器はこのようなUPDATEステートメントの作成を可能にしていますが、コードの視点からは読みやすくありません。幸いにも、Posjsonhelperにはビルダーコンポーネントがあり、このような複雑なステートメントの構築を簡単にすることができます。

Hibernate6JsonUpdateStatementBuilder型は、JSONを変更する複数の操作に基づいた更新ステートメントの構築を可能にし、互いに依存していることをサポートします。

以下は、このコードの例です。

Java

 

先程提起したSQLステートメントは、このコードによって生成されました。

ビルダーの機能について詳細に知るには、ドキュメントを確認してください。

結論

PostgresデータベースはJSONデータの修正操作について幅広い可能性を持っています。これにより、Postgresを良い文書保存解決策と考えることができます。なお、私たちの解決策が高い読み取りパフォーマンス、より良いスケーリング、または分割(これらはすべてPostgresデータベースで实现でき、特にAWSなどのクラウドプロバイダーから提供される解決策で)を必要としない場合、JSON文書をPostgresデータベースに格納することを考慮する価値があることは言うまでもありません。また、Postgresのようなデータベースとのトランザクションサポートも考慮すべきです。

Source:
https://dzone.com/articles/modify-json-data-in-postgres-and-hibernate