【发布时间】:2019-09-13 16:38:05
【问题描述】:
写在 Jooq 助手类上。 (一旦我弄清楚这一点,就会添加更多特定于业务的方法......)
import org.jooq.*
import org.springframework.stereotype.Repository
import javax.inject.Inject
import javax.inject.Provider
/**A helper class facilitating database manipulations.
* Uses jOOQ for its manipulations.
* Relies on the application context being properly initialized s.t. Spring creates and injects the correct
* [DSLContext].*/
@Repository
class DatabaseManipulator{
private val provider: Provider<DSLContext>
private val create:DSLContext
get() = provider.get()
@Inject
constructor(provider: Provider<DSLContext>) {
this.provider = provider
}
/**Executes non-[Select]-type SQL queries on the database.
* Does not perform any kind of checks and trusts the client to know what they're doing.
* @param query the query to be executed.
*
* @return depending on the type of the [query]:
* <ul>
* <li> Delete : the number of deleted records</li>
* <li> Insert : the number of inserted records</li>
* <li> Merge : result may be meaningless</li>
* <li> Truncate : result may be meaningless</li>
* <li> Update : the number of updated records</li>
* </ul>
*/
fun execute(query:DSLContext.()-> Query):Int{
return create.query().execute()
}
/**Executes [Select]-type [query] and returns its result*/
fun <T:Record> execute(query:DSLContext.()->Select<T>):Result<T>{
return create.query().fetch()
}
}
到目前为止,一切都很好。现在让我们添加一些测试
@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest
@Transactional
class DatabaseManipulatorIT {
@Inject
private lateinit var manipulate:DatabaseManipulator
@Inject
private lateinit var em:EntityManager
@Test
fun executeSelectQuery() {
//given
val expectedNumberOfOrganizations = em.createQuery("SELECT COUNT (o) FROM Organization o")
.singleResult as Long
//when
val reportedNumberOfOrganizations = manipulate.execute {
selectCount().from(ORGANIZATION)
}.first().value1().toLong()
//then
assertThat(reportedNumberOfOrganizations).isEqualTo(expectedNumberOfOrganizations)
}
@Test
fun executeNonSelectQuery() {
//given
val expectedNumberOfDeletions = em.createQuery(
"SELECT COUNT (o) FROM Organization o WHERE o.usageCreditLimited = true"
).singleResult as Long
//when
val actualNumberOfDeletions = manipulate.execute {(
deleteFrom(ORGANIZATION)
.where(ORGANIZATION.USAGECREDITLIMITED.eq(true))
) as Query
}
//then
assertThat(actualNumberOfDeletions).isEqualTo(expectedNumberOfDeletions)
}
}
编译失败是因为
Error:(50, 50) Kotlin: Type inference failed: fun <T : Record> execute(query: DSLContext.() -> Select<T>): Result<T>
cannot be applied to
(DSLContext.() -> DeleteConditionStep<OrganizationRecord!>!)
Error:(50, 58) Kotlin: Type mismatch: inferred type is DSLContext.() -> DeleteConditionStep<OrganizationRecord!>! but DSLContext.() -> Select<???> was expected
不,我不想想要你使用那个方法,我想要你使用其他。
让我们试着明确一点:
//when
val actualNumberOfDeletions = manipulate.execute {(
deleteFrom(ORGANIZATION)
.where(ORGANIZATION.USAGECREDITLIMITED.eq(true))
) as Query
}
仍然无法编译第二个测试用例,因为
Error:(50, 50) Kotlin: Type inference failed: fun <T : Record> execute(query: DSLContext.() -> Select<T>): Result<T>
cannot be applied to
(DSLContext.() -> Query)
Error:(50, 58) Kotlin: Type mismatch: inferred type is DSLContext.() -> Query but DSLContext.() -> Select<???> was expected
如何让 Kotlin 调用正确的方法?
【问题讨论】:
-
Select本质上是一个Query,因此您要使用的方法无法正确解析。尝试更改方法签名,使其不依赖于传递的参数来解析方法。 -
@KeivanEsbati 您能否详细说明“因此无法正确解决您要使用的方法”?我可以看到情况就是这样,我更愿意了解为什么会这样。即使
Select是Query,反过来也不成立。那么为什么非Select查询不绑定到非Select方法呢? -
我添加了一个参考并将其作为答案发布,以便您可以阅读更多相关信息。
-
jOOQ 区分
fetch(产生结果)和execute(产生更新计数)。为什么要对两者使用相同的术语呢?在某些时候,重载是比方便更大的痛苦来源......请注意,您可能希望引用ResultQuery<T>而不是Select<T>以获得更通用的解决方案。 -
如果您将代码提取到它自己的函数中,例如
fun q() : DSLContext.() -> Query = { ... }或fun s() : DSLContext.() -> Select并将这些函数传递给您的execute它将(可能)工作(尚未测试)...甚至可能以下工作:fun <T> dsl(dsl : DSLContext.() -> T) = dsl并调用manipulate.execute(dsl { ... })...工作? ;-) 也许你迟早会来到这里:How does type erasure work in Kotlin? 或这里:Kotlin type erasure - ... generics
标签: java kotlin overloading type-inference overload-resolution