Modificar Dados JSON em Postgres e Hibernate 6

Este é outro artigo na série relacionada à Suporte às funções JSON do Postgres em um projeto que usa o framework Hibernate com a versão 6. O tópico do artigo são as operações de modificação em registros JSON. Como no artigo anterior, vale mencionar que o Postgres pode não ter operações tão abrangentes quanto outros bancos de dados NoSQL, como o 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 se adapta a maioria dos projetos que exigem modificação de JSON. Além disso, com suporte a transações (não existente em um nível de NoSQL), é uma boa ideia usar o Postgres com dados JSON. Claro, os bancos de dados NoSQL têm outras vantagens que podem ser melhores para alguns projetos.

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

Caso alguém esteja interessado em consultar dados JSON ou em pesquisas de texto usando o Postgres e o Hibernate, veja as links abaixo:

Dados de Teste

Para o artigo, vamos supor 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 outros frameworks Java, com Hibernate, você pode executar consultas SQL nativas – o que é bem documentado e há muitos exemplos na internet. É por isso que 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 de Objeto JSON sem Alterar todo o Objeto JSON (Caminho)

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

É semelhante a ajustar propriedades JSON para uma linha de banco de dados. Nós apenas ler a linha da tabela, deserializar o valor JSON the 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 queremos modificar propriedades de JSON para várias linhas de banco de dados.

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

Seria muito melhor fazer essa atualização com uma única instrução update na qual definimos valores para propriedades JSON específicas. 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 oferece 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 essa funcionalidade para o Hibernate na versão 6. Todos os exemplos abaixo usarão a biblioteca Posjsonhelper. Veja esselinkpara 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

 

Excluir o Campo ou Elemento do Array Baseado no Índice na Rota Específica “#-“

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 de chaves ou índices de array. Por exemplo, o código abaixo remove da propriedade do objeto JSON com base em "child.pets" caminho JSON.

Java

 

O SQL gerado seria:

SQL

 

Excluir Múltiplos Elementos de Array na Rota Específica

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 sua base de dados.

SQL

 

Um dos wrappers usará essa função para permitir a exclusão de múltiplos valores do array JSON. Este código remove 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 com Uma Instrução de Atualização Unica

Todos os exemplos acima demonstraram a execução de uma operação única que modifica dados JSON. Claro, nossos códigos podem ter declarações de atualização que usam muitos dos wrappers mencionados neste artigo juntos. No entanto, estar ciente de como essas operações e funções serão executadas é crucial porque 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 ilustrar isso melhor, veja o código SQL.

SQL

 

Esse código assume 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 um parâmetro.

Embora isso seja a abordagem correta, e o wrapper existente permita a criação de tais declarações de UPDATE, pode não ser legível a partir do ponto de vista do 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 múltiplas operações que modificam JSON e dependem umas das outras.

Abaixo está um exemplo de código:

Java

 

A declaração SQL mencionada anteriormente foi gerada por esse código.

Para saber mais sobre como o construtor funciona, 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. Isto nos leva a considerar o Postgres uma boa escolha para soluções de armazenamento de documentos. Portanto, se nossa solução não requer altas performances de leitura, melhores escalas ou sharding (apesar de todas essas coisas serem alcançadas com o banco de dados Postgres, especialmente com as soluções fornecidas por provedores de nuvem como AWS), então é interessante 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