jOOQ 3.15.1 新的 ad-hoc 转换 API 是不是在 Kotlin 中工作?
Posted
技术标签:
【中文标题】jOOQ 3.15.1 新的 ad-hoc 转换 API 是不是在 Kotlin 中工作?【英文标题】:Does the jOOQ 3.15.1 new ad-hoc conversion API work in Kotlin?jOOQ 3.15.1 新的 ad-hoc 转换 API 是否在 Kotlin 中工作? 【发布时间】:2021-10-24 10:29:15 【问题描述】:我正在尝试将工作示例从 jOOQ 3.15's New Multiset Operator Will Change How You Think About SQL 翻译成 Kotlin,但遇到一些类型推断问题阻止我进行编译。
到此为止:
使用sakila
架构
从 jOOQ 代码生成步骤生成 Kotlin
使用来自kotlinx.coroutines.jdk-8
的async()
extension method 使Java 的CompletionStage
未来适应Kotlin 协同例程
data class Actor(val firstName: String?, val lastName: String?)
data class Film(val title: String?, val actors: List<Actor>, val
categories: List<String>)
...
val future = dsl.select(
FILM.TITLE,
multiset(
select(
FILM_ACTOR.actor().FIRST_NAME,
FILM_ACTOR.actor().LAST_NAME
)
.from(FILM_ACTOR)
.where(FILM_ACTOR.FILM_ID.eq(FILM.FILM_ID))
).`as`("actors").convertFrom it.map( mapping(::Actor) ) , // <--[1]
multiset(
select(FILM_CATEGORY.category().NAME)
.from(FILM_CATEGORY)
.where(FILM_CATEGORY.FILM_ID.eq(FILM.FILM_ID))
).`as`("films").convertFrom it.map r -> r.getValue(0)
)
.from(FILM)
.orderBy(FILM.TITLE)
.fetchAsync()
val result = future.await().map( mapping(::Film) ) // <--[2]
我遇到的问题在代码中的 [1] 和 [2] 处注明。
在 [1] 中,当获取构造函数引用时,它归结为 Kotlin 与 Java:
Type mismatch.
Required: ((String?, String?) → Actor!)!
Found: KFunction2<String, String, Actor>
... 很接近,但没有雪茄。可能有一种我缺少的简单方法来适应它(除了手动调用构造函数)。
在 [2] 处,Kotlin 无法决定选择 mapping(...)
的哪个重载。
我目前不明白为什么每个案例的问题都不同。
请问mapping(/*constructor reference*/): RecordManager<R,U>
是否有望在 jOOQ 3.15.1 中与 Kotlin 一起使用? 如果没有,我可以合理地做些什么来解决它?
【问题讨论】:
我只想说,除了这个小的互操作边缘案例,我完全对 MULTISET 功能感到敬畏。使用 jOOQ 就像驾驶火箭! 关于await()
增加了上述内容的复杂性,我将讨论移至SO: Does jOOQ play nicely with kotlin coroutines?
【参考方案1】:
关于问题 [1],有时不能使属性为空:您的整个应用程序将被此可空属性污染。即使该列在数据库中可能被指定为 NOT NULL,jOOQ (3.15.1) 目前不提供非空记录字段(因为它们不能保证在例如外连接中是非空的)。
对于不可为空的属性,到目前为止我发现的最简单的解决方案是这种构造:
multiset(
select(
FILM_ACTOR.actor().FIRST_NAME,
FILM_ACTOR.actor().LAST_NAME
)
.from(FILM_ACTOR)
.where(FILM_ACTOR.FILM_ID.eq(FILM.FILM_ID))
).`as`("actors").convertFrom(List::class.java) it.map s -> Actor(s.value1()!!, s.value2()!!) ,
【讨论】:
谢谢,这很有帮助。只需要多几十个字符就可以得到你想要的类型,如果你经常这样做,你可以写一个(Record2<X?,Y?>) -> Z
函数并重复使用它。【参考方案2】:
你的错误[1]
您确定您在测试中使数据类中的属性为空吗?错误信息是:
Type mismatch. Required: ((String?, String?) → Actor!)! Found: KFunction2<String, String, Actor>
后者应该改为KFunction2<String?, String?, Actor>
。
你的错误[2]
不确定 kotlinx-coroutines 库在这里做了什么,但如果您使用 JDK CompletionStage
API,这也适用于我(应用了正确的可空性):
.fetchAsync().thenApply mapping(::Film)
也许这也是应用了错误的可空性的副作用
【讨论】:
您对#1 的看法是正确的。我的错 - 我在用我自己的mapping()
函数添加我的答案时对其进行了排序,然后将这些行添加到问题中。 #2 需要 2 次修复:a) IDEA 已从 jOOQ 生成的表类中导入符号 Film
,甚至没有使用我的 data class Film(...)
,并且 b) 我不得不将 r -> r.getValue(0)
更改为 @ 987654331@。我会修改我的答案来写这个。
@DavidBullock 啊,是的,getValue(int)
方法不知道列的类型。感谢您用自己的答案记录您的发现!我认为这个问题对未来的访问者仍然非常有用,因为他们可能会遇到类似的打字问题,尤其是可空性问题。【参考方案3】:
答:是的!
这是与jOOQ 3.15's New Multiset Operator Will Change How You Think About SQL 的最终工作示例最直接的 Kotlin 等价物,而不会增加 fetchAsync()
的任何复杂性。
数据类声明的属性必须都是可以为空的类型。
data class Actor(val firstName: String?, val lastName: String?)
data class Film(val title: String?, val actors: List<Actor>?, val categories: List<String>?)
然后
val result = dsl.select(
FILM.TITLE,
multiset(
select(
FILM_ACTOR.actor().FIRST_NAME,
FILM_ACTOR.actor().LAST_NAME
)
.from(FILM_ACTOR)
.where(FILM_ACTOR.FILM_ID.eq(FILM.FILM_ID))
).`as`("actors").convertFrom it?.map( mapping(::Actor) ) ,
multiset(
select(FILM_CATEGORY.category().NAME)
.from(FILM_CATEGORY)
.where(FILM_CATEGORY.FILM_ID.eq(FILM.FILM_ID))
).`as`("films").convertFrom it?.map r -> r.getValue1()
)
.from(FILM)
.orderBy(FILM.TITLE)
.fetch( mapping(::Film) )
陷阱
我包含的问题:
不让我的数据/记录类的所有属性都使用可为空的类型 不小心使用Record1#getValue()
而不是Record1#getValue1()
来映射电影的名称,因此有一个Any!
而不是String!
我的 data class Film(...)
没有被使用,因为 IDEA 导入了 jOOQ 生成的同名类而不是使用我的类
在上述所有情况下,问题都表现为“错误类型推断”问题以及令人眼花缭乱的演示。
我也有:
convertFrom...
块中的 NPE,根据表中的数据,it
可能为空! (在未修改的 Sakila 数据库中,我遇到了author
的问题)
麻烦调用Record#map(RecordMapper)
...有时使用mapping(::MyClass)
来获得RecordMapper
(在这种情况下,map
()
是正确的调用方式),有时使用带有签名 R -> T
(在这种情况下为 map
)的 lambda 是正确的调用样式。使用后一种风格,但通过
mapping()
提供RecordMapper
稍后会给您错误的类型!例如。
convertFrom it?.map( mapping(::Actor) // <- gives List<Actor> in the object graph's type after fetch()
对
convertFrom it?.map mapping(::Actor) // <- gives List<RecordMapper<Record3<...etc...>,Actor>> in the object's graph type after fetch()
【讨论】:
以上是关于jOOQ 3.15.1 新的 ad-hoc 转换 API 是不是在 Kotlin 中工作?的主要内容,如果未能解决你的问题,请参考以下文章