JOOQ 获取外键表

Posted

技术标签:

【中文标题】JOOQ 获取外键表【英文标题】:JOOQ fetch over foreign keys table 【发布时间】:2021-01-30 08:42:25 【问题描述】:

我有三张桌子:

Users
Keys
UserKeys

UserKeys 表具有来自UsersKeys 表的主键,用于建立用户和键之间的关系。

如何获取带有所有相关键的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 获取外键表的主要内容,如果未能解决你的问题,请参考以下文章

sql查询指定表外键约束

数据库中的表怎样设置外键又怎样才能看出已经是外键了

8. EF Core 外键的删除模式

带外键Mysql

sql用命令创建主键与外键。

sql怎么设置外键