【问题标题】:kotlin hibernate error "No property null found for type"kotlin hibernate 错误“找不到类型的属性 null”
【发布时间】:2021-09-22 03:37:54
【问题描述】:

我正在松散地遵循 springboot 教程来构建我的第一个 springboot API。

我在尝试运行存储库测试时遇到错误:

@Test
fun `savePattern - expect saved`() {
    val name = "test pattern"
    val chunkSize = 8
    val rows = 8
    val columns = 8
    val frame = Frame(listOf(1, 0, 1))
    val pattern = Pattern(name, rows, columns, chunkSize, setOf(frame))
    entityManager.persist(pattern)
    entityManager.flush()

    val found = patternRepository.findByIdOrNull(pattern.id)

    assertThat(pattern).isEqualTo(found)

}

使用仓库接口:

interface PatternRepository : CrudRepository<Pattern, Long> {
    fun findByIdOrNull(id: Long?): Pattern?
}

还有我的实体模式:

@Entity
class Pattern(
    var name: String,
    var rows: Int,
    var columns: Int,
    var chunkSize: Int,
    @OneToMany(targetEntity = Pattern::class) var frames: Set<Frame>,
    @Id @GeneratedValue var id: Long? = null
)

及相关实体Frame:

@Entity
class Frame(
    @ElementCollection
//    @OrderColumn
    var data: List<Int>,
    @Id @GeneratedValue var id: Long? = null
)

当我尝试触发测试时,我收到错误:

无法为公共抽象 com.lightinspiration.matrixanimator.domain.Pattern com.lightinspiration.matrixanimator.repository.PatternRepository.findByIdOrNull(java.lang.Long) 创建查询!原因:无法为方法 public abstract com.lightinspiration.matrixanimator.domain.Pattern com.lightinspiration.matrixanimator.repository.PatternRepository.findByIdOrNull(java.lang.Long) 创建查询!没有找到类型 Pattern 的属性 null!;嵌套异常是 java.lang.IllegalArgumentException:无法为方法 public abstract com.lightinspiration.matrixanimator.domain.Pattern com.lightinspiration.matrixanimator.repository.PatternRepository.findByIdOrNull(java.lang.Long)创建查询!没有找到类型 Pattern 的属性 null!

我知道这个错误消息对于有 kotlin 和/或 java 经验的人来说可能非常清楚,但它让我陷入了一个循环。

这是说没有找到类型 Pattern! 的属性 null,但我不确定它所指的 null 属性是什么。另外,如果我定义了一个可以返回 null 的方法接口,为什么 spring 会寻找一个非 null 模式(即Pattern!,或者我误解了错误中的含义?)。我是否在我的代码中定义了错误(我假设我做了:|)

更新

对不起,如果我给人的印象是我没有先浏览文档。我意识到我在原始帖子中没有提供足够的信息。

为了提供更多上下文,我在 persistence with JPA 中关注的 Spring 指南显示了以下测试示例:

@DataJpaTest
class RepositoriesTests @Autowired constructor(
    val entityManager: TestEntityManager,
    val userRepository: UserRepository,
    val articleRepository: ArticleRepository) {

  @Test
  fun `When findByIdOrNull then return Article`() {
    val juergen = User("springjuergen", "Juergen", "Hoeller")
    entityManager.persist(juergen)
    val article = Article("Spring Framework 5.0 goes GA", "Dear Spring community ...", "Lorem ipsum", juergen)
    entityManager.persist(article)
    entityManager.flush()

    // looking specifically at this line
    val found = articleRepository.findByIdOrNull(article.id!!)

    assertThat(found).isEqualTo(article)
  }

  @Test
  fun `When findByLogin then return User`() {
    val juergen = User("springjuergen", "Juergen", "Hoeller")
    entityManager.persist(juergen)
    entityManager.flush()
    val user = userRepository.findByLogin(juergen.login)
    assertThat(user).isEqualTo(juergen)
  }
}

并指出:

我们在这里使用 Spring Data 默认提供的 CrudRepository.findByIdOrNull Kotlin 扩展,它是基于 Optional 的 CrudRepository.findById 的可空变体。阅读伟大的 Null 是您的朋友,而不是错误的博文了解更多详细信息。

当我查找底层扩展时,它看起来应该查找 id 或返回 null:

fun <T, ID> CrudRepository<T, ID>.findByIdOrNull(id: ID): T? = findById(id).orElse(null)

我的假设是,当我用CrudRepository 扩展我的PatternRepository 时,内置的findByIdOrNull 扩展将被添加到我的存储库中,并可用于查找ID。

这就是我对错误消息感到困惑的原因。我的存储库设置了正确的TID 类型,我使用匹配ID 类型的ID 调用findByIdOrNull,所以我认为这会触发底层扩展方法。很高兴看到有关 JPA 如何构建查询的更多详细信息,但我不认为查询构建是我正在努力解决的问题。

【问题讨论】:

  • 你的pattern.id是否为空...?

标签: spring-boot hibernate kotlin


【解决方案1】:

使用 CrudRepository 时,SQL 是从方法名称生成的。在你写fun findByIdOrNull(id: Long?): Pattern?的情况下,这转化为

SELECT * FROM pattern WHERE id = 'value' OR null = 'value'

由于您的表中没有名称为 null 的字段,因此您会收到错误消息。

例如,如果您想使用给定的 namechunkSize 搜索所有模式,那么您将定义一个方法如下(请参阅方法名称中 And 运算符的使用)

fun findByNameAndChunkSize(name: String, chunkSize: Int): List<Pattern>?

我建议你阅读Docs,有很多例子

【讨论】:

  • 此信息很有帮助,但它让我意识到我需要包含有关本教程的更多信息。更新我的问题。
【解决方案2】:

啊,所以今天醒来并检查所有我看到的问题。

就像我在上面的问题更新中所说的那样,findByIdOrNull 是 spring 提供的扩展功能。我搞砸的地方是将通过扩展继承的方法与事后可以添加的扩展函数混为一谈。

我的完整原始测试文件如下所示:

package com.lightinspiration.matrixanimator.repository

import com.lightinspiration.matrixanimator.domain.Pattern
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager


@DataJpaTest
class PatternRepositoryTest @Autowired constructor(
    val entityManager: TestEntityManager,
    val patternRepository: PatternRepository
) {

    @Test
    fun `savePattern - expect saved`() {
        val name = "test pattern"
        val chunkSize = 8
        val rows = 8
        val columns = 8
//        val frame = Frame(listOf(1, 0, 1))
        val pattern = Pattern(name, rows, columns, chunkSize)
        entityManager.persist(pattern)
        entityManager.flush()

        val found = patternRepository.findByIdOrNull(pattern.id!!)

        assertThat(pattern).isEqualTo(found)

    }

}

(抱歉,应该在问题中发布导入,为了便于阅读,我试图保持代码简洁,但不小心遗漏了一些关键细节:/)

findByIdOrNull 方法是CrudRepository 接口本身的一部分,是的,我会继承该方法,但findByIdOrNull扩展函数,这意味着它不是 CrudRepository 的一部分界面:

这是事后添加的功能。因此,本教程的测试暗示您在测试用例代码本身中导入了扩展函数:

在我弄清楚这一点后,我 went and looked for the tutorial source code 发现他们执行相同的导入:facepalm:。

回想起来,这很有意义,尽管我对扩展函数/方法还是新手,所以这不是我想到的第一件事。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-10
    • 2017-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    相关资源
    最近更新 更多