Мне нужно разрешить пользователям редактировать заказы, которые включают добавление новых позиций и подпунктов под этими элементами заказа. Я смоделировал три сущности соответственно: Order
, OrderItem
с и OrderSubItem
с. Каждый OrderItem
должен иметь один или несколько OrderSubItem
с (для краткости опущены дополнительные реквизиты сущности):
@Entity
@Table(name="[Order]")
@Data
public class Order {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
@JoinColumn(name="orderId", nullable=false)
@Valid
@NotNull(message="order.orderItems.notNullorEmpty")
@Size(min=1, message="order.orderItems.notNullorEmpty")
private List<OrderItem> orderItems;
}
@Entity
@Data
public class OrderItem {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
@JoinColumn(name="orderId", nullable=false)
@Valid
@NotNull(message="order.orderSubItems.notNullorEmpty")
@Size(min=1, message="order.orderSubItems.notNullorEmpty")
private List<OrderSubItem> ordersSubItems;
@ToString.Exclude
@JsonIgnore
@ManyToOne
@JoinColumn(name="orderId", insertable=false, updatable=false)
private Order order;
}
@Entity
@Data
public class OrderSubItem {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ToString.Exclude
@JsonIgnore
@ManyToOne
@JoinColumn(name="orderId", insertable=false, updatable=false)
private OrderItem orderItem;
}
Когда пользователь запрашивает обновление заказа, скажем добавить новый OrderItem
с новыми OrderSubItem
s, вызывается следующий контроллер:
@RestController
@RequestMapping("/orders")
@RequiredArgsConstructor
@Validated
@Slf4j
public class OrderController {
@NonNull
private final OrderService orderService;
@PutMapping
@ResponseStatus(HttpStatus.NO_CONTENT)
public void update(@Valid @RequestBody Order order) {
orderService.update(order);
}
}
Когда пользователь отправляет:
Order(id=1, orderItems=[
OrderItem(id=null, orderSubItems=[OrderSubItem(id=null, value=1)]),
OrderItem(id=1, orderSubItems=[OrderSubItem(id=1, value=2)])
проверка проходит здесь на контроллере. Тогда услуга называется:
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderService {
@Transactional
public void update(Order order) {
List<OrderItem> orderItems = order.getOrderItems();
for (OrderItem orderItem : orderItems) {
List<OrderSubItem> orderSubItems = orderItem.getOrderSubItems();
List<OrderSubItem> newOrderSubItems = new ArrayList<OrderSubItem>();
Collections.reverse(orderSubItems);
for (OrderSubItem orderSubItem : orderSubItems) {
if (orderSubItem.getValue() == null) {
//skip it
} else {
orderSubItem.setIndex(newOrderSubItems.size());
newOrderSubItems.add(orderSubItem);
}
}
orderItem.setOrderSubItems(newOrderSubItems);
//also tried:
// orderItem.getOrderSubItems().clear();
// orderItem.getOrderSubItems().addAll(newOrderSubItems);
}
log.debug(order);
orderRepo.save(order);
}
Непосредственно перед сохранением я вижу, что у каждого элемента заказа есть подпункт заказа. Но сохранение завершается неудачно с ConstraintViolationException
, сообщающим, что у вновь добавленной позиции заказа для подпозиций заказа есть ноль.
Почему проверка не проходит? Я думаю, что это может иметь отношение к двунаправленным отношениям и манипулированию списком.
Обновление 1
Я создал пользовательский @NotNull
валидатор, чтобы я мог проверить содержимое OrderItem
и вот, orderSubItems
пуст во время проверки в постоянное время.
Обновление 2
После дополнительных исследований я обнаружил, что любое свойство @OneToMany
, которое я добавляю к OrderItem
, будет иметь нулевое значение только для одного объекта, создаваемого Hibernate. Все верно, похоже, что Hibernate создает OrderItem
. Неважно, двусторонние отношения или нет. Также не имеет значения, манипулировать списком или нет. Другие свойства заселены в порядке. Теперь вот странная часть:
Если я напишу пользовательский валидатор на уровне Order
, чтобы проверить, что его OrderItem
s имеет ненулевое значение orderSubItems
, и удалю аннотацию NotNull
в orderSubItems
, он пройдет проверку и его сохраняет без проблем.
То есть OrderItem
, создаваемый Hibernate, похоже, не присоединен к объекту Order
. Является ли это промежуточным этапом, который Hibernate делает, чтобы сначала сохранить OrderItem
, а затем получить его идентификатор из базы данных, чтобы он мог сохранить orderSubItems
? Если так, почему это вызывает проверку?