Modificar datos JSON en Postgres y Hibernate 6

Este es otro artículo de la serie relacionada con el apoyo a las funciones JSON de Postgres en un proyecto que utiliza elmarco de Hibernate con la versión 6. El tema para el artículo son las operaciones de modificación en registros JSON. Como en el artículo anterior, conviene mencionar que Postgres podría no tener operaciones tan comprensivas como otras bases de datos NoSQL comoMongoDB para la modificación de JSON (aunque, con las construcciones de función apropiadas, es posible alcanzar el mismo efecto). Aún así, es apropiado para la mayoría de los proyectos que requieren modificación de JSON. Además, con el soporte de transacciones (no soportadas en una base de datos NoSQL a ese nivel), es una buena idea utilizar Postgres con datos JSON. Naturalmente, las bases de datos NoSQL tienen otras ventajas que podrían ser mejores para los proyectos.

Generalmente hay muchos artículos sobre el apoyo deJSON en Postgres. Este artículo se centra en integrar este apoyo con la biblioteca Hibernate 6.

En caso de que alguien esté interesado en consultar datos JSON o búsquedas de texto utilizando Postgres y Hibernate, por favor vea los enlaces siguientes:

Datos de prueba

Para el artículo, supongamos que nuestra base de datos tiene una tabla llamada item, que tiene una columna con contenido JSON, como en el ejemplo de abajo:

SQL

 

También podríamos tener algunos datos de prueba:

SQL

 

Ejecución SQL nativa

Como en otros frameworks de Java, con Hibernate, puede ejecutar consultas SQL nativas, lo cual está bien documentado y hay muchos ejemplos en internet. Es por eso que en este artículo no nos centraremos en la ejecución de operaciones de SQL nativa. Sin embargo, habrá ejemplos de qué clase de SQL generan las operaciones del API JPA. Debido a que Hibernate es una implementación de JPA, tiene sentido mostrar cómo la API de JPA puede modificar datos JSON en la base de datos de Postgres.

Modificar propiedades del objeto JSON y no el objeto JSON completo (ruta)

Establecer el payload JSON completo para una columna es fácil y no requiere mucha explicación. Simplemente establecemos el valor de la propiedad en nuestra clase Entity, que representa una columna con contenido JSON.

Es similar a establecer una o varias propiedades de JSON para una sola fila de base de datos. Simplemente leemos la fila de la tabla, deserializamos el valor JSON a un POJO que representa un objeto JSON, establecemos valores para propiedades particulares y actualizamos los registros de base de datos con el payload completo. Sin embargo, such an approach might not be practical when we want to modify JSON properties for multiple database rows.

Supongamos que tenemos que realizar actualizaciones en lote de propiedades JSON particulares. Extraer datos de la base de datos y actualizar cada registro podría no ser un método efectivo.

Sería mucho mejor realizar una actualización de esa manera con una solaupdateinstrucción en la cual establezcamos valores para las propiedades JSON particulares. Afortunadamente, Postgres posee funciones que modifican el contenido JSON y pueden ser utilizadas en la instrucción SQL de actualización.

Posjsonhelper

Hibernate ofrece un mejor soporte para la modificación de JSON en la versión 7, incluyendo la mayoría de las funciones y operadores mencionados en este artículo. Sin embargo, no hay planes para agregar tal soporte en la versión 6. Afortunadamente, el proyecto Posjsonhelperagrega talesfunciones de soportepara Hibernate en la versión 6. Todos los ejemplos que se muestran a continuación utilizarán la biblioteca Posjsonhelper.Revise esteenlacepara averiguar cómo adjuntar una biblioteca a su proyecto Java. También tendrá que adjuntar FunctionContributor.

Todos los ejemplos utilizan la clase de entidad Java que representa la tabla item, cuya definición se mencionó anteriormente:

Java

 

jsonb_set Function Wrapper

La función jsonb_set es probablemente la función más útil cuando se requiere modificar datos JSON. Permite establecer propiedades específicas para objetos JSON y elementos de array específicos en función del índice del array.

Por ejemplo, el siguiente código añade la propiedad "cumpleaños" a la propiedad interna "niño".

Java

 

Este código generaría una sentencia SQL de este tipo:

SQL

 

Wrapper del operador de concatenación “||”

El wrapper del operador de concatenación (||) concatena dos valores JSONB en un nuevo valor JSONB.

