JOOQ 获取外键 table

JOOQ fetch over foreign keys table

我有三个table:

Users
Keys
UserKeys

UserKeys table 具有来自 UsersKeys table 的主键以建立用户和键之间的关系。

如何获取 User 及其所有相关键?

如果存在额外的 tables(例如 UserRoles)等等。一般来说,如何获取用户和所有关联的行,通过外键关联 tables ?

使用标准 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();

嵌套集合

What if additional tables exist (for instance UserRoles), etc. In general, how to fetch a user and all associated rows, related via foreign keys tables?

我不确定这是否仍然是同一个问题。上面可能是关于如何进行一般的连接,这个似乎更具体地讲了如何获取嵌套集合?

因为一旦加入多个 to-many 路径,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 class 看起来像这样,并且您的 class 路径上有 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, or this one explaining how to use MULTISET.

请注意,JSON_ARRAYAGG() 将空集聚合为 NULL,而不是空集 []

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() overloads along with ad-hoc data type conversion 的方法假定存在不可变的构造函数,例如如果您的 classes 是 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,您可以 运行 几个查询和 assemble 手动结果。