将一对一和一对多关系映射到记录的类型安全

发布于 2025-01-21 00:51:48 字数 1633 浏览 4 评论 0原文

我想将一对一和一对多关系映射到生成的记录,最好以类型安全的方式。

我现在可以使用 Java 记录进行此操作,但我宁愿使用代码生成的记录,这样我以后就可以更轻松地更新它们,尽管我不需要记录的所有列。

我还遇到的问题不是不将联系人映射到列表,而是映射到列表,同时它确实正确映射了地址。

record BulkContactEdit(long id, String organizationName, List < Contact > contacts, Address visitedAddress) {}

record Contact(long id) {}

record Address(long id, String address, String state, String city, String zipcode, Long country_id) {}

var bce = BULK_CONTACT_EDIT.as("bce");
var bceContacts = BULK_CONTACT_EDIT_CONTACTS.as("bceContacts");
var rel = RELATION.as("rel");

var bceAddress = ADDRESS.as("bceAddress");

var query = jooq().select(
bce.ID, bce.ORGANIZATION_NAME,
// For some reason this doesn't map.
//                DSL.multisetAgg(rel.ID)
//                    .as("contacts")
//                    .convertFrom(r -> r.map(mapping(Contact::new))),
jsonArrayAgg(jsonObject(rel.ID)).as("contacts"), jsonObject(bceAddress.fields()).as("visitedAddress"))
// Same as above, but with redundant sub-query.
//                DSL.field(
//                        jooq()
//                            .select(jsonArrayAgg(jsonObject(rel.ID)))
//                            .from(rel)
//                            .where(rel.ID.eq(bceContacts.CONTACT_ID)))
//                    .as("contacts"))
.from(
bce.join(bceContacts).on(bceContacts.BULK_CONTACT_EDIT_ID.eq(bce.ID)).join(rel).on(rel.ID.eq(bceContacts.CONTACT_ID)).leftJoin(bceAddress).on(bceAddress.ID.eq(bce.VISIT_ADDRESS_ID)).where(bce.ID.eq(bulkContactEditId)));

var bulkContactEdit = query.fetchOneInto(BulkContactEdit.class);

I want to map one-to-one and one-to-many relationships to generated Records, preferably in a type-safe manner.

I now have this working with Java records, but I would rather use the code generated Records, so I can update them easier afterwards, though I don't need all the columns of a Record.

I also have the problem that isn't doens't map the contacts to a List but to a List while it does map the Address correctly.

record BulkContactEdit(long id, String organizationName, List < Contact > contacts, Address visitedAddress) {}

record Contact(long id) {}

record Address(long id, String address, String state, String city, String zipcode, Long country_id) {}

var bce = BULK_CONTACT_EDIT.as("bce");
var bceContacts = BULK_CONTACT_EDIT_CONTACTS.as("bceContacts");
var rel = RELATION.as("rel");

var bceAddress = ADDRESS.as("bceAddress");

var query = jooq().select(
bce.ID, bce.ORGANIZATION_NAME,
// For some reason this doesn't map.
//                DSL.multisetAgg(rel.ID)
//                    .as("contacts")
//                    .convertFrom(r -> r.map(mapping(Contact::new))),
jsonArrayAgg(jsonObject(rel.ID)).as("contacts"), jsonObject(bceAddress.fields()).as("visitedAddress"))
// Same as above, but with redundant sub-query.
//                DSL.field(
//                        jooq()
//                            .select(jsonArrayAgg(jsonObject(rel.ID)))
//                            .from(rel)
//                            .where(rel.ID.eq(bceContacts.CONTACT_ID)))
//                    .as("contacts"))
.from(
bce.join(bceContacts).on(bceContacts.BULK_CONTACT_EDIT_ID.eq(bce.ID)).join(rel).on(rel.ID.eq(bceContacts.CONTACT_ID)).leftJoin(bceAddress).on(bceAddress.ID.eq(bce.VISIT_ADDRESS_ID)).where(bce.ID.eq(bulkContactEditId)));

var bulkContactEdit = query.fetchOneInto(BulkContactEdit.class);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

别念他 2025-01-28 00:51:48

关于类型安全

我想将一对一和一对多关系映射到生成的记录,最好以类型安全的方式。

如果您想要类型安全,则不应使用已使用过的 2 个功能:

  • 手动使用 SQL/JSON API,这会生成缺乏类型安全的通用 JSON 文档
  • 通过 into(X.class) 调用,使用 DefaultRecordMapper 的反射映射功能。

上面的做法没有什么问题。 jOOQ 3.14 引入了 SQL/JSON 支持以及 如果在类路径上找到,则使用 Gson 或 Jackson 将 JSON 文档映射到 Java 对象。该方法不是类型安全的。

现在,让我们单独看看您的个人问题,您似乎在混合关注点...

一对一映射

从 jOOQ 3.17 开始(尚未发布,但很快),Table 类型扩展SelectField 为了方便,这意味着您可以将任何表表达式投影为嵌套记录,并将其映射到您自己的自定义记录(或不映射):

// Using implicit joins, mixing scalar values and nested records
Result<Record3<Long, String, AddressRecord>> r1 =
ctx.select(bce.ID, bce.ORGANIZATION_NAME, bce.address())
   .from(bce)
   .fetch();

// Using implicit joins, projecting only nested records
Result<Record2<BulkContactEditRecord, AddressRecord>> r1 =
ctx.select(bce, bce.address())
   .from(bce)
   .fetch();

虽然不是必需的,但上面的示例使用 隐式join,这大大简化了这样的查询。当然,您可以像以前一样继续使用显式联接。

在 jOOQ 3.16 中,您可以通过 嵌套row()表达式,像这样。将嵌套记录映射到类型化的生成的 UpdatableRecord 时不存在类型安全,但我认为这是可以接受的,因为您仍在使用生成的代码,因此实际上不会有任何映射错误:

// Using implicit joins, mixing scalar values and nested records
Result<Record3<Long, String, AddressRecord>> r1 =
ctx.select(
        bce.ID, 
        bce.ORGANIZATION_NAME, 
        row(bce.address().fields()).convertFrom(r -> r.into(ADDRESS)))
   .from(bce)
   .fetch();

// Using implicit joins, projecting only nested records
Result<Record2<BulkContactEditRecord, AddressRecord>> r1 =
ctx.select(
        row(bce.fields()).convertFrom(r -> r.into(BULK_CONTACT_EDIT)), 
        row(bce.address().fields()).convertFrom(r -> r.into(ADDRESS)))
   .from(bce)
   .fetch();

一对多映射

当您使用 multisetAgg(rel.ID) 方法时,您的代码中似乎出现了问题,但我看不出可能是什么问题你的问题。另外,这与您上一个问题并没有什么不同,上一个问题已经有关于如何执行此操作的答案

你的两个问题之间的唯一区别是,在这里,你不喜欢使用 Java 记录,而是使用 jOOQ 生成的记录,在这种情况下,你将使用以下习惯用法:

Field<Result<RelationRecord>> f = multisetAgg(rel.ID).convertFrom(r -> r.map(RELATION));

Regarding type safety

I want to map one-to-one and one-to-many relationships to generated Records, preferably in a type-safe manner.

If you want type safety, you should not use 2 of the features you've used:

  • Manual SQL/JSON API usage, which produces generic JSON documents that lack type safety
  • Reflective mapping via into(X.class) calls, which make use of the DefaultRecordMapper's reflective mapping capabilities.

There's nothing wrong with the above approach. jOOQ 3.14 introduced SQL/JSON support as well as the capability of mapping JSON documents to Java objects using Gson or Jackson, if found on the classpath. The approach is just not type safe.

Now, let's look at your individual questions individually, you seem to be mixing concerns...

One-to-one mapping

Starting with jOOQ 3.17 (not yet released, but soon), the Table<R> type extends SelectField<R> for convenience, which means you can project any table expression as a nested record, and map it to your own custom record (or not):

// Using implicit joins, mixing scalar values and nested records
Result<Record3<Long, String, AddressRecord>> r1 =
ctx.select(bce.ID, bce.ORGANIZATION_NAME, bce.address())
   .from(bce)
   .fetch();

// Using implicit joins, projecting only nested records
Result<Record2<BulkContactEditRecord, AddressRecord>> r1 =
ctx.select(bce, bce.address())
   .from(bce)
   .fetch();

While not necessary, the above example makes use of implicit joins, which greatly simplify a query like this. Of course, you can continue using explicit joins like you did.

In jOOQ 3.16, you can do this via an explicit projection of a nested row() expression, like this. There is no type safety when mapping the nested record to a typed, generated UpdatableRecord, but I think that's acceptable, given that you're still using generated code and as such can't really have any mapping errors:

// Using implicit joins, mixing scalar values and nested records
Result<Record3<Long, String, AddressRecord>> r1 =
ctx.select(
        bce.ID, 
        bce.ORGANIZATION_NAME, 
        row(bce.address().fields()).convertFrom(r -> r.into(ADDRESS)))
   .from(bce)
   .fetch();

// Using implicit joins, projecting only nested records
Result<Record2<BulkContactEditRecord, AddressRecord>> r1 =
ctx.select(
        row(bce.fields()).convertFrom(r -> r.into(BULK_CONTACT_EDIT)), 
        row(bce.address().fields()).convertFrom(r -> r.into(ADDRESS)))
   .from(bce)
   .fetch();

One-to-many mapping

There seems to have been something going wrong in your code when you used the multisetAgg(rel.ID) approach, but I can't see what could have been the problem from your question. Also, it's not really a different question from your previous one, which already has an answer on how to do this.

The only difference between your two questions is that here, you prefer not to use a Java record, but jOOQ's generated records, in case of which you'll use this idiom:

Field<Result<RelationRecord>> f = multisetAgg(rel.ID).convertFrom(r -> r.map(RELATION));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文