JPA — Как обновить дочерний объект в отношениях OneToMany?

#spring #spring-boot #hibernate #rest #jpa

Вопрос:

У меня есть класс клиентов, в котором у каждого клиента может быть несколько продуктов. Класс выглядит следующим образом:

 @Entity
@Table(name = "customer")
public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "firstName")
private String firstName;

@Column(name = "lastName")
private String lastName;

@OneToMany(targetEntity=Product.class, mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
@JsonManagedReference
private List<Product> products = new ArrayList<>();

//getters and setters here

}
 

и класс продукта имеет одну единственную связь с другими классами, и это выглядит следующим образом:

 @Entity
@Table(name = "product")

public class Product {

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

@ManyToOne(targetEntity=Customer.class, fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id")
@JsonBackReference
private Customer customer;

@OneToOne(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
@JsonManagedReference
private SomeType1 someType1;

@OneToOne(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
@JsonManagedReference
private SomeType2 someType2;

@OneToOne(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
@JsonManagedReference
private SomeType3 someType3;

@OneToOne(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
@JsonManagedReference
private SomeType4 someType4;


//getters and setters here

    }
 

Я пытаюсь добиться следующей функциональности с помощью этого:

Учитывая идентификатор клиента и идентификатор продукта, обновите значения в SomeType1, SomeType2, SomeType3 классах. Я получаю обновленные SomeType1, SomeType2, SomeType3 объекты из пользовательского интерфейса и хочу обновить значения в БД. У меня уже есть PUT метод для этого.

Here’s the PUT method:

 @PutMapping(value = "customer/{id}/product/{product_id}")
@ResponseBody
public ResponseEntity<Product> updateProduct(@PathVariable final String id, 
        @PathVariable final String product_id, @RequestBody final Product product) {
    Optional<Customer> customerInDb = customerService.getCustomerById(id);
    if (!customerInDb.isPresent()) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND,
                MessageFormat.format("Customer with id {0} does not exist.", id));
    } else {
        product.setId(Long.valueOf(product_id));
        product.setCustomer(customerInDb.get());
        Product savedProduct = customerService.createProduct(product);
        return ResponseEntity.ok(savedProduct);
    }
}
 

Я получаю следующую ошибку для этого вызова REST:

 javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.myapp.arg.entities.SomeType2#12]
 

В чем может быть причина этого?

Способ создания продукта:

 @Override
public Product createProduct(Product product) {
    Product savedProduct = productRepository.save(product);
    return savedProduct;
}
 

Ввод JSON в PUT метод:

 {
   "id":9,
   "someType1":{
      "id":9,
      "avg":20,
      "total":20
   },
   "someType2":{
      "id":9,
      "circum":45.0,
      "strength":45.0,
      "totalNav":0.0
   },
   "someType3":{
      "id":9,
      "tensile":87,
      "pull":128,
      "push":56,
      "upward":28.0
   },
   "measuredBy":"SJ",
   "addedDate":"2021-05-23",
   "type":"Prime"
}
 

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

1. пожалуйста, покажите createProduct метод. И в будущем не абстрагируйтесь от проблемы с каким — то типом 1, каким-то типом 2-это затрудняет нам отслеживание/решение

2. @JAsgarov Я добавил createProduct метод в вопрос. Спасибо вам за предложение относительно абстрагирования проблемы.

3. 1. что вы выдаете за а @RequestBody final Product product ? поделитесь json plz, особенно SomeTypeN свойствами 2. совершенно плохая идея передавать сущность в качестве модели, вы должны использовать отдельные классы для транспортировки и сохранения целей 3. показать Customer отображение в Product 4. что product(Long.valueOf(product_id)) делать? это какой-то метод или опечатка?

4. @YuriyTsarkov Я обновил вопрос вашими предложениями. Спасибо вам за ваш отзыв (пункт 2 в вашем комментарии).

Ответ №1:

вы используете один и тот же идентификатор для всех ваших сущностей. Идентификатор должен быть уникальным

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

1. Но эти идентификаторы автоматически генерируются для дочерних сущностей. Стратегия создания идентификаторов для этих дочерних сущностей имеет значение GenerationType.AUTO . Нужно ли мне это менять? Поскольку дочерние сущности имеют разные таблицы, возможно, что идентификаторы могут быть одинаковыми.

2. вы можете сохранить автогенерацию, просто удалите эту строку product.setId(Long.valueOf(product_id)); , чтобы jpa могла автоматически позаботиться о создании вспомогательных средств