Colecciones anidadas Con compatibilidad con SQL/XML o SQL/JSON desde jOOQ 3.14 - Java, SQL y jOOQ.

Una de las principales características de los ORM es M como Mapping. Las bibliotecas como jOOQ ayudan a asignar automáticamente registros de bases de datos planos o anidados a clases de Java que tienen la misma estructura que el conjunto de resultados de SQL. Lo siguiente siempre ha sido posible en jOOQ, asumiendo PostgreSQL INFORMATION_SCHEMA (usando el código generado a partir del módulo jOOQ-meta):



class Column {
    String tableSchema;
    String tableName;
    String columnName;
}

for (Column c :
    ctx.select(
            COLUMNS.TABLE_SCHEMA, 
            COLUMNS.TABLE_NAME, 
            COLUMNS.COLUMN_NAME)
       .from(COLUMNS)
       .where(COLUMNS.TABLE_NAME.eq("t_author"))
       .orderBy(COLUMNS.ORDINAL_POSITION)
       .fetchInto(Column.class))
    System.out.println(
        c.tableSchema + "." + c.tableName + "." + c.columnName
    );


Lo anterior resultando en algo como:


public.t_author.id
public.t_author.first_name
public.t_author.last_name
public.t_author.date_of_birth
public.t_author.year_of_birth
public.t_author.address

El mapeo es simple, como se explica en jOOQ's DefaultRecordMapper.

Asignaciones anidadas

Una característica menos conocida que ofrecimos durante un tiempo fue usar la notación de puntos para emular registros anidados en clases anidadas de Java. Suponiendo que desea utilizar una descripción de tipo de datos reutilizable en sus columnas y en otros lugares:



class Type {
    String name;
    int precision;
    int scale;
    int length;
}

class Column {
    String tableSchema;
    String tableName;
    String columnName;
    Type type;
}


Ahora puede escribir esta consulta en la que crea un alias para algunas columnas usando la notación de puntos para type.namepor ejemplo (son posibles varios niveles de anidamiento):



for (Column c :
    ctx.select(
            COLUMNS.TABLE_SCHEMA,
            COLUMNS.TABLE_NAME,
            COLUMNS.COLUMN_NAME,
            COLUMNS.DATA_TYPE.as("type.name"),
            COLUMNS.NUMERIC_PRECISION.as("type.precision"),
            COLUMNS.NUMERIC_SCALE.as("type.scale"),
            COLUMNS.CHARACTER_MAXIMUM_LENGTH.as("type.length")
       )
       .from(COLUMNS)
       .where(COLUMNS.TABLE_NAME.eq("t_author"))
       .orderBy(COLUMNS.ORDINAL_POSITION)
       .fetchInto(Column.class))

    System.out.println(String.format(
        "%1$-30s: %2$s",
        c.tableSchema + "." + c.tableName + "." + c.columnName,
        c.type.name + (c.type.precision != 0
               ? "(" + c.type.precision + ", " + c.type.scale + ")"
               :       c.type.length != 0
               ? "(" + c.type.length + ")"
               : "")
    ));


Lo anterior imprimirá:


public.t_author.id            : integer(32, 0)
public.t_author.first_name    : character varying(50)
public.t_author.last_name     : character varying(50)
public.t_author.date_of_birth : date
public.t_author.year_of_birth : integer(32, 0)
public.t_author.address       : USER-DEFINED

Usar XML o JSON

Usando XML o JSON, comenzando con jOOQ 3.14, también puede anidar colecciones en su mapeo de conjuntos de resultados. Primero, veamos nuevamente cómo usar una consulta JSON usando jOOQ, por ejemplo, para encontrar todas las columnas por tabla:



for (Record1<JSON> record :
    ctx.select(
            jsonObject(
                key("tableSchema").value(COLUMNS.TABLE_SCHEMA),
                key("tableName").value(COLUMNS.TABLE_NAME),
                key("columns").value(jsonArrayAgg(
                    jsonObject(
                        key("columnName").value(COLUMNS.COLUMN_NAME),
                        key("type").value(jsonObject(
                            "name", COLUMNS.DATA_TYPE)
                        )
                    )
                ).orderBy(COLUMNS.ORDINAL_POSITION))
            )
       )
       .from(COLUMNS)
       .where(COLUMNS.TABLE_NAME.in("t_author", "t_book"))
       .groupBy(COLUMNS.TABLE_SCHEMA, COLUMNS.TABLE_NAME)
       .orderBy(COLUMNS.TABLE_SCHEMA, COLUMNS.TABLE_NAME)
       .fetch())
    System.out.println(record.value1());


Se devuelven los siguientes documentos JSON:


{
  "tableSchema": "public", 
  "tableName": "t_author", 
  "columns": [{
    "columnName": "id", 
    "type": {"name": "integer"}
  }, {
    "columnName": "first_name", 
    "type": {"name": "character varying"}
  }, {...}]
}

{
  "tableSchema": "public", 
  "tableName": "t_book", 
  "columns": [{...}, ...]
}

Ya es genial, ¿no? Ya hemos escrito sobre esto aquí y aquí. A partir de jOOQ 3.14, puede eliminar todos los demás middleware y asignaciones, y producir sus documentos XML o JSON directamente desde su base de datos utilizando la API estándar de SQL/XML o SQL/JSON.

¡Pero eso no es todo!

Tal vez en realidad no necesite el documento JSON, solo quiere usar JSON para habilitar el anidamiento de estructuras de datos, asignándolas a Java. ¿Qué pasa con estas clases de Java anidadas:



