当前位置: 首页 > 知识库问答 >
问题:

Jooq中的一对多选择

冯玮
2023-03-14

我正在尝试JOOQ,并试图使用连接语句从3个表(作者,书籍和文章)中进行选择。ERD如下:

Author ----< Books
   |
   |
   ^
Articles

我的问题如下:

    final List<Tuple3<AuthorRecord, BooksRecord, ArticlesRecord>> tupleList =
        persistenceContext.getDslContext()
            .select()
            .from(Author.AUTHOR)
            .join(Books.BOOKS)
            .on(Author.AUTHOR.ID.eq(Books.BOOKS.AUTHOR_ID))
            .join(Articles.ARTICLES)
            .on(Author.AUTHOR.ID.eq(Articles.ARTICLES.AUTHOR_ID))
            .where(Author.AUTHOR.ID.eq(id))
            .fetch()
            .map(r -> Tuple.tuple(r.into(Author.AUTHOR).into(AuthorRecord.class),
                r.into(Books.BOOKS).into(BooksRecord.class),
                r.into(Articles.ARTICLES).into(ArticlesRecord.class)));

我也有一个原型对象如下:

message Author {
    int64 id = 1;
    string name = 2;
    repeated string books = 3;
    repeated string articles = 4;
}

(或任何其他pojo)将所有实体(作者详细信息图书列表文章列表)保存到一个对象中。我的问题是,是否有某种方法可以使用JOOQ将所有三个表映射到一个对象中。

提前谢谢。

共有1个答案

秦跃
2023-03-14

您的查询将相当低效,因为如果您以这种方式使用连接,您将在图书和文章表之间创建笛卡尔乘积,导致数据库和Java客户端中的内存和CPU消耗相当大-复制所有无意义的组合。

正确的SQL方法是使用MULTISET,如本文所述。不幸的是,jOOQ 3.9还不支持MULTISET(许多数据库也不支持)。因此,您应该创建两个独立的查询:

  1. 取所有的书
  2. 获取所有文章

然后使用Java8流之类的东西将它们映射到单个对象中。

幸运的是,从jOOQ 3.15开始,有一个现成的解决方案可以使用MULTISET在SQL中嵌套集合。您的查询如下所示:

使用反射

List<Author> authors =
ctx.select(
      AUTHOR.ID,
      AUTHOR.NAME,
      multiset(
        select(BOOKS.TITLE)
        .from(BOOKS)
        .where(BOOKS.AUTHOR_ID.eq(AUTHOR.ID))
      ).as("books"),
      multiset(
        select(ARTICLES.TITLE)
        .from(ARTICLES)
        .where(ARTICLES.AUTHOR_ID.eq(AUTHOR.ID))
      ).as("articles")
    )
   .from(AUTHOR)
   .where(AUTHOR.ID.eq(id))
   .fetchInto(Author.class);

使用类型安全的特殊转换

java prettyprint-override">List<Author> authors =
ctx.select(
      AUTHOR.ID,
      AUTHOR.NAME,
      multiset(
        select(BOOKS.TITLE)
        .from(BOOKS)
        .where(BOOKS.AUTHOR_ID.eq(AUTHOR.ID))
      ).as("books").convertFrom(r -> r.map(Record1::value1)),
      multiset(
        select(ARTICLES.TITLE)
        .from(ARTICLES)
        .where(ARTICLES.AUTHOR_ID.eq(AUTHOR.ID))
      ).as("articles").convertFrom(r -> r.map(Record1::value1))
    )
   .from(AUTHOR)
   .where(AUTHOR.ID.eq(id))
   .fetch(Records.mapping(Author::new));

有关MULTISET的更多信息,请参阅本博文或手册部分:

  • MULTISET值构造函数
  • MULTISET\u AGG

从JOOQ3.14开始,如果RDBMS支持,您可以通过SQL/XML或SQL/JSON嵌套集合。您可以生成一个文档,然后使用Gson、Jackson或JAXB之类的工具将其映射回您的Java类。例如:

List<Author> authors =
ctx.select(
      AUTHOR.ID,
      AUTHOR.NAME,
      field(
        select(jsonArrayAgg(BOOKS.TITLE))
        .from(BOOKS)
        .where(BOOKS.AUTHOR_ID.eq(AUTHOR.ID))
      ).as("books"),
      field(
        select(jsonArrayAgg(ARTICLES.TITLE))
        .from(ARTICLES)
        .where(ARTICLES.AUTHOR_ID.eq(AUTHOR.ID))
      ).as("articles")
    )
   .from(AUTHOR)
   .where(AUTHOR.ID.eq(id))
   .fetchInto(Author.class);

请注意,JSON_ARRAYAGG()将空集聚合为NULL,而不是聚合为空的[]。如果这是一个问题,请使用COALESCE()

 类似资料:
  • 我正在努力理解如何处理与JOOQ的一对多和多对多关系的Pojo。 我存储玩家创建的位置(一对多关系)。一个位置可以容纳多个可能访问它的其他玩家(多对多)。数据库布局可归结为以下内容: 在我的java应用程序中,所有这些信息都存储在一个pojo中。请注意,玩家和受邀玩家列表可以从应用程序中更新,也需要在数据库中更新: 我可以使用JOOQ的pojo映射将这三个记录映射到单个pojo吗?我可以使用这个p

  • 根据这篇文章,我正在尝试反序列化与JooQ的一对多关联(没有代码生成)。 这是我的目标课程。 我的JooQ查询如下: 方法无法按预期工作。生成的SQL语句如下所示: 翻译后的postgres查询没有正确替换的key属性,这会导致SQL异常。 PS:我正在使用JooQ 3.14.0和postgres 11.5

  • 我想在SQL中执行以下操作: 在jOOQ中,我将subQ存储到Select中 我的问题是,如何从subQ中获取maxCol列,并在join中使用它?我的连接是这样的: 我在()上出错 类型字段中的方法eq(字符串)不适用于参数(字段) 我该怎么办?

  • 我使用jOOQ和Postgreql从表中选择枚举值。 不管怎样,我得到了一个例外: 我看到将返回

  • 问题内容: 我有桌子: 我想写一个返回BookName,FirstName,LastName,RubricName的SELECT查询。 我有两个单独的查询,但我不知道如何在一个选择中编写此查询。这就是我得到的: 它工作正常,但如何在一个选择中编写? 我尝试这样做,但是不起作用: 它返回错误: 问题答案: 试试这个查询

  • 我有一个桌子实验和一个桌子标签。一个实验可能有许多标签。架构: 是否可以使用返回实验和相应标签列表的jooq创建查询? 类似