sql >> Base de Datos >  >> RDS >> Mysql

Jooq consulta única con una relación de uno a muchos

Hay muchas formas de materializar una colección anidada con SQL y/o con jOOQ. Solo estoy revisando algunos de ellos:

Uso de uniones

Si no anida profundamente esas colecciones, desnormaliza (aplana) tus resultados con JOIN podría ser el truco para usted, sin agregar demasiada sobrecarga ya que los datos se duplican. Básicamente, escribirás:

Map<ExperimentRecord, Result<Record>> map =
DSL.using(configuration)
   .select()
   .from(EXPERIMENT)
   .join(TAGS)
   .on(...)
   .fetchGroups(EXPERIMENT);

El mapa anterior contiene registros de experimentos como claves y colecciones anidadas que contienen todas las etiquetas como valores.

Creando dos consultas

Si desea materializar un gráfico de objeto complejo, es posible que el uso de uniones ya no sea óptimo. En su lugar, probablemente desee recopilar los datos en su cliente a partir de dos consultas distintas:

Result<ExperimentRecord> experiments = 
DSL.using(configuration)
   .selectFrom(EXPERIMENT)
   .fetch();

Y

Result<TagsRecord> tags =
DSL.using(configuration)
   .selectFrom(TAGS)
   .where(... restrict to the previous experiments ...)
   .fetch();
 

Y ahora, combine los dos resultados en la memoria de su cliente, por ejemplo,

experiments.stream()
           .map(e -> new ExperimentWithTags(
                e, 
                tags.stream()
                    .filter(t -> e.getId().equals(t.getExperimentId()))
                    .collect(Collectors.toList())
           ));

Anidamiento de colecciones usando SQL/XML o SQL/JSON

Esta pregunta no lo requería, pero otros pueden encontrar esta pregunta en busca de una forma de anidar a muchas relaciones con jOOQ. He proporcionado una respuesta aquí . A partir de jOOQ 3.14, puede usar las capacidades SQL/XML o SQL/JSON de su RDBMS y luego usar Jackson, Gson o JAXB para anidar colecciones como esta:

List<Experiment> experiments =
ctx.select(
      EXPERIMENT.asterisk(),
      field(
        select(jsonArrayAgg(jsonObject(TAGS.fields())))
        .from(TAGS)
        .where(TAGS.EXPERIMENT_ID.eq(EXPERIMENT.ID))
      ).as("tags")
    )
   .from(EXPERIMENT)
   .fetchInto(Experiment.class);

Donde Experiment es una clase Java personalizada como esta:

class Experiment {
  long id;
  String name;
  List<Tag> tags;
}

class Tag {
  long id;
  String name;
}

Anidar colecciones usando MULTISET

Incluso mejor que lo anterior, puede ocultar usando SQL/XML o SQL/JSON detrás del nuevo MULTISET de jOOQ 3.15 asistencia al operador . Suponiendo que las clases de Java anteriores son registros de Java 16 (o cualquier otra clase inmutable), incluso puede mapear el tipo de colecciones anidadas de forma segura en sus DTO:

List<Experiment> experiments =
ctx.select(
      EXPERIMENT.ID,
      EXPERIMENT.NAME,
      multiset(
        select(TAGS.ID, TAGS.NAME)
        .from(TAGS)
        .where(TAGS.EXPERIMENT_ID.eq(EXPERIMENT.ID))
      ).as("tags").convertFrom(r -> r.map(Records.mapping(Tag::new)))
    )
   .from(EXPERIMENT)
   .fetch(Records.mapping(Experiment::new));

Donde Experiment es una clase Java personalizada como esta:

record Experiment(long id, String name, List<Tag> tags) {}
record Tag(long id, String name) {}

Consulte también esta publicación de blog para obtener más información .