public static class Type {
    public String name;
}

public static class Column {
    public String columnName;
    public Type type;
}

public static class Table {
    public String tableSchema;
    public String tableName;

    public List<Column> columns;
}


Suponiendo que tiene gson o Jackson o JAXB en su classpath (o configurarlos directamente), puede escribir exactamente la misma consulta que antes y usar jOOQ DefaultRecordMapper utilizando el fetchInto(Table.class) llamada:



for (Table t :
    ctx.select(
            jsonObject(
                key("tableSchema").value(COLUMNS.TABLE_SCHEMA),
                key("tableName").value(COLUMNS.TABLE_NAME),
                key("columns").value(jsonArrayAgg(
                    jsonObject(
                        key("columnName").value(COLUMNS.COLUMN_NAME),
                        key("type").value(jsonObject(
                            "name", COLUMNS.DATA_TYPE)
                        )
                    )
                ).orderBy(COLUMNS.ORDINAL_POSITION))
            )
       )
       .from(COLUMNS)
       .where(COLUMNS.TABLE_NAME.in("t_author", "t_book"))
       .groupBy(COLUMNS.TABLE_SCHEMA, COLUMNS.TABLE_NAME)
       .orderBy(COLUMNS.TABLE_SCHEMA, COLUMNS.TABLE_NAME)
       .fetchInto(Table.class))
    System.out.println(t.tableName + ":n" + t.columns
       .stream()
       .map(c -> c.columnName + " (" + c.type.name + ")")
       .collect(joining("n  ")));


Siendo la salida:


t_author:
  id (integer)
  first_name (character varying)
  last_name (character varying)
  date_of_birth (date)
  year_of_birth (integer)
  address (USER-DEFINED)
t_book:
  id (integer)
  author_id (integer)
  co_author_id (integer)
  details_id (integer)
  title (character varying)
  published_in (integer)
  language_id (integer)
  content_text (text)
  content_pdf (bytea)
  status (USER-DEFINED)
  rec_version (integer)
  rec_timestamp (timestamp without time zone)

Sin magia de unión. Sin productos cartesianos. Sin deduplicación de datos. Solo colecciones anidadas de SQL nativo, utilizando un enfoque intuitivo y declarativo para crear la estructura de datos del documento, combinado con la genialidad habitual de SQL.

Usar sin el jOOQ DSL

Por supuesto, esto también funciona sin la API jOOQ, por ejemplo, usando nuestro analizador. Descubra nuestra herramienta de traducción. Conecta esta belleza nativa de SQL:



SELECT
  json_object(
    KEY 'tableSchema' VALUE columns.table_schema,
    KEY 'tableName' VALUE columns.table_name,
    KEY 'columns' VALUE json_arrayagg(
      json_object(
        KEY 'columnName' VALUE columns.column_name,
        KEY 'type' VALUE json_object(
          KEY 'name' VALUE columns.data_type
        )
      )
    )
  )
FROM columns
WHERE columns.table_name IN ('t_author', 't_book')
GROUP BY columns.table_schema, columns.table_name
ORDER BY columns.table_schema, columns.table_name


Y, debido a que el diablo de SQL y el agnosticismo de la traducción están en los detalles, genere la versión específica del proveedor, por ejemplo, para PostgreSQL:



SELECT json_build_object(
  'tableSchema', columns.table_schema,
  'tableName', columns.table_name,
  'columns', json_agg(json_build_object(
    'columnName', columns.column_name,
    'type', json_build_object('name', columns.data_type)
  ))
)
FROM columns
WHERE columns.table_name IN (
  't_author', 't_book'
)
GROUP BY
  columns.table_schema,
  columns.table_name
ORDER BY
  columns.table_schema,
  columns.table_name


Es posible que deba ejecutar esto antes:



SET search_path="information_schema"


Conclusión

Hemos esperado demasiado tiempo con esta característica que cambia el juego. Creo sinceramente que este enfoque cambiará la forma en que vemos a los MNO en el futuro. El primer enfoque de la base de datos, donde podemos usar SQL y solo SQL para asignar datos SQL a cualquier estructura de datos jerárquica, es muy convincente. Del lado de jOOQ, estamos lejos de terminar. ¿Qué pasaría si pudiéramos generar automáticamente parte de la declaración del documento JSON a partir de otros tipos de metadatos para usted? ¿Qué pasaría si pudieras hacerlo tú mismo? Por ejemplo, para asignar una especificación GraphQL a consultas JSON basadas en la API jOOQ. ¡En todos los dialectos de SQL que admiten estas características! El futuro de la asignación de estructuras de datos anidados de SQL a cualquier cliente, XML, JSON, objetos es brillante. jOOQ 3.14 está a la vuelta de la esquina y se lanzará en las próximas 2 semanas. Ya puede compilarlo desde github: https://github.com/jOOQ/jOOQ, o si tiene una licencia, descargue una compilación nocturna desde aquí: https://www.jooq.org/download/versiones sus reacciones.

Si quieres conocer otros artículos parecidos a Colecciones anidadas Con compatibilidad con SQL/XML o SQL/JSON desde jOOQ 3.14 - Java, SQL y jOOQ. puedes visitar la categoría Código.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Esta página web utiliza cookies para analizar de forma anónima y estadística el uso que haces de la web, mejorar los contenidos y tu experiencia de navegación. Para más información accede a la Política de Cookies . Ver mas