Kotlin data class No String-argument constructor w

2019-07-26 13:05发布

问题:

I'm using Spring data rest with Kotlin and if I use data classes the associations via uri stops working with the error no String-argument constructor/factory method to deserialize from String value

Data class

@Entity
data class Comment(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)     
    var id: Long = 0,
    var author: String? = null,
    var content: String? = null,

    @ManyToOne
    var post: Post? = null) {
}

If I use a simple class instead the association works fine.

@Entity
class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)     var id: Long = 0
    var author: String? = null
    var content: String? = null

    @ManyToOne
    var post: Post? = null
}

The association is done via a POST request {"author":"John Doe","content":"Dummy Content", "post":"http://localhost:8080/post/33"}

Any ideia why I have this error when I use a data class and what can I do to use the association creation via uri and keep using data classes?

回答1:

I did some investigation, and turns out Spring Data Rest uses a custom Jackson module to deserialize JSON into JPA entities: it uses PersistentEntityJackson2Module class and using the inner class UriStringDeserializer to resolve the concrete entities from entity URI references, http://localhost:8080/post/33 in your example.

Problem is, this custom deserialization only kicks in when the "standard deserialization" of Jackson is triggered: The one that uses empty constructor, then using setters to resolve & set the fields. At that moment, UriStringDeserializer converts the string into the concrete entity - Post instance of your example.

When you use a data class, the class neither has an empty constructor nor setters, therefore in BeanDeserializer#deserializeFromObject method of Jackson, it branches into if (_nonStandardCreation) being true, from there the call goes into BeanDeserializerBase#deserializeFromObjectUsingNonDefault , but not handed over to PersistentEntityJackson2Module anymore, and directly failing due to type mismatch between the constructor argument and the json value.

It seems you need to create a feature request for it to be implemented. If you decide to implement yourself, providing a _delegateDeserializer to the BeanDeserializer might be a start (not sure).

However, I don't know how JPA itself plays with data classes in the first place - after all it tracks the entity state changes, but a data class cannot have state changes. So, it might not be possible to use data classes after all - better to keep in mind.

Note: You probably cannot simply extend/override PersistentEntityJackson2Module because it is registered to multiple beans in RepositoryRestMvcConfiguration