I have three entities: Parent, its Child and some Reference:
Parent
@Entity
@Table(name = "parents")
public class Parent extends LongId {
@NonNull
@Column(nullable = false)
private String name = "Undefine";
@NonNull
@OneToMany(cascade = MERGE)
private List<Child> children = new ArrayList<>();
}
Child
@Entity
@Table(name = "children")
public class Child extends LongId {
@NonNull
@Column(nullable = false)
private String name;
@NonNull
@ManyToOne(optional = false)
private Reference reference;
}
Reference
@Entity
@Table(name = "references")
public class Reference extends LongId {
@NotEmpty
@Column(nullable = false)
@Length(min = 3)
@NonNull
private String description;
}
And their repositories:
@RepositoryRestResource
public interface ParentRepo extends JpaRepository<Parent, Long> {
}
@RepositoryRestResource
public interface ChildRepo extends JpaRepository<Child, Long> {
}
@RepositoryRestResource
public interface ReferenceRepo extends JpaRepository<Reference, Long> {
}
Beforehand I persisted several Children with References. Then I created a new Parent with one child:
POST http://localhost:8080/api/parents
{
"name" : "parent2",
"children" : [
"http://localhost:8080/api/children/3"
]
}
And have successfully got status 201 Created. But when I try to add another child to parent2 (update it with PATCH):
PATCH http://localhost:8080/api/parents/2
{
"name" : "parent2",
"children" : [
"http://localhost:8080/api/children/3",
"http://localhost:8080/api/children/4"
]
}
I've got an error:
{
"cause": {
"cause": null,
"message": "Can not construct instance of restsdemo.domain.entity.Child: no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/children/4')\n at [Source: N/A; line: -1, column: -1]"
},
"message": "Could not read payload!; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of restsdemo.domain.entity.Child: no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/children/4')\n at [Source: N/A; line: -1, column: -1]"
}
If I remove link to Reference entity from Child:
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "children")
public class Child extends LongId {
@NonNull
@Column(nullable = false)
private String name;
// @NonNull
// @ManyToOne(optional = false)
// private Reference reference;
}
all works perfectly - child4 was successfully adds to parent2.
Could you point me how to correctly update a list of child entities if they have reference to another entities?
Repo with this example is here: https://github.com/Cepr0/restdemo
I can't believe it!!! I added an empty constructor with String argument to Child and everything worked!
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "children")
public class Child extends LongId {
@NonNull
@Column(nullable = false)
private String name;
@NonNull
@ManyToOne(optional = false)
private Reference reference;
public Child(String reference) {
}
}
Can anyone explain why it worked?!
None of these solutions will work. Sure you can create a fake dummy constructor that accepts a string so the error will go away, but that is useless seems it does nothing! (eg it will get passed in a string uri such as localhost:8080/address/1
which is useless as no resolution happens to map it to a domain entity.
From my research there is no way to get this to work. As soon as you implement a custom controller you can no longer automatically convert a URI from the @RequestBody
object to a repository managed domain entity. Basically spring data rest adds secret sauce to resolve URI's which cant be accessed in a manual controller you define.
There are probably methods you can implement to create a URI resolver but good luck with that as its more than likely undocumented.
As a workaround or 'hack'.. Try just use spring data rest repository end points, maybe even repository event hooks like @HandleBeforeSave
or if that is not an option I have considered this.
This wont work as URI's wont get mapped to entities
{
"name": "joe",
"addresses": ["localhost:8080/address/1",
"localhost:8080/address/8",
"localhost:8080/address/23"]
}
Instead create a DTO and pass in primary key which in the custom controller you do your own resolution (eg get address by Primary Key in the AddressRepository). This is quite a manual approach and obviously not ideal.
{
"name": "joe",
"addresses": [1, 8, 23]
}
来源:https://stackoverflow.com/questions/41324078/spring-data-rest-can-not-update-patch-a-list-of-child-entities-that-have-a-r