在使用JOOQ时如何最好地检索包含相关表项的记录?

时间:2016-08-19 09:25:28

标签: mysql database jooq

我首先为标题道歉,但不能想出一个更好的方式来表达它 其次,我有下表:

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

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

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

工作正常,但我不确定如何改进这一点以包括检索存储库表中的可能的存储库。也就是说,对于配置文件表,存储库不是强制性的而是可选的。 我现在唯一的选择是创建一个第二周期逻辑&#39;在解组数据之前使用配置文件名称检索存储库。

1 个答案:

答案 0 :(得分:3)

将操作推送到数据库

随着数据的增长,您的查询速度会非常慢。为什么?因为您的SQL查询仅在PROFILESREPOSITORIES表之间运行cartesian product,而连接谓词和限制子句则应用于Java内存。

数据库永远不知道你想对这个交叉产品做什么,所以它非常愚蠢地运行这个非常慢的查询。如果通过“将谓词向下推”到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谓词与“普通”谓词明显分开:

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

“可选”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的工作方式