Basado en la documentación de Postgres, el comportamiento del operador es el siguiente:

Concatenar dos arrays genera un array que contiene todos los elementos de cada entrada. Concatenar dos objetos genera un objeto que contiene la unión de sus claves, tomando el valor del segundo objeto cuando hay claves duplicadas. Todos los demás casos se tratan convirtiendo una entrada que no sea una matriz en una matriz de un solo elemento, y procediendo a continuación como para dos matrices. No opera de forma recursiva: sólo se fusiona la matriz o estructura de objetos de nivel superior.

Aquí tiene un ejemplo de cómo utilizar esta envoltura en su código:

Java

 

Code fusionar un objeto JSON con la propiedad child con el objeto JSON ya almacenado en la base de datos.

Este código genera una consulta SQL de este tipo:

SQL

 

Eliminar el campo o elemento del arreglo en función del índice especificado en la ruta “#-“

La Posjsonhelper tiene una capa de abstracción para la operación de eliminación (#-). Elimina el campo o elemento del arreglo en función del índice especificado en la ruta, donde los elementos de la ruta pueden ser claves de campos o índices de arreglos. Por ejemplo, el código de abajo elimina de la propiedad del objeto JSON en función de la ruta JSON "child.pets".

Java

 

El SQL generado sería:

SQL

 

Eliminar múltiples elementos de arreglo en la ruta especificada

Por defecto, Postgres (al menos en la versión 16) no tiene una función integrada que permita la eliminación de elementos de arreglo en función de su valor. Sin embargo, tiene el operador integrado, -#, que mencionamos anteriormente, que ayuda a eliminar elementos de arreglo en función del índice pero no en función de su valor.

Para este propósito, la Posjsonhelper puede generar una función que debe agregarse a la operación DDL y ejecutarse en su base de datos.

SQL

 

Uno de los wrappers utilizará esta función para permitir la eliminación de múltiples valores del arreglo JSON. Este código elimina elementos "mask" y "compass" para la propiedad "child.inventory".

Java

 

Aquí está el SQL generado por el código de arriba:

SQL

 

Hibernate6JsonUpdateStatementBuilder: Cómo combinar múltiples operaciones de modificación en una sola sentencia de actualización.

Todos los ejemplos anteriores demostraron la ejecución de una sola operación que modifica los datos JSON. Naturalmente, podemos tener sentencias de actualización en nuestro código que usen muchos de los wrappers mencionados en este artículo juntos. Sin embargo, ser consciente de cómo se ejecutarán esas operaciones y funciones es crucial porque tiene sentido cuando el resultado de la primera operación JSON es una entrada para las operaciones de modificación de JSON siguientes. La salida de esa operación sería una entrada para la próxima operación, y así sucesivamente, hasta la última operación de modificación de JSON.

Para ilustrar esto mejor, revise el código SQL.

SQL

 

Esto asume que tenemos cuatro ejecuciones de la función jsonb_set y dos operaciones delete. La operación delete más anidada es una primera operación de modificación de JSON porque se pasa como parámetro el valor original de una columna que almacena datos JSON.

Aunque esta es la approach correcta y el wrapper existente permite la creación de tal UPDATE statement, puede que no sea legible desde el punto de vista del código. Afortunadamente, Posjsonhelper tiene un componente constructor que hace que la construcción de una declaración tan compleja sea fácil.

El tipo Hibernate6JsonUpdateStatementBuilder permite la construcción de sentencias de actualización con múltiples operaciones que modifican JSON y dependen unas de otras.

A continuación se muestra un ejemplo de código:

Java

 

La declaración SQL mencionada anteriormente fue generada por este código.

Para saber más sobre cómo funciona el constructor, por favor revise la documentación.

Conclusión

La base de datos Postgres ofrece un amplio abanico de posibilidades en cuanto a operaciones de modificación de datos JSON. Esto nos lleva a considerar a Postgres como una buena opción para almacenar documentos. Así que, si nuestra solución no requiere un mayor rendimiento de lectura, una mejor escalabilidad o sharding (aunque todas esas cosas se pueden lograr con la base de datos Postgres, especialmente con soluciones proporcionadas por proveedores de servicios en la nube como AWS), entonces es interesante considerar almacenar sus documentos JSON en una base de datos Postgres. No mencionar la respaldo de transacciones con bases de datos como Postgres.

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