【问题标题】:One-to-many select in JooqJooq 中的一对多选择
【发布时间】:2017-07-29 02:36:25
【问题描述】:

我正在尝试 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)));

我还有一个protobuf对象如下:

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

(或任何其他 pojo)将所有实体(作者详细信息 + 书籍列表 + 文章列表)保存到一个对象中。我的问题是,有没有办法使用 JOOQ 将所有三个表都映射到一个对象中。

提前致谢。

【问题讨论】:

  • 您能否再描述一下所需的结果类型(我不知道 protobuf)。 repeated string 是不是像 "book1, book2, ..., bookN" 这样的逗号分隔的串联字符串?还是更像一个数组?
  • 感谢@LukasEder,从 protobuf 生成的作者类将有获取书籍/文章列表的方法。如果您想了解更多有关 protobuf 的信息,可以查看 here。所以在理想情况下,连接查询会给我一个 ProtobufAuthor 或 List 实际上,我们最终按照您在下面的建议做了一些事情。非常感谢您的帮助!
  • 感谢您的反馈。很高兴知道。你介意在你自己的答案中展示你做了什么吗? (可以在 Stack Overflow 上回答自己的问题)。我很想知道你是怎么做到的。

标签: java mysql one-to-many jooq


【解决方案1】:

使用JOIN 对此不起作用。

您的查询效率将相当低,因为如果您以这种方式使用连接,您将在书籍和文章表之间创建一个cartesian product,从而导致数据库和您的Java 客户端,在您对所有无意义的组​​合进行重复数据删除之前。

“正确”的 SQL 方法是使用 MULTISET as described in this article here。不幸的是,jOOQ 3.9 doesn't support MULTISET yet(许多数据库也没有)。因此,您应该创建两个单独的查询:

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

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

从 jOOQ 3.15 开始使用 MULTISET

幸运的是,从 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);

使用安全类型,ad-hoc conversion

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));

For more information about MULTISET, please refer to this blog post,或手册部分:

从 jOOQ 3.14 开始使用 SQL/XML 或 SQL/JSON

从 jOOQ 3.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,而不是空集 []If that's a problem, use COALESCE()

【讨论】:

    猜你喜欢
    • 2022-01-13
    • 1970-01-01
    • 2023-01-18
    • 2021-11-02
    • 2016-05-22
    • 2021-10-01
    • 1970-01-01
    • 2021-04-25
    • 2020-11-27
    相关资源
    最近更新 更多