Изменить объект @OneToMany в Spring Data Rest без его репозитория

В моем проекте я использую объект типа A, который имеет отношение OneToMany (orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER) к объектам типа B. Мне нужно SpringDataRest (SDR) для хранения полного объекта A с его объектами B (дочерними), используя один запрос POST. Я попробовал несколько комбинаций в SDR, единственная, которая сработала для меня, это создать @RepositoryRestResource для объекта A и создать @RepositoryRestResource также для объекта B, но отметьте это (B) как exported=false (если бы я вообще не создавал репозиторий из объекта B, это не сработало бы -> просто A объект будет сохранен в одном запросе POST, но не его дочерние элементы (@отношения OneToMany) типа B; тот же результат произойдет, если exported=false опущен для репозитория B) . Это нормально и единственный способ добиться этого (один запрос POST с сохранением всех объектов сразу)?

Причина, по которой я спрашиваю, в моем предыдущем примере заключается в том, что я должен (я хотел бы) контролировать «жизненный цикл» всех объектов, используя репозиторий A. Меня это устраивает, потому что отношение A->B является композицией (B не существует вне A ). Но у меня есть серьезная проблема с редактированием (также удалением) одного определенного объекта типа B с помощью SDR с использованием его родительского репозитория (поскольку у объекта B нет экспортированного собственного репозитория). Может быть, это невозможно по определению. Я пробовал эти решения:

  • PATCH для "/A/1/B/2" не работает -> метод не разрешен (в заголовках стоит "Allow: GET, DELETE") -> так что о PUT не может быть и речи
  • Json Patch также не будет работать - PATCH для "/A/1" с использованием json patch content-type [{"op": "add", "path": "/B/2", ....}] -> «нет такого индекса в целевом массиве» - потому что Json Patch использует скаляр «2» после «массива» в качестве индекса для своего массива. Это непрактично в мире Java, когда отношения хранятся в наборе объектов - индексация вообще не имеет смысла.
  • Я мог бы экспортировать репозиторий (exported=true) объекта B для манипулирования им «напрямую», но таким образом я потерял бы возможность хранить весь объект A с его B объекты в одном запросе POST, как я упоминал ранее.

Я хотел бы избежать отправки всего объекта A с одной крошечной модификацией его объекта B для PUT, если это возможно. Спасибо.


person rotmajster    schedule 17.01.2016    source источник


Ответы (1)


Мне удалось изменить дочернюю сущность следующим образом. В качестве образца я использовал следующие сущности:

@Entity
@Data
@NoArgsConstructor
public class One {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(cascade = ALL)
    private List<Many> manies = new ArrayList<>();

}

@Entity
@Data
@NoArgsConstructor
public class Many {

    public Many(String name) {
        this.name = name;
    }

    @Id
    @GeneratedValue
    private Long id;

    private String name;
}

У меня просто есть репозиторий для One.

(В моих примерах используется превосходный https://github.com/jkbrzt/httpie)

Удаление элемента с помощью патча json

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

echo '[{"op":"remove", "path":"/manies/1"}]' | http PATCH :8080/ones/1 Content-Type:application/json-patch+json -v

PATCH /ones/1 HTTP/1.1
Content-Type: application/json-patch+json

[
    {
        "op": "remove", 
        "path": "/manies/1"
    }
]

Замена всего списка с помощью патча json

В этом примере список заменяется массивом, указанным в значении.

echo '[{"op":"add", "path":"/manies", "value":[{"name":"3"}]}]' | http PATCH :8080/ones/1 Content-Type:application/json-patch+json -v

PATCH /ones/1 HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json

[
    {
        "op": "add", 
        "path": "/manies", 
        "value": [
            {
                "name": "3"
            }
        ]
    }
]

Добавление элемента в список с помощью патча json

В этом примере элемент добавляется в конец списка. Также здесь клиенту просто нужно знать длину списка перед обновлением. Так что порядок здесь не имеет особого значения.

echo '[{"op":"add", "path":"/manies/-", "value":{"name":"4"}}]' | http PATCH :8080/ones/1 Content-Type:application/json-patch+json -v

PATCH /ones/1 HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json

[
    {
        "op": "add", 
        "path": "/manies/-", 
        "value": {
            "name": "4"
        }
    }
]

Надеюсь это поможет.

person Mathias Dpunkt    schedule 18.01.2016
comment
Большое спасибо, Матиас, за подробный пост. Моя проблема, как я уже упоминал, заключается в том, что я использую наборы, а не списки. (@OrderColumn не работает с набором). Позвольте задать вам личный вопрос: не кажется ли вам, что использование индекса вместо id реального объекта с легким запросом на отдых - довольно странное решение? Я знаю, это связано с основной целью JsonPatch (изменить json)... но в любом случае мне любопытно, можно ли это сделать по-другому. - person rotmajster; 20.01.2016
comment
Я пробую ваше решение для добавления элемента в список и получаю несколько ошибок: действие "op": "add", "path": "/manies/-", "value":{"name":"4"} со списком, в котором нет записей, дает мне o.s.e.s.SpelEvaluationException: EL1004E: Method call: Method size() cannot be found on One type, поэтому я попытался использовать /manies, что дало мне org.springframework.data.rest.webmvc.json.patch.PatchException: Could not read {"name":"4"} into class org.hibernate.collection.internal.PersistentBag! Есть идеи? - person joshwa; 31.03.2017