如何在Spring Data存储库接口中使用Kotlin默认方法?

mp9*_*1de 4 java spring-data kotlin

请考虑以下存储库接口声明:

interface KotlinUserRepository : Repository<User, String> {

  fun findById(username: String): User

  fun search(username: String) = findById(username)
}
Run Code Online (Sandbox Code Playgroud)

我正在声明一个默认search(…)调用的默认接口方法findById(…).

启动我的应用程序失败:

org.springframework.data.mapping.PropertyReferenceException: No property Search found for type User!

如何在Spring Data存储库接口中使用Kotlin默认方法并阻止PropertyReferenceException

小智 8

正如 Ben 指出的那样,您现在可以(Kotlin 1.2.40+)使用@JvmDefault.

interface BatchRepository : PagingAndSortingRepository<Batch, Long> {
    fun getAllByOrderByPriorityAscNameAsc(): List<Batch>

    @JvmDefault
    fun getForAdmin() = getAllByOrderByPriorityAscNameAsc()
}
Run Code Online (Sandbox Code Playgroud)

您需要使用以下内容在 build.gradle 中启用该选项:

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
    kotlinOptions {
        freeCompilerArgs = ['-Xenable-jvm-default']
    }
}
Run Code Online (Sandbox Code Playgroud)

我刚刚在 Kotlin 1.2.41 上对其进行了测试,并且可以正常工作。

  • 对我来说是 ['-Xjvm-default=enable']; 请参阅 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-default/index.html 感谢您指路:) (2认同)

mp9*_*1de 7

TL; DR

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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

该方法search(…)编译为抽象接口方法.实现位编译为DefaultImpls反映默认方法签名的类.KotlinUserRepository需要实现的类需要实现search(…).在纯Kotlin环境中使用接口将让Kotlin编译器创建实现位.

Spring Data存储库可以使用下面的代理.存储库中的每个方法都必须是:

  1. 由特定于商店的存储库实现.
  2. 由自定义实现实现.
  3. Java 8默认方法.
  4. 使用查询注释进行注释.
  5. 适合方法命名方案以允许查询派生.

在这种情况下,search(…)根据您实现Java接口的方式,任何自定义代码都不会实现.Spring Data尝试派生查询并将其视为域类的search(…)属性User.查找失败并抛出PropertyReferenceException.

这是一个已知的限制.

参考


Jas*_*son 5

FWIW Kotlin 扩展方法在这里对我来说效果很好,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`
}
Run Code Online (Sandbox Code Playgroud)