在使用 JOOQ 时如何最好地检索具有相关 table 项的记录?

How best to retrieve a record with related table items in using JOOQ?

首先,我为标题道歉,但想不出更好的措辞方式。
其次我有以下 table:

Profiles Table:
Primary Key: profileName <-----
                              |
Repositories Table:           |
Composite Primary Keys: (profileName, repository_name)

模拟 1 - n 配置文件 tablen 之间的关系 存储库 table。 我最近发现 jooq 并使用它从数据库中检索和存储数据,并使用此代码从数据库中检索配置文件:

profile = db.select().from(Profiles.PROFILES, Repositories.REPOSITORIES).fetch().stream()
                .filter(t -> t.getValue(Profiles.PROFILES.PROFILENAME).equalsIgnoreCase(profileName))
                .limit(1) //limit it to just the one result
                .map(this::convertToProfile)
                .collect(Collectors.toList()).get(0);

工作正常,但我不确定如何改进它以包括检索在存储库 table 中找到的 可能的存储库 。也就是说,对于配置文件 table,存储库不是强制性的,而是可选的。 我现在唯一的选择是创建一个 'second cycle logic' 以在解组数据之前使用配置文件名称检索存储库。

将操作推送到数据库

随着数据的增长,您的查询会变得很慢。为什么?因为您的 SQL 仅查询 运行s PROFILESREPOSITORIES 表之间的 cartesian product,而连接谓词和限制子句随后应用于 Java记忆.

数据库永远不知道你想用那个叉积做什么,所以它 运行 非常愚蠢地处理这个非常慢的查询。如果您通过 "pushing the predicate down" 为数据库提供更多信息到 jOOQ/SQL 查询中,整个过程将 运行 快得多(尽管,流结果在技术上是等效的)。所以,改为这样写:

profile = db.select()
            .from(PROFILES, REPOSITORIES)
            // WHERE and LIMIT are moved "up" into the SQL query:
            .where(PROFILES.PROFILENAME.equalIgnoreCase(profileName))
            .limit(1)
            .fetch().stream()
            .map(this::convertToProfile)
            .collect(Collectors.toList()).get(0);

此查询与您的相同(尚未正确),但速度更快

正确获取连接

上面的查询仍然运行是两个表之间的笛卡尔积。相反,您可能想加入他们。 SQL有两种加入方式:

使用 WHERE 子句

只需在 where 子句中添加一个 JOIN 谓词即可

profile = db.select()
            .from(PROFILES, REPOSITORIES)
            // Join predicate here:
            .where(PROFILES.PROFILENAME.equal(REPOSITORIES.PROFILENAME))
            .and(PROFILES.PROFILENAME.equalIgnoreCase(profileName))
            .limit(1)
            .fetch().stream()
            .map(this::convertToProfile)
            .collect(Collectors.toList()).get(0);

这也称为INNER JOIN,可以使用JOIN子句编写以提高可读性:

使用 (INNER) JOIN 子句:

大多数人会发现此语法更具可读性,因为 JOIN 谓词与 "ordinary" 谓词明显分开:

profile = db.select()
            .from(PROFILES)
            // Join expression and predicates here:
            .join(REPOSITORIES)
            .on(PROFILES.PROFILENAME.equal(REPOSITORIES.PROFILENAME))
            // Ordinary predicates remain in the where clause:
            .where(PROFILES.PROFILENAME.equalIgnoreCase(profileName))
            .limit(1)
            .fetch().stream()
            .map(this::convertToProfile)
            .collect(Collectors.toList()).get(0);

"optional" JOIN

在 SQL 中,这称为 OUTER JOIN,或者更具体地说是 LEFT (OUTER) JOIN:

profile = db.select()
            .from(PROFILES)
            // LEFT JOIN expression and predicates here:
            .leftJoin(REPOSITORIES)
            .on(PROFILES.PROFILENAME.equal(REPOSITORIES.PROFILENAME))
            .where(PROFILES.PROFILENAME.equalIgnoreCase(profileName))
            .limit(1)
            .fetch().stream()
            .map(this::convertToProfile)
            .collect(Collectors.toList()).get(0);

请注意,REPOSITORIES 的列表不会为空,而是包含一个所有值都设置为 NULL 的存储库。这就是 OUTER JOIN 的工作原理