在使用 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 配置文件 table 和 n 之间的关系 存储库 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 PROFILES
和 REPOSITORIES
表之间的 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
的工作原理
首先,我为标题道歉,但想不出更好的措辞方式。
其次我有以下 table:
Profiles Table: Primary Key: profileName <----- | Repositories Table: | Composite Primary Keys: (profileName, repository_name)
模拟 1 - n 配置文件 table 和 n 之间的关系 存储库 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 PROFILES
和 REPOSITORIES
表之间的 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
的工作原理