Не удается ОПУБЛИКОВАТЬ коллекцию

#spring-data-rest

#spring-data-rest

Вопрос:

У меня есть простая сущность с отображением одной коллекции.

 @Entity
public class Appointment Identifiable<Integer>   {  

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @JsonIgnore
    private Integer id;

    @Column(name="TRAK_NBR")
    private String trackNumber;

    @OneToMany(fetch =FetchType.EAGER, cascade= CascadeType.ALL)
    @JoinColumn(name="CNSM_APT_VER_WRK_I", nullable = false)
    private Set<Product> products = new HashSet<Product>();
}

@Entity
public class Product implements Identifiable<Integer> {

    @Id
    @Column(name = "CNSM_PRD_VER_WRK_I")
    @GeneratedValue(strategy = GenerationType.AUTO)
    @JsonIgnore
    private Integer id;

    @Column(name = "PRD_MDL_NBR")
    private String model;

    @Column(name = "PRD_SPEC_DSC")
    private String description;
}
  

В моем приложении, когда я включаю только PagingAndSortingRepository для назначения. Я могу вызвать команду POST со следующей полезной нагрузкой.

 {
  "trackNumber" : "XYZ123",
  "products": [
    {"model" : "MODEL",
     "description" : "NAME"
    }]
}
  

Когда я добавляю PagingAndSortingRepository для Product и пробую тот же POST, я получаю следующее сообщение об ошибке.

 {
  "cause" : {
    "cause" : {
      "cause" : null,
      "message" : null
    },
    "message" : "(was java.lang.NullPointerException) (through reference chain: com..model.Appointment["products"])"
  },
  "message" : "Could not read JSON: (was java.lang.NullPointerException) (through reference chain: com.model.Appointment["products"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.model.AppointmentVerification["products"])"
}

My GET payload with both Repositories returns this.  This is my desired format.  The link to products should be included

{
  "trackNumber" : "XYZ123", 
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/consumerappointment/appointments/70"
    },
    "products" : {
      "href" : "http://localhost:8080/consumerappointment/appointments/70/products"
  }
}
  

Используя только репозиторий назначений, я получаю следующую полезную нагрузку и могу опубликовать список продуктов.

 {
  "trackNumber" : "XYZ123",
  "products" : [ {
    "model" : "MODEL",
    "description" : "NAME",
  } ],
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/consumerappointment/appointments/1"
    }
  }
}
  

Ответ №1:

Давайте сделаем шаг назад и убедимся, что вы понимаете, что здесь происходит: если репозиторий обнаружен, Spring Data REST предоставляет ему выделенный набор ресурсов для управления агрегатами, обрабатываемыми репозиторием через HTTP. Таким образом, если у вас есть репозитории для нескольких объектов, связанных друг с другом, связь представляется в виде ссылки. Вот почему вы видите продукты, встроенные только AppointmentRepository на месте, и products ссылку на месте, как только вы создаете ProductRepository .

Если вы хотите предоставить оба хранилища в качестве ресурсов, вам необходимо передать URI Product экземпляров в полезной нагрузке для POST создания Appointment . Это означает, что вместо публикации этого:

 { "trackNumber" : "XYZ123",
  "products": [
    { "model" : "MODEL",
      "description" : "NAME"
    }
  ]
}
  

вы бы создали Product первый:

 POST /products
{ "model" : "MODEL",
  "description" : "NAME" }

201 Created
Location: …/products/4711
  

А затем передать идентификатор продукта в Appointment полезную нагрузку:

 { "trackNumber" : "XYZ123",
  "products": [ "…/products/4711" ]}
  

В случае, если вы не хотите ничего из этого ( Product в первую очередь, никаких ресурсов, доступных для, используйте @RepositoryRestResource(exported = false) on PersonRepository . Это все равно оставило бы вас с экземпляром компонента, созданным для репозитория, но ресурсы не экспортировались, а ресурс, предоставленный для Appointment s, возвращался к встраиванию связанных Product s.

Комментарии:

1. Я вижу, это не очевидно из кода там. Я, конечно, не разбираюсь в деталях вашего дизайна базы данных, но внешний ключ не означает, что он должен быть установлен в первую очередь. Это может быть просто ссылка, которая связывает продукт с назначением позже (следовательно, ваш Set ). Тем не менее, я попытался объяснить, почему это работает так, как работает. Вполне возможно, что модель не соответствует вашему варианту использования, но вам не нужно разделять совокупные корни, которые по определению имеют двунаправленную связь друг с другом.

2. Моя таблица продуктов имеет ограничение внешнего ключа для таблицы назначений. Таким образом, создание продуктов сначала не работает. Могу ли я изменить поведение POST, когда у меня есть оба репозитория? Мне нужно иметь возможность создавать назначение и продукты в одной транзакции, но при этом иметь ссылку на продукты в GET.

3. Да, просто предоставление реализованного вручную контроллера для соответствующего пути и метода HTTP должно помочь. Тем не менее, я думаю, что это маскирует фундаментальную проблему дизайна.

4. Я создал небольшое приложение с контроллером, реализованным вручную, о котором мы говорили. Я все еще не могу опубликовать полную полезную нагрузку, как я думал, что смогу. Похоже, у меня такая же проблема, как и эта jira.spring.io/browse/DATAREST-377 . Мой пример кода можно найти по адресу github.com/zachariahyoung/posterror .