Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

jooq consulta única com relacionamento um para muitos


Existem muitas maneiras de materializar uma coleção aninhada com SQL e/ou com jOOQ. Estou passando por alguns deles:

Usando junções


Se você não aninhar profundamente essas coleções, desnormalizando (achatando) seus resultados com um JOIN pode fazer o truque para você, sem adicionar muita sobrecarga à medida que os dados estão sendo duplicados. Essencialmente, você escreverá:
Map<ExperimentRecord, Result<Record>> map =
DSL.using(configuration)
   .select()
   .from(EXPERIMENT)
   .join(TAGS)
   .on(...)
   .fetchGroups(EXPERIMENT);

O mapa acima contém registros de experimentos como chaves e coleções aninhadas contendo todas as tags como valores.

Criando duas consultas


Se você deseja materializar um gráfico de objeto complexo, o uso de junções pode não ser mais ideal. Em vez disso, você provavelmente deseja coletar os dados em seu cliente de duas consultas distintas:
Result<ExperimentRecord> experiments = 
DSL.using(configuration)
   .selectFrom(EXPERIMENT)
   .fetch();

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

E agora, mescle os dois resultados na memória do seu cliente, por exemplo
experiments.stream()
           .map(e -> new ExperimentWithTags(
                e, 
                tags.stream()
                    .filter(t -> e.getId().equals(t.getExperimentId()))
                    .collect(Collectors.toList())
           ));

Aninhando coleções usando SQL/XML ou SQL/JSON


Esta pergunta não exigia isso, mas outros podem encontrar essa pergunta em busca de uma maneira de aninhar muitos relacionamentos com jOOQ. Forneci uma resposta aqui . A partir do jOOQ 3.14, você pode usar os recursos SQL/XML ou SQL/JSON do seu RDBMS e, em seguida, usar Jackson, Gson ou JAXB para aninhar coleções 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);

Onde Experiment é uma classe Java personalizada como esta:
class Experiment {
  long id;
  String name;
  List<Tag> tags;
}

class Tag {
  long id;
  String name;
}

Aninhando coleções usando MULTISET


Ainda melhor que o acima, você pode se esconder usando SQL/XML ou SQL/JSON atrás do novo MULTISET do jOOQ 3.15 suporte ao operador . Supondo que as classes Java acima sejam registros Java 16 (ou quaisquer outras classes imutáveis), você pode até mapear o tipo de coleções aninhadas com segurança em seus DTOs:
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));

Onde Experiment é uma classe Java personalizada como esta:
record Experiment(long id, String name, List<Tag> tags) {}
record Tag(long id, String name) {}

Veja também esta postagem do blog para obter mais informações .