如何避免在保存之前包含元素但在持久时失败的实体中的列表?



我需要允许用户编辑订单,包括添加新的订单项目和这些订单项目下的子项目。我相应地对三个实体进行了建模,OrderOrderItems和OrderSubItems。每个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;
}

当用户请求更新订单时,例如使用新OrderSubItem添加新OrderItem,将调用以下控制器:

@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,抱怨新添加的订单项的订单子项为 null。

为什么验证失败?我认为这可能与双向关系和列表的操纵有关。

更新 1

我创建了一个自定义@NotNull验证器,以便我可以检查OrderItem和 lo 的内容,并在持久验证期间看到它orderSubItems为 null。

更新 2

经过更多的研究,我发现我添加到OrderItem的任何@OneToMany属性都将仅由一个 Hibernate 似乎创建的对象为 null。没错,看来 冬眠创造了一个OrderItem.关系是否是双向的并不重要。列表是否纵也无关紧要。其他属性填充良好。现在,这是奇怪的部分:

如果我在Order级别编写一个自定义验证器来检查其OrderItem是否具有非空orderSubItems并删除orderSubItems上的NotNull注释,它会毫无问题地通过验证和保存。

也就是说,Hibernate创建的OrderItem似乎没有附加到Order对象。这是Hibernate首先保存OrderItem,然后从数据库中获取其id以便它可以保存其orderSubItems的临时步骤吗?如果是这样,为什么它调用验证?

有时我更喜欢使用 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);
}
}

相关内容

  • 没有找到相关文章

最新更新