Modify JSON Data in Postgres and Hibernate 6

Это другой статья в серии, связанных с поддержкой функций JSON Postgres в проекте, использующемфреймворк Hibernate с версией 6. Тема статьи – операции модификации записей JSON. Как и в предыдущей статье, следует отметить, что Postgres может не иметь таких всесторонних операций, как другие NoSQL-базы данных, такие как MongoDB для модификации JSON ( хотя, с помощью соответствующих конструкций функций, можно достичь того же эффекта). Тем не менее, он подходит для большинства проектов, требующих модификации JSON. Кроме того, с поддержкой транзакций (что не поддерживается в NoSQL-базе данных на таком уровне), это довольно хорошая идея использовать Postgres с данными JSON. Конечно, NoSQL-базы данных имеют и другие преимущества, которые могли бы лучше соответствовать потребностям проектов.

Вообще говоря, существует много статей о поддержке JSON Postgres. Эта статья фокусируется на интеграции этой поддержки с библиотекой Hibernate 6.

Если кто-то заинтересован в запросах данных JSON или текстовом поиске с использованием Postgres и Hibernate, см. следующие ссылки:

Тестовые данные

Для статьи предположим, что у нашей базы данных есть таблица с именем item, у которой есть колонка с JSON-содержимым, как в примере ниже:

SQL

 

Мы также могли бы иметь некоторые тестовые данные:

SQL

 

Нative SQL Execution

Как и в других фреймворках Java, с Hibernate вы можете выполнять нативные SQL-запросы — которые хорошо документированы и есть много примеров в интернете. Поэтому в этой статье мы не будем сосредотачиваться на выполнении операций нативного SQL. Тем не менее, будут приведены примеры того, какого SQL JPA операции генерируют.因为 Hibernate – это реализация JPA, это имеет смысл показать, как API JPA может изменять JSON-данные в базе данных Postgres.

Изменение свойств JSON-объекта и не всего JSON-объекта (пути)

Установка всего JSON-payload для одной колонки легко и не требует много объяснений. Мы просто устанавливаем значение для свойства в нашем классе Entity, который представляет собой колонку с JSON-содержимым.

Также это похоже на установку одного или нескольких JSON-свойств для одной строки базы данных. Мы просто читаем строку таблицы, десериализуем JSON-значение в POJO, представляющем JSON-объект, устанавливаем значения для определенных свойств и обновляем записи базы данных с целым payload. Однако такой подход может оказаться не практичным, когда мы хотим изменить JSON-свойства для множества строк базы данных.

Предположим, что нам необходимо выполнить пакетную обновление определённых свойств JSON. Запрос данных из базы данных и обновление каждого записи может не быть эффективным методом.

Более удобно выполнить такое обновление однимupdate запросом, где мы устанавливаем значения для определённых свойств JSON. К счастью, Postgres предоставляет функции, модифицирующие содержимое JSON, которые можно использовать в SQL запросе обновления.

Posjsonhelper

Hibernate имеет лучшую поддержку модификации JSON в версии 7, включая большинство функций и операторов, упомянутых в этой статье. Тем не менее, в версии 6 такая поддержка не планируется. К счастью, проект Posjsonhelper добавляет такую поддержку для Hibernate в версии 6. Все примеры ниже будут использовать библиотеку Posjsonhelper.Пожалуйста,тест этогоссылки, чтобы узнать, как прикрепить библиотеку к вашему Java-проекту. Вы также должны прикрепить FunctionContributor.

Во всех примерах используется класс Java entity, представляющий таблицу item, определение которой было приведено выше:

Java

 

jsonb_set Function Wrapper

Функция jsonb_set, вероятно, является наиболее полезной функцией, когда требуется модификация JSON-данных. Она позволяет устанавливать определенные свойства для объектов JSON и конкретных элементов массива на основе индекса массива.

Например, приведенный ниже код добавляет свойство "birthday" к внутреннему свойству "child".

Java

 

Этот код сгенерирует такой SQL-оператор:

SQL

 

Обертка оператора конкатенации “||”

Обертка для оператора конкатенации (|) объединяет два значения JSONB в новое значение JSONB.

Согласно документации Postgres, оператор ведет себя следующим образом:

При конкатенации двух массивов создается массив, содержащий все элементы каждого входа. Конкатенация двух объектов создает объект, содержащий объединение их ключей, принимая значение второго объекта, если ключи дублируются. Во всех остальных случаях входные данные, не являющиеся массивами, преобразуются в одноэлементный массив, а затем выполняются действия, как для двух массивов. Не работает рекурсивно: объединяется только массив или объектная структура верхнего уровня.

Вот пример использования этой обертки в вашем коде:

Java

 

Код объединения JSON-объекта со свойством child с уже сохраненным JSON-объектом в базе данных.

Этот код генерирует такой SQL-запрос:

SQL

 

Удалить поле или элемент массива на основании индекса указанного пути “#-“

Posjsonhelper имеет обёртку для операции удаления (#-). Она удаляет поле или элемент массива на основании индекса указанного пути, где элементы пути могут быть либо ключами полей JSON, либо индексами массива. Например, следующий код удаляет из свойства JSON-объекта на основании JSON-пути "child.pets".

Java

 

Generated SQL would be:

SQL

 

Удалить несколько элементов массива на указанном пути

По умолчанию Postgres (по крайней мере, в версии 16) не имеет встроенной функции, которая позволяет удалять элементы массива на основании их значения. Однако у него есть встроенный оператор -#, о котором мы упомянули выше, который помогает удалять элементы массива на основании индекса, но не на основании их значения.

Для этой цели Posjsonhelper может генерировать функцию, которую необходимо добавить к операции DDL и выполнить на вашей базе данных.

SQL

 

Один из обёрток будет использовать эту функцию, чтобы позволить удаление нескольких значений из JSON-массива. Этот код удаляет элементы "mask" и "compass" для свойства "child.inventory".

Java

 

Here is the SQL generated by the above code:

SQL

 

Hibernate6JsonUpdateStatementBuilder: Как объединить несколько операций модификации в одно SQL-запрос на обновление.

Все вышеуказанные примеры демонстрируют выполнение одной операции, которая изменяет данные JSON. конечно, в нашем коде могут быть утверждения обновления, использующие множество оберток, упомянутых в этой статье вместе. Однако важно быть осведомленным в том, как эти операции и функции будут выполнены, поскольку это имеет смысл, когда результат первой операции изменения JSON является входом для следующих операций модификации JSON. Выход из этой операции станет входом для следующей операции, и так далее, до последней операции модификации JSON.

Для лучшего понимания этого, посмотрите на SQL-код.

SQL

 

Это предполагает, что у нас есть четыре выполнения jsonb_set function и две операции delete. Самая вложенная операция delete является первой операцией модификации JSON, поскольку исходное значение из колонки, хранящей JSON-данные, выступает в качестве параметра.

尽管这是一种正确的做法,现有的包装器允许创建这样的 UPDATE 语句,但从代码角度来看,它可能不太易读。幸运的是,Posjsonhelper 有一个构建器组件,使得构建这样的复杂语句变得容易。

タイプ Hibernate6JsonUpdateStatementBuilder 使いこなす更新语句にJSONを変更する複数の操作が相互依存する。

以下は代碼の例です。

Java

 

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

Для более подробного ознакомления с работой конструктора, пожалуйста, проверьте документацию.

Заключение

База данных Postgres предлагает широкий спектр возможностей по работе с JSON-данными. Это делает Postgres хорошим выбором для хранения документов. Так что если наше решение не требует высокой производительности чтения, лучшей масштабируемости или схемы распределения ( хотя все эти вещи могут быть достигнуты с помощью базы данных Postgres, особенно с решениями, предоставляемыми cloud providers, такими как AWS), то может быть sensble рассмотреть возможность хранения ваших JSON-документов в базе данных Postgres. Не говоря уже о поддержке транзакций с базами данных, такими как Postgres.

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