考虑以下存储库接口声明:
interface KotlinUserRepository : Repository<User, String> {
fun findById(username: String): User
fun search(username: String) = findById(username)
}
我声明默认接口方法search(…)
默认调用findById(…)
。
启动我的应用程序失败:
org.springframework.data.mapping.PropertyReferenceException: No property Search found for type User!
如何在Spring Data存储库接口中使用Kotlin默认方法并阻止PropertyReferenceException
?
答案 0 :(得分:6)
Kotlin 1.1 / 1.2首先编译抽象接口方法的默认方法。在Spring Data存储库接口中使用Kotlin的默认方法是不可能的。
Kotlin允许使用Java运行时版本1.6的默认接口方法。 Java 1.8引入了JVM级缺省接口方法。这导致Kotlin使用不同的方法来编译默认接口方法而不是Java。
KotlinUserRepository
的代码汇编为:
interface KotlinUserRepository extends Repository {
User findById(String username);
User search(String username);
@Metadata(…)
public static final class DefaultImpls {
public static User search(KotlinUserRepository $this, String username) {
Intrinsics.checkParameterIsNotNull(username, "username");
return $this.findById(username);
}
}
}
方法search(…)
编译为抽象接口方法。实现位编译为类DefaultImpls
,它反映了默认方法签名。要实现KotlinUserRepository
的类需要实现search(…)
。在纯Kotlin环境中使用该接口将让Kotlin编译器创建实现位。
Spring Data存储库可以使用下面的代理。存储库中的每个方法都必须是:
在这种情况下,根据您实现Java接口的方式,任何自定义代码都不会实现search(…)
。 Spring Data尝试派生查询并将search(…)
视为User
域类的属性。查找失败并抛出PropertyReferenceException
。
这是一个已知的限制。
答案 1 :(得分:2)
正如本指出,你现在可以(Kotlin 1.2.40+)使用@JvmDefault
。
interface BatchRepository : PagingAndSortingRepository<Batch, Long> {
fun getAllByOrderByPriorityAscNameAsc(): List<Batch>
@JvmDefault
fun getForAdmin() = getAllByOrderByPriorityAscNameAsc()
}
您需要使用以下内容在build.gradle中启用该选项:
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
freeCompilerArgs = ['-Xenable-jvm-default']
}
}
我刚刚在Kotlin 1.2.41上进行了测试,它确实有效。
答案 2 :(得分:2)
FWIW Kotlin extension methods 在这里非常适合我,1 个 .kt 文件:
interface FooRepository : JpaRepository<FooDb, UUID>
object FooRepositoryExtensions {
fun FooRepository.doFoo(something: String): FooDb {
// do whatever you want here, the 'FooRepository' instance is available via `this`
}
答案 3 :(得分:1)
最近发布的Kotlin 1.2.40现在支持一个实验性功能,可以通过@JvmDefault
注释和功能标志设置将Kotlin默认方法编译为Java 8默认方法:Xenable-jvm-default
https://blog.jetbrains.com/kotlin/2018/04/kotlin-1-2-40-is-out/#more-5922
我还没有尝试过,但理论上你的例子应该这样工作:
interface KotlinUserRepository : Repository<User, String> {
fun findById(username: String): User
@JvmDefault
fun search(username: String) = findById(username)
}