spring 数据休息 kotlin 协会 POST

spring data rest kotlin associations POST

我遵循了教程 http://www.baeldung.com/spring-data-rest-relationships。 我还观察到我可以通过向关系提供 link 来直接创建关联。

curl -i -X POST -H "Content-Type:application/json" -d '{"name":"My Library"}' http://localhost:8080/libraries
curl -i -X POST -d '{"title":"Books", "library":"http://localhost:8080/libraries/1"}' -H "Content-Type:application/json" http://localhost:8080/books

这在 Java 中工作正常,在使用常规 class 时在 Kotlin 中也能正常工作。

但是,如果我在 Kotlin 中使用数据 class,我会收到以下错误

2018-04-26 14:13:43.730 ERROR 79256 --- [nio-8080-exec-2] b.e.h.RestResponseEntityExceptionHandler : org.springframework.http.converter.HttpMessageNotReadableException: JSON 解析错误: 无法构造 com.baeldung.models.Library 的实例(尽管至少存在一个创建者):没有字符串参数 constructor/factory 方法从 [Source: (org.apache.catalina.connector.CoyoteInputStream);行:1,列:29](通过引用链:com.baeldung.models.Book["library"])

我的项目中确实有相关的 kotlin-spring、kotlin-jpa 和 kotlin-noarg 插件。

代码在这里https://github.com/vijaysl/spring-data-rest

Kotlin 数据 classes 非常严格。它告诉您,基本上,它无法构建您的 POKO,并且列出了它尝试的一些方法。其中之一是使用 String 构造函数。其他是通过私有字段操作(这是通常完成的方式)。

Kotlin 中的数据 classes,如果它们的字段声明为 private val name:String 转换为(在 java 中)private final String name; 它不能分配给 final 字段(尝试分配给私有字段是脏的,但当它是最终的时是不可能的;JVM 不允许它)并且没有 getName()setName() 函数可以是用作另一种补水方法。

部分选项:

  1. 声明你的变量是 var 而不是 valprivate var name:String java 等同于 private String name 将使用基于现场的(肮脏的)水合作用。
  2. 为 kotlin 添加一个特定的 kotlin 依赖项来解决这个问题:compile("com.fasterxml.jackson.module:jackson-module-kotlin") 看看 this project

应该适合您的 kotlin 示例 class:

import org.springframework.hateoas.Identifiable
import java.time.LocalDate
import javax.persistence.*
import javax.validation.constraints.*

@Entity
data class Employee(@Pattern(regexp = "[A-Za-z0-9]+")
                    @Size(min = 6, max = 32)
                    val name: String,
                    @Email
                    @NotNull
                    val email: String?,
                    @PastOrPresent
                    val hireDate: LocalDate = LocalDate.now(),

                    @OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL])
                    val forms:List<Form> = listOf(),
                    @OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL])
                    val reports:List<Report> = listOf(),
                    @Id @GeneratedValue( strategy =  GenerationType.IDENTITY) private val id: Long? = null): Identifiable<Long> {

    override fun getId() = id

    constructor(name:String): this(name,"$name@foo.com")
}

有了kotlin就OK了。

只需将"data class"替换为"class"即可。

Jackson 在 "data class" 中没有找到空构造函数。并使用其他反序列化器...而不是 Uri....

尝试在主构造函数上添加 @JsonCreator(mode = JsonCreator.Mode.DISABLED) 注释。无需禁用 com.fasterxml.jackson.module:jackson-module-kotlin.

解释:

  • Kotlin Jackson 模块暗示您的默认构造函数是 JSON 创建者(参见 KotlinValueInstantiator class)。
  • 因此,Spring Data REST 不应用其 bean 反序列化器修饰符(应该通过 URI 加载 bean),因为 bean 属性映射不用于创建者属性(构造函数参数)。
  • KotlinValueInstantiator 尝试使用标准反序列化器和实例化器反序列化构造函数参数,这会导致您提到的错误。

可能的解决方案:

由于 koltin-jpa 模块为 JPA 添加了一个默认的空构造函数,您可以通过显式禁用它来指示 Jackson 不要使用 JSON 创建者,而是使用默认的空构造函数。

示例:

@Entity
class Book @JsonCreator(mode = JsonCreator.Mode.DISABLED) constructor(

  @ManyToMany
  val libraries: ModifiableList<Library> = ArrayList(),

): AbstractPersistable<Long>(), Identifiable<Long>