Slick/Scala传递TableQuery参数出现奇怪类型错误,注入则正常
嘿,这个问题其实是个很容易踩的括号优先级坑!咱们一步步拆解来看:
核心原因:方法调用顺序被括号搞反了
你的柯里化代码里,returning方法的调用括号位置写错了!Slick的returning API逻辑是先指定要返回的列,再执行插入操作,但你现在的写法把+=的结果当成了returning的参数,直接搞反了调用顺序。
看你出错的代码行:
val action = clients.returning( clients.map(_.clientId) += toRow(info) )
这里的括号把clients.map(_.clientId) += toRow(info)整个包了起来,相当于先执行插入操作(返回类型是DBIOAction[ClientRow, ...]),再把这个结果传给returning——但returning期望接收的是要返回的列(比如clients.map(_.clientId),类型是Rep[Long]),这就直接导致了类型不匹配:编译器看到你传了个ClientRow相关的对象,却需要Long类型的列定义,自然报错。
而你在类里注入依赖的版本,括号位置是完全正确的:
val action = clients.returning(clients.map(_.clientId)) += toRow(info)
这里returning(clients.map(_.clientId))先构建出支持返回指定列的查询对象,再链式调用+=执行插入,完全符合Slick的API设计,所以编译正常。
修正后的柯里化函数
只需要调整括号位置,把+= toRow(info)移到returning调用的外面就行:
val persist: PersistClient[ClientTable] = db => clients => info => { val action = clients.returning(clients.map(_.clientId)) += toRow(info) db.run(action).map(constructId(_)) }
修正后,returning会先拿到要返回的clientId列,再执行插入操作,此时action的类型是DBIOAction[Long, NoStream, Effect.Write],正好和后续db.run以及constructId的类型要求匹配,编译错误就消失了。
为啥类注入版本没这个问题?
说白了就是括号优先级的锅:类注入的代码里,你正确拆分了方法调用步骤,而柯里化版本的括号错误改变了代码的执行逻辑,把插入操作的结果传给了本应接收列定义的returning方法,才触发了类型不匹配的错误。
内容的提问来源于stack exchange,提问作者Nimrod Sadeh

