我有一个相对嵌套的订单域对象,其中有一个 OrderItem 列表,而 OrderItem 列表又包含一个供应商列表。我想做一个更新插入,以便可以添加新订单,并且它们相应的订单不存在。问题是我不知道如何处理嵌套集合的更新。我正在使用 Spring Boot 3 和 Reactive Mongo。是否可以在实体级别进行更新,即将 OrderItem 作为对象进行更新,而不必更新各个字段?
这是对象模型
// Order class
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
@Document(collection = "order")
@Data
public class Order {
@Id
String orderId;
List<OrderItem> orderItemList;
}
// OrderItem class
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "order-item")
public class OrderItem {
@Id
String orderItemId;
BigDecimal price;
String partNumber;
String description;
int quantityInStock;
List<Supplier> suppliers;
}
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "supplier")
@Data
public class Supplier {
@Id
String supplierId;
String supplierName;
String supplierPartNumber;
}
以下是执行更新插入的代码的开头。缺少的部分是嵌套对象片段,我不知道该怎么做:我相信代码的其余部分很好,当然除了嵌套对象的缺失部分。理想情况下,我想在更新中使用整个实体,例如 update("$[orderItems].orderItem", orderItem) 但我不知道这是否可能以及如果可能的话使用什么语法。
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderService {
private final MongoOperations mongoOperations;
private final static String ORDER_COLLECTION = "orders";
public void upsertOrders(List<Order> orders, List<OrderItem> orderItems) {
try {
if (!ordersItems.isEmpty()) {
BulkOperations bulkTemplateOperations = mongoOperations.bulkOps(BulkOperations.BulkMode.UNORDERED, ORDER_COLLECTION);
for (Order order : orders) bulkTemplateOperations.upsert(new Query(Criteria.where("_id").is(order.getOrderId())), createOrderUpdate(order));
bulkTemplateOperations.execute();
}
} catch (Exception e) {
log.error("Failed to update orders collection {}",e.getMessage());
throw e;
}
}
protected Update createOrderUpdate(Order order) {
Update update = new Update();
update.set("orderId", order.getOrderId());
// how to do for OrderItems
return update;
}
}
我希望能得到一些帮助来填补我理解上的空白。预先感谢
您的代码没问题,您可以使用整个对象更新单个文档,而不是更新每个字段。我在这里提交:https://github.com/sbernardo/spring-issues-examples/tree/main/sof-questions-77623978完整代码。
您有2个选择:
protected Update createOrderUpdate(Order order) {
Update update = new Update();
update.set("orderId", order.getOrderId());
update.set("orderItemList", order.getOrderItemList());
return update;
}
protected Update createOrderUpdateWithAddToSet(Order order) {
Update update = new Update();
update.set("orderId", order.getOrderId());
order.getOrderItemList().forEach(oi -> update.addToSet("orderItemList", oi));
return update;
}
第一个用输入列表替换整个列表(仅当发生更改时)。第二个添加一个新的订单项到列表中(如果它已经存在)。
注意:我建议使用第一个,这样您就可以管理整个列表,只有当您想添加新内容时才必须使用第二个(不适用于更新数组中的单个元素,仅适用于添加新元素).
我尝试了项目中的第一个(请参阅以下代码)来管理单个订单:
@PatchMapping("orders")
public String myEndpoint() {
var supplierFirstOrderItem = new Supplier("suppId1", "Supplier1", "PART_NUM122");
var suppliersFirstOrderItem = List.of(supplierFirstOrderItem);
var firstOrderItem = new OrderItem("IDIT1", BigDecimal.ONE, "PART_NUM1", "Ordered from Amazon", 1, suppliersFirstOrderItem);
var orderItems = new ArrayList<>(List.of(firstOrderItem));
var order = new Order("ID1", orderItems);
var orders = List.of(order);
//First upsert -> new order -> INSERT DONE
var firstUpsert = service.upsertOrders(orders, orderItems);
log.info("First upsert result: {}", firstUpsert);
//Second upsert -> add a new order item on an existing order -> UPDATE DONE
var supplierSecondOrderItem = new Supplier("suppId2", "Supplier2", "PART_NUM211");
var suppliersSecondOrderItem = List.of(supplierSecondOrderItem);
var newOrderItem = new OrderItem("IDIT2", BigDecimal.TEN, "PART_NUM2", "Ordered from Ebay", 100, suppliersSecondOrderItem);
orderItems.add(newOrderItem);
var secondUpsert = service.upsertOrders(orders, orderItems);
log.info("Second upsert result: {}", secondUpsert);
//Third upsert -> modified price on existing order item -> UPDATE DONE
order.getOrderItemList().getFirst().setPrice(BigDecimal.valueOf(10000));
var thirdUpsert = service.upsertOrders(orders, orderItems);
log.info("Third upsert result: {}", thirdUpsert);
//Fourth upsert -> writing same order -> NO UPDATE DONE
var fourthUpsert = service.upsertOrders(orders, orderItems);
log.info("Fourth upsert result: {}", fourthUpsert);
var newOrderListOrdered = List.of(newOrderItem, firstOrderItem);
order.setOrderItemList(newOrderListOrdered);
//Fifth upsert -> writing same order (different orderItems internal list) -> UPDATE DONE
var fifthUpsert = service.upsertOrders(orders, orderItems);
log.info("Fifth upsert result: {}", fifthUpsert);
var newOrderListDeleteOne = List.of(newOrderItem);
order.setOrderItemList(newOrderListDeleteOne);
//Sixth upsert -> writing same order (orderItems deleted one) -> UPDATE DONE
var sixthUpsert = service.upsertOrders(orders, orderItems);
log.info("Sixth upsert result: {}", sixthUpsert);
return sixthUpsert.toString();
}
输出日志向您建议 mongo 做了什么
First upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=0, removedCount=0, modifiedCount=0, upserts=[BulkWriteUpsert{index=0, id=BsonString{value='ID1'}}], inserts=[]}
Second upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=1, upserts=[], inserts=[]}
Third upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=1, upserts=[], inserts=[]}
Fourth upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=0, upserts=[], inserts=[]}
Fifth upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=1, upserts=[], inserts=[]}
Sixth upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=1, upserts=[], inserts=[]}
如您所见,只有发生更改才会更新(列表的顺序很重要!更新不同的顺序!)
希望这会有用! :)