如何在REST端点中POST具有关系的实体-Kotlin Spring Data



我遵循本教程使用Spring Boot在Kotlin中创建了一个基本的web应用程序。但是,我无法POST与现有资源具有多对一关系的新实体。

我的代码:

@Entity
class Song(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
var title: String,
@ManyToOne(fetch = FetchType.EAGER)
var addedBy: User)
@Entity
class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
var email: String,
var displayName: String)
@RestController
@RequestMapping("/api/songs")
class SongController(private val repository: SongRepository) {
@PostMapping("/")
fun add(@RequestBody song: Song) =
repository.save(song)

这个答案和其他人指出,您可以使用另一个资源的URI引用它,但发送以下请求:

{
"title": "Some title",
"addedBy": "http://localhost:8080/api/users/1"
}

获取堆栈跟踪org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of 'com.example.springboot.User' (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/users/1'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of 'com.example.springboot.User' (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/users/1')n at [Source: (PushbackInputStream); line: 6, column: 13] (through reference chain: com.example.springboot.Song["addedBy"])的错误

我发现,在Jackson/Hibernate/Spring Data之间的某个地方,它无法将用户资源URI转换为用户实体,但我不知道这种魔力应该发生在哪里。

这似乎是Kotlin特有的问题。这里关于SO的所有建议都没有解决这个特定的错误,教程本身也没有处理好关系。如果用这种方式处理关系根本不是正确的方法,我很想知道首选的做法是什么

本教程使用HATEOAS。使用查看他们引用相应子实体的请求主体

"books" : {       "href" : "http://localhost:8080/authors/1/books"     }

这意味着您也应该将此模式应用于您的请求。否则,这将不起作用。HAEOAS允许您通过相应的资源路径直接引用相关的子实体,但您需要保留发布的请求体所缺少的必要结构。此外,您必须在WebService/WebApi/Spring Boot项目中支持HAEOAS。

你能做什么:

{
"title": "Some title",
"addedByUserId": "1"
}

然后

@PostMapping("/")
fun add(@RequestBody song: Song) =
val userEntity = userRepository.findById(song.getAddedByUserId())
Song newSong = new SongEntity();
// map props
newSong.setUser(userEntity)
repository.save(song)

这个代码不起作用,但我希望你能明白。

进一步

在您的代码中,您将请求主体视为一个实体。请考虑将传入类和实体类分开。这将使一些事情变得更容易。

我认为您缺少jackson的kotlin模块,它正是为之创建的。

https://github.com/FasterXML/jackson-module-kotlin

只要在您的项目中添加这个依赖项,spring就会使用这个新模块自动配置您的对象映射器。如果您有一个带有自己创建的objectMapper的Bean,那么您需要手动配置它,在模块的README.md中有一节关于这一点