我将 JOOQ 3.17.6 与 Kotlin 一起使用,无法弄清楚如何检索和填充 A->B->C 关系。
给定一些类 Foo->Bar->Baz
data class Foo(
val fooName: String,
val bar: Bar
)
data class Bar(
val barName: String,
val baz: Baz
)
data class Baz(
val bazName: String
)
如果我只想检索
Bar
它可以很容易地完成
context.select(
BAR.barName,
BAR.baz().as("baz")
).from(BAR)
.where(BAR.barName.eq("first"))
fetchOneInto(Bar::class.java)
我遇到的问题是当我想获取完整的树 Foo->Bar->Baz 时做同样的事情。以下代码将失败,因为
baz
的 Bar
类没有填充,因为显然 JOOQ 不知道从哪里得到它。
context.select(
Foo.fooName,
FOO.bar().as("bar")
).from(FOO)
.where(FOO.fooName.eq("first"))
.fetchOneInto(Foo::class.java)
我可以做这里描述的事情https://www.jooq.org/doc/3.17/manual/sql-building/table-expressions/tables-as-selectfield/然后查询看起来像
context.select(
FOO,
FOO.bar(),
FOO.bar().baz()
).where(FOO.fooName.eq("first"))
.fetch()
.map { /* do actual mapping here */ }
但是如果实体更复杂,有 4-5-6 个引用,每个引用都有 10+ 个字段,那么这个映射就变得非常乏味。
另一种方法是在实体上保留 ID 而不是域类型,即
data class Foo(
val name: String,
val barId: String
)
并在我们实际需要引用实体时执行数据库调用,但是随着我们从 Hibernate 迁移,这种更改将需要比预期更多的工作。
您的初步尝试表明您可以使用基于反射的方法来映射数据,因此您可以继续这样做。这将是最简单的,但缺乏类型安全,有出错的风险。
context.select(
Foo.fooName,
FOO.bar().as("bar"),
FOO.bar().baz().as("bar.baz") // This is the magic
).from(FOO)
.where(FOO.fooName.eq("first"))
.fetchOneInto(Foo::class.java)
Settings.namePathSeparator
标记(默认为"."
)来计算DefaultRecordMapper
中嵌套对象的路径。根据 Javadoc:
如果
是Field.getName()
(区分大小写!),那么这个字段的值将被认为是一个嵌套值MY_field.MY_nested_field
,它被设置在一个嵌套的POJOMY_nested_field
如果你想要类型安全,你必须在你的查询中模仿目标数据结构,例如
context.select(
FOO.NAME,
// List all of Foo's columns here
row(
FOO.bar().NAME,
// List all of Bar's columns here
row(
FOO.bar().baz().NAME
// List all of Baz's columns here
).mapping(::Baz)
).mapping(::Bar)
).from(FOO)
.where(FOO.fooName.eq("first"))
.fetchOne(Records.mapping(::Foo))
当然,您也可以通过投影
FOO.bar()
和 FOO.bar().baz()
来添加更多级别的嵌套,以避免单独列出列,但前提是您的目标数据结构也以这种方式嵌套。
这是一个很好的提醒,问问自己是否真的需要投影 all 列,或者从性能角度来看,明确列出列可能不是更好的方法。
我用
namePathSeparator
从以前的 Java 答案中尝试了这种方法,但它不起作用。
我将尝试使用前面的示例来解释我的应用程序中发生了什么。如果我这样写查询
context.select(FOO.FOO_NAME,
FOO.bar().as("bar"),
FOO.bar().baz().as("bar.baz"))
.from(FOO)
.where(FOO.FOO_NAME.eq("first"))
.fetchOneInto(Foo.class);
我得到以下结果:
bar.baz
为空baz
的所有属性覆盖同名的bar
属性(例如bar
的ID值被baz.id
覆盖)bar
的其他属性具有NULL
值