Как избежать списка в объекте, содержащем элемент перед сохранением, но сбой при сохранении времени? - PullRequest
0 голосов
/ 11 июня 2019

Мне нужно разрешить пользователям редактировать заказы, которые включают добавление новых позиций и подпунктов под этими элементами заказа. Я смоделировал три сущности соответственно: 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? Если так, почему это вызывает проверку?

1 Ответ

0 голосов
/ 11 июня 2019

Иногда я предпочитаю использовать DTO для настройки и предоставления только тех полей, которые мне нужны, вместо того, чтобы предоставлять свои сущности клиентам моего API.Я думаю, что легче обрабатывать объекты, когда у вас есть данные в других объектах.
Например:

@Data
class OrderDTO {
    private Long orderId;
    private List<ItemDTO> items;
}

public void update(@Valid @RequestBody OrderDTO orderDTO) {
    Order order = repository.load(orderDTO.getId());
    for (OrderItem orderItem : order.getItems()) {
         repository.delete(orderItem);
    }
    order.getItems().clear();

    for (ItemDTO newItem : orderDTO.getItems()) {
        OrderItem orderItem = new OrderItem(newItem.getFieldA(), newItem.getFieldB());
        orderItem.setOrder(order);
        repository.save(orderItem);
        order.getItems().add(orderItem);
    }
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...