我在 Spring Boot 示例中创建订单时遇到问题。当我向如下所示的 createOrder 发送 CreateOrderRequest 时,我遇到了这个问题。
{
"orderDetailSet": [
{
"bookId": "bookId",
"amount": 1
}
]
}
这是下图所示的问题
java.sql.SQLIntegrityConstraintViolationException: Column 'order_id' cannot be null
我还修改了createOrder的相关部分,但没有任何改变。我仍然遇到同样的问题。
Order order = OrderMapper.toOrder(orderDTO);
// Associate each OrderItem with the Order
for (OrderItemDTO orderItemDTO : orderDetailDTOSet) {
OrderItem orderItem = OrderItemMapper.toOrderItem(orderItemDTO);
orderItem.setOrder(order);
}
// Save the Order, which should cascade to the associated OrderItems
Order orderCompleted = orderRepository.save(order);
return OrderMapper.toOrderDTO(orderCompleted);
这是如下所示的订单实体
@Entity
@Data
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "ORDERS")
public class Order extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "userId", nullable = false)
private User user;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<OrderItem> orderItems;
}
这是下面显示的 OrderItem
@Entity
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "ORDER_ITEMS")
public class OrderItem extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "bookId")
private Book book;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "orderId", referencedColumnName = "id", nullable = false)
private Order order;
}
这里是OrderSaveServiceImpl的createOrder方法如下所示
@Transactional
@Override
public OrderDTO createOrder(CreateOrderRequest createOrderRequest) {
CustomUserDetails customUserDetails = identity.getCustomUserDetails();
User user = userService.findByEmail(customUserDetails.getEmail())
.orElseThrow(() -> new UserNotFoundException(String.valueOf(customUserDetails.getId())));
UserDTO userDTO = UserMapper.toDTO(user);
Set<OrderItemDTO> orderDetailDTOSet = createOrderRequest
.getOrderDetailSet()
.stream()
.map(orderItemService::createOrderItem)
.collect(Collectors.toSet());
OrderDTO orderDTO = OrderDTO.builder()
.user(userDTO)
.orderItems(orderDetailDTOSet)
.createdAt(LocalDateTime.now())
.build();
Order order = OrderMapper.toOrder(orderDTO);
Order orderCompleted = orderRepository.save(order);
for (OrderItemDTO orderItemDTO : orderDetailDTOSet) {
OrderItem orderItem = OrderItemMapper.toOrderItem(orderItemDTO);
orderItem.setOrder(orderCompleted);
orderItemRepository.save(orderItem);
}
return OrderMapper.toOrderDTO(orderCompleted);
}
这里是OrderItemServiceImpl的createOrderItem方法,如下所示
@Retryable(retryFor = StockModifiedException.class, maxAttempts = 2, backoff = @Backoff(delay = 100))
@Override
public OrderItemDTO createOrderItem(OrderItemRequest orderDetailRequest) {
BookDTO bookDTO = bookService.getBookById(orderDetailRequest.getBookId());
boolean stockAvailable = bookService.isStockAvailable(bookDTO, orderDetailRequest.getAmount());
if (stockAvailable) {
OrderItemDTO.OrderItemBook orderItemBook = OrderItemDTO.OrderItemBook.builder()
.isbn(bookDTO.getIsbn())
.price(bookDTO.getPrice().multiply(BigDecimal.valueOf(orderDetailRequest.getAmount())))
.authorFullName(bookDTO.getAuthorFullName())
.id(bookDTO.getId())
.version(bookDTO.getVersion())
.build();
BookUpdateStockRequest bookUpdateStockRequest = BookUpdateStockRequest.builder()
.stock(bookDTO.getStock() - orderDetailRequest.getAmount())
.build();
bookService.updateBookStockById(bookDTO.getId(), bookUpdateStockRequest);
return OrderItemDTO.builder()
.book(orderItemBook)
.build();
}
throw new StockModifiedException();
}
我该如何修复它?
这是回购协议:链接
认为这是因为当你保存一个
Order
时,它也会级联保存它的OrderItem
,因为CascadeType.ALL
中的
@OneToMany
但是当时的
OrderItem
还没有配置它的 order
字段,所以它是 NULL,所以它的映射列 order_id
也抛出 SQLIntegrityConstraintViolationException
。
因此,请确保在保存 Order 时,其所有订单项都配置了
order
字段,使其与此 Order 实例相同。您可以通过在 Order
中创建辅助函数来添加 OrderItem
: 来简化它
@Entity
@Table(name = "ORDERS")
public class Order extends BaseEntity {
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<OrderItem> orderItems;
public void add(OrderItem item){
this.orderItems.add(item);
item.setOrder(this);
}
}
也因为
CascadeType.ALL
,你不需要单独调用保存订单项。只需准备一个订单并正确配置其所有项目的状态,然后仅保存订单。
还有一条评论仅供参考,
FetchType.EAGER
对我来说是一种代码味道。强烈建议您将其更改为延迟加载。