jooq 中的多重嵌套关系

问题描述 投票:0回答:2

我将 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 迁移,这种更改将需要比预期更多的工作。

kotlin jooq
2个回答
1
投票

使用反射

您的初步尝试表明您可以使用基于反射的方法来映射数据,因此您可以继续这样做。这将是最简单的,但缺乏类型安全,有出错的风险。

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
(区分大小写!),那么这个字段的值将被认为是一个嵌套值
MY_nested_field
,它被设置在一个嵌套的POJO

使用类型安全映射器

如果你想要类型安全,你必须在你的查询中模仿目标数据结构,例如


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 列,或者从性能角度来看,明确列出列可能不是更好的方法。


0
投票

我用

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
© www.soinside.com 2019 - 2024. All rights reserved.