Spring Data Rest - can not update (PATCH) a list of child entities that have a reference to another entity

走远了吗. 提交于 2019-12-01 18:57:21

问题


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


回答1:


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?!




回答2:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!