2016-12-25 4 views
3

У меня есть три сущности: Родитель, его дочерняя и некоторые ссылки:Spring Data Rest - не может обновить (PATCH) список дочерних объектов, которые имеют ссылки на другое лицо

Родитель

@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<>(); 
} 

Детский

@Entity 
@Table(name = "children") 
public class Child extends LongId { 

    @NonNull 
    @Column(nullable = false) 
    private String name; 

    @NonNull 
    @ManyToOne(optional = false) 
    private Reference reference; 
} 

Ссылка

@Entity 
@Table(name = "references") 
public class Reference extends LongId { 

    @NotEmpty 
    @Column(nullable = false) 
    @Length(min = 3) 
    @NonNull 
    private String description; 
} 

И их репозитории:

@RepositoryRestResource 
public interface ParentRepo extends JpaRepository<Parent, Long> { 
} 

@RepositoryRestResource 
public interface ChildRepo extends JpaRepository<Child, Long> { 
} 

@RepositoryRestResource 
public interface ReferenceRepo extends JpaRepository<Reference, Long> { 
} 

Заблаговременно я сохранялся несколько детей со ссылками. Затем я создал нового родителя с одним ребенком:

POST http://localhost:8080/api/parents 
{ 
    "name" : "parent2", 
    "children" : [ 
     "http://localhost:8080/api/children/3" 
     ] 
} 

И успешно получил статус 201 Создано. Но когда я пытаюсь добавить еще одного ребенка в parent2 (обновить его с патчем):

PATCH http://localhost:8080/api/parents/2 
{ 
    "name" : "parent2", 
    "children" : [ 
     "http://localhost:8080/api/children/3", 
     "http://localhost:8080/api/children/4" 
     ] 
} 

Я получил сообщение об ошибке:

{ 
    "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]" 
} 

Если удалить ссылку Ссылку объект от ребенка :

@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; 
} 

все работает отлично - child4 успешно добавляет parent2.

Не могли бы вы указать мне, как правильно обновлять список дочерних объектов, если они имеют ссылку на другие объекты?

Репо с этим примером здесь: https://github.com/Cepr0/restdemo

+0

попробовать после добавления другого конструктора в классе 'Parent' с этой подписью' общественного Parent (именем String, List детей) ' –

+0

Спасибо @abhishek! Но это не помогло ( – Cepr0

+0

@AbhishekBhatia, у меня уже есть конструктор 'public Parent (String name, Child ... children)' и добавление вашего не помогло. (У меня есть конструкторы в каждом классе - я использую Lombock) – Cepr0

ответ

1

Я не могу в это поверить !!! Я добавил пустой конструктор с аргументом String 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; 

    public Child(String reference) { 
    } 
} 

Может кто-нибудь объяснить, почему это сработало ?!

+0

+1 такая же ситуация, сообщение об ошибке также отображает ** no String аргумент constructor/factory method **. Он упоминает меня о добавлении конструктора аргументов строки, и я решил эту проблему. И я также не знаю, почему это работает Вы нашли объяснение с ним? – g1eny0ung

+0

@ g1eny0ung - еще нет)) – Cepr0

+1

Похоже, это известная проблема, и, похоже, она получает некоторое внимание. Вот [Весенний реестр данных Jira Ticket] (https://jira.spring.io/browse/DATAREST-1041?jql=project%20%3D%20DATAREST%20AND%20status%20%3D%20Open%20ORDER%20BY% 20priority% 20DESC) относительно проблемы. –

0

Ни одно из этих решений не будет работать. Конечно, вы можете создать поддельный конструктор фиктивных элементов, который принимает строку, чтобы ошибка исчезла, но это бесполезно, кажется, ничего не делает! (например, он будет передан в строке uri, такой как localhost:8080/address/1, что бесполезно, поскольку никакое разрешение не происходит, чтобы сопоставить его с объектом домена.

Из моих исследований нет способа заставить это работать. пользовательский контроллер, вы больше не можете автоматически конвертировать URI из объекта @RequestBody в объект домена, управляемый репозиторией. В основном, для хранения данных для хранения URI, доступ к которым можно получить в ручном контроллере, вы определяете.

Возможно, методы, которые вы можете реализовать для создания URI-резольвера, но удачи в этом, так как это более чем вероятно недокументировано.

Как обходной путь или «взломать». Попробуйте использовать конечные точки репозитория для хранения данных весны, возможно, даже крючки событий репозитория, такие как @HandleBeforeSave, или если это не вариант, я это рассмотрел.

Это не будет работать, как обыкновению Урьте получить карту к объектам

{ 
    "name": "joe", 
    "addresses": ["localhost:8080/address/1", 
        "localhost:8080/address/8", 
        "localhost:8080/address/23"] 
    } 

Вместо создать DTO и передавать в первичном ключе, который в пользовательском контроллера вы делаете свое собственное разрешение (например, получить адрес по первичному ключу в AddressRepository). Это довольно ручной подход и, очевидно, не идеальный.

{ 
    "name": "joe", 
    "addresses": [1, 8, 23] 
}