JOOQ 获取外键表
Posted
技术标签:
【中文标题】JOOQ 获取外键表【英文标题】:JOOQ fetch over foreign keys table 【发布时间】:2021-01-30 08:42:25 【问题描述】:我有三张桌子:
Users
Keys
UserKeys
UserKeys
表具有来自Users
和Keys
表的主键,用于建立用户和键之间的关系。
如何获取带有所有相关键的User
?
如果存在额外的表(例如UserRoles
)等怎么办?一般来说,如何获取一个用户和所有关联的行,通过外键表关联?
【问题讨论】:
在 Jooq 或 SQL 中? @jarlh JOOQ 是我所需要的 【参考方案1】:使用标准 SQL JOIN
我假设您使用的是 jOOQ 的 code generator。编写联接就像在 SQL 中编写联接一样:
ctx.select() // Optionally, list columns here, explicitly
.from(USERS)
.join(USER_KEYS).on(USERS.ID.eq(USER_KEYS.USER_ID))
.join(KEYS).on(USER_KEYS.KEY_ID.eq(KEYS.ID))
.where(USERS.NAME.eq("something"))
.fetch();
嵌套集合
如果存在额外的表(例如 UserRoles)等怎么办?一般来说,如何获取一个用户和所有关联的行,通过外键表关联?
我不确定这是否仍然是同一个问题。上面可能是关于如何进行一般的连接,这一篇似乎更具体的是关于如何获取嵌套集合?
因为JOIN
总是会产生笛卡尔积,这是不可取的,一旦您加入了多对多路径。从 jOOQ 3.14 开始,如果您的数据库支持,您可以使用 SQL/XML 或 SQL/JSON 作为解决方法。从 jOOQ 3.15 开始,您可以使用MULTISET
。例如,JSON 解决方案可能如下所示:
List<User> users =
ctx.select(jsonObject(
jsonEntry("id", USERS.ID),
jsonEntry("name", USERS.NAME),
jsonEntry("keys", field(
select(jsonArrayAgg(jsonObject(KEYS.NAME, KEYS.ID)))
.from(KEYS)
.join(USER_KEYS).on(KEYS.ID.eq(USER_KEYS.KEY_ID))
.where(USER_KEYS.USER_ID.eq(USER.ID))
)),
jsonEntry("roles", field(
select(jsonArrayAgg(jsonObject(ROLES.NAME, ROLES.ID)))
.from(ROLES)
.join(USER_ROLES).on(ROLES.ID.eq(USER_ROLES.ROLE_ID))
.where(USER_ROLES.USER_ID.eq(USER.ID))
))
))
.from(USERS)
.where(USERS.NAME.eq("something"))
.fetchInto(User.class);
假设 User
类看起来像这样,并且您的类路径中有 Gson 或 Jackson 以从 JSON 映射到 Java 数据结构:
class Key
long id;
String name;
class Role
long id;
String name;
class User
long id;
String name;
List<Key> keys;
List<Role> roles;
当然,您不必映射到 Java 数据结构并直接生成 JSON 结果,无需进一步映射。 See also this blog post for more details,或this one explaining how to use MULTISET
。
请注意,JSON_ARRAYAGG()
将空集聚合到 NULL
,而不是空集 []
。 If that's a problem, use COALESCE()
MULTISET
解决方案如下所示:
List<User> users =
ctx.select(
USERS.ID,
USERS.NAME,
multiset(
select(KEYS.NAME, KEYS.ID)
.from(KEYS)
.join(USER_KEYS).on(KEYS.ID.eq(USER_KEYS.KEY_ID))
.where(USER_KEYS.USER_ID.eq(USER.ID))
).as("keys").convertFrom(r -> r.map(Records.mapping(Key::new))),
multiset(
select(ROLES.NAME, ROLES.ID)
.from(ROLES)
.join(USER_ROLES).on(ROLES.ID.eq(USER_ROLES.ROLE_ID))
.where(USER_ROLES.USER_ID.eq(USER.ID))
).as("roles").convertFrom(r -> r.map(Records.mapping(Role::new)))
)
.from(USERS)
.where(USERS.NAME.eq("something"))
.fetch(Records.mapping(User::new));
上述使用各种Records.mapping()
重载和ad-hoc data type conversion 的方法假定存在不可变的构造函数,例如,如果您的类是Java 16 记录,您会得到:
record Key (int id, String name)
record Role (int id, String name)
record User (int id, String name, List<Key> keys, List<Role> roles)
使用多个查询
如果您不能使用上述方法,因为您还不能使用 jOOQ 3.14,或者因为您的 RDBMS 不支持 SQL/XML 或 SQL/JSON,您可以运行多个查询并手动组合结果最后。
【讨论】:
以上是关于JOOQ 获取外键表的主要内容,如果未能解决你的问题,请参考以下文章