Modificar dados JSON em Postgres e Hibernate 6

Este é outro artigo da série relacionada à suporte de funções JSON do Postgres em um projeto usando o framework Hibernate com a versão 6. O tópico do artigo é as operações de modificação em registros JSON. Como no artigo anterior, é digno de menção que o Postgres pode não ter operações tão abrangentes quanto outros bancos de dados não-relacionais, como MongoDB, para modificação de JSON (embora, com a construção apropriada de funções, seja possível alcançar o mesmo efeito). Ainda assim, ele ajusta a maioria dos projetos que exigem modificação de JSON. Além disso, com suporte a transações (não oferecido em um banco de dados não-relacional a esse nível), é uma boa ideia usar o Postgres com dados JSON. Claro, os bancos de dados não-relacionais têm outros benefícios que podem melhor ajustar aos projetos.

Existem, em geral, muitos artigos sobre o suporte do Postgres para JSON . Este artigo se concentra na integração deste suporte com a biblioteca Hibernate 6.

Caso alguém se interesse em consultar dados JSON ou busca de texto usando Postgres e Hibernate, veja as links abaixo:

Dados de Teste

Para o artigo, vamos assumir que nossa base de dados tem uma tabela chamada item, que tem uma coluna com conteúdo JSON, como no exemplo abaixo:

SQL

 

Também podemos ter alguns dados de teste:

SQL

 

Execução SQL Nativa

Como em outras frameworks Java, com Hibernate, você pode executar consultas SQL nativas – o que é bem documentado e há muitos exemplos na internet. Por isso, neste artigo, não iremos focar na execução de operações de SQL nativa. No entanto, haverá exemplos do tipo de SQL que as operações JPA geram. Como Hibernate é uma implementação de JPA, faz sentido mostrar como a API JPA pode modificar dados JSON na base de dados Postgres.

Modificar Propriedades do Objeto JSON e Não o Objeto JSON inteiro (Caminho)

Ajustar todo o payload JSON para uma coluna é fácil e não requer muita explicação. Nós apenas definimos o valor das propriedades em nossa classe Entity, que representa uma coluna com conteúdo JSON.

É semelhante a ajustar propriedades JSON únicas ou múltiplas para uma linha de banco de dados. Nós apenas ler a linha da tabela, desserializar o valor JSON para um POJO representando um objeto JSON, ajustar valores para propriedades particulares e atualizar registros de banco de dados com o payload inteiro. No entanto, essa abordagem pode não ser prática quando você quiser modificar propriedades JSON de várias linhas de banco de dados.

Suponha que precisamos fazer atualizações em lote de propriedades de JSON específicas. Recuperar do banco de dados e atualizar cada registro pode não ser uma forma eficiente.

Seria muito melhor fazer essa atualização com uma única instrução update na qual definimos valores para propriedades de JSON particulares. Felizmente, o Postgres tem funções que modificam o conteúdo JSON e podem ser usadas na instrução SQL de atualização.

Posjsonhelper

O Hibernate tem melhor suporte para modificação de JSON na versão 7, incluindo a maioria das funções e operadores mencionados neste artigo. Ainda assim, não há planos para adicionar esse suporte na versão 6. Felizmente, o projeto Posjsonhelper adiciona essesuportepara o Hibernate na versão 6. Todos os exemplos abaixo usarão a biblioteca Posjsonhelper.Veja estelinkpara descobrir como anexar uma biblioteca the seu projeto Java. Você também terá que anexar FunctionContributor.

Todos os exemplos utilizam a classe de entidade Java que representa a tabela item, cuja definição foi mencionada acima:

Java

 

jsonb_set Function Wrapper

A função jsonb_set é provavelmente a função mais útil quando é necessário modificar dados JSON. Ela permite que propriedades específicas para objetos JSON e elementos específicos do array sejam definidos com base no índice do array.

Por exemplo, o código abaixo adiciona a propriedade "birthday" à propriedade interna "child".

Java

 

Este código geraria uma instrução SQL desse tipo:

SQL

 

Envoltório do operador de concatenação “||”

O envoltório para o operador de concatenação (||) concatena dois valores JSONB em um novo valor JSONB.

Com base na documentação do Postgres, o comportamento do operador é o seguinte:

Concatenar dois arrays gera um array contendo todos os elementos de cada entrada. A concatenação de dois objectos gera um objeto que contém a união das suas chaves, tomando o valor do segundo objeto quando existem chaves duplicadas. Todos os outros casos são tratados convertendo uma entrada não array em um array de elemento único, e então procedendo como para dois arrays. Não opera recursivamente: apenas o array de nível superior ou a estrutura do objeto é mesclada.

Aqui está um exemplo de como usar esse wrapper no seu código:

Java

 

Codifique a mesclagem de um objeto JSON com a propriedade child com o objeto JSON já armazenado no banco de dados.

Este código gera uma consulta SQL:

SQL

 

Exclua o Campo ou Elemento do Array Baseado no Índice na Rota Especificada “#-“

O Posjsonhelper tem uma wrapper para a operação de exclusão (#-). Ela exclui o campo ou elemento do array com base no índice na rota especificada, onde os elementos da rota podem ser campos chaves ou índices de array. Por exemplo, o código abaixo remove da propriedade do objeto JSON com base na rota "child.pets".

Java

 

O SQL gerado seria:

SQL

 

Excluir Múltiplos Elementos de Array na Rota Especificada

Por padrão, o Postgres (pelo menos na versão 16) não tem uma função interna que permite a remoção de elementos de array com base em seu valor. No entanto, ele tem o operador interno, -#, que mencionamos acima, que ajuda a excluir elementos de array com base no índice mas não em seu valor.

Para esse fim, o Posjsonhelper pode gerar uma função que deve ser adicionada à operação DDL e executada em seu banco de dados.

SQL

 

Um dos wrappers usará essa função para permitir a exclusão de múltiplos valores do array JSON. Esse código remove os elementos "mask" e "compass" para a propriedade "child.inventory".

Java

 

Aqui está o SQL gerado pelo código acima:

SQL

 

Hibernate6JsonUpdateStatementBuilder: Como Combinar Múltiplas Operações de Modificação em Uma Instrução de Atualização Unica.

Todos os exemplos acima mostraram a execução de uma operação única que modifica dados JSON. Claro, nossos comandos de atualização no código podem usar muitos dos wrappers mencionados neste artigo juntos. No entanto, ficar ciente de como essas operações e funções serão executadas é crucial porque isso faz sentido quando o resultado da primeira operação de JSON é um input para as operações de modificação de JSON seguintes. O output dessa operação seria um input para a próxima operação, e assim por diante, até a última operação de modificação de JSON.

Para melhor ilustrar isso, verifique o código SQL.

SQL

 

Este pressupõe que temos quatro execuções da função jsonb_set e duas operações delete. A operação mais aninhada de delete é a primeira operação de modificação de JSON porque o valor original de uma coluna que armazena dados JSON é passado como parâmetro.

Embora isso seja a abordagem correta, e o wrapper existente permita a criação de tais UPDATE statements, pode não ser lido a partir de uma perspectiva de código. Felizmente, o Posjsonhelper tem um componente construtor que torna a construção de tais declarações complexas fácil.

O tipo Hibernate6JsonUpdateStatementBuilder permite a construção de declarações de atualização com várias operações que modificam JSON e dependem umas das outras.

Abaixo está um exemplo de código:

Java

 

O comando SQL mencionado anteriormente foi gerado por este código.

Para saber mais sobre o funcionamento do construtor, por favor, verifique a documentação.

Conclusão

O banco de dados Postgres oferece uma ampla gama de possibilidades em relação às operações de modificação de dados em formato JSON. Isso nos leva a considerar o Postgres uma boa escolha para armazenamento de documentos. Portanto, se nossa solução não exigir performance de leitura superior, melhor escalabilidade ou sharding (embora todas essas coisas possam ser alcançadas com o banco de dados Postgres, especialmente com as soluções fornecidas por provedores de nuvem como AWS), então é bom considerar armazenar seus documentos JSON em um banco de dados Postgres — não mencionando ainda o suporte a transações com bancos de dados como o Postgres.

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