java - 使用 JpaRepository 更新实体中的列表项(保留主键)

标签 java spring-boot jpa arraylist spring-data-jpa

最好用一个例子来提问,这样......

实体 PurchaseOrder有一个列表 PurchaseOrderDetailOneToMany关系。 Entitylist items无法直接保存使用的 JPA 方法,因为涉及一些计算。 当我们对 PurchaseOrder 执行更新时我们必须保留主键,以便我们可以像这样更新

PurchaseOrder purchaseOrder = purchaseOrderRepository.getOne(purchaseOrderRequest.getId());
purchaseOrder.setSupplier(purchaseOrderRequest.getSupplier());
purchaseOrder.setDollarRate(purchaseOrderRequest.getDollarRate());
purchaseOrder.setShippingCost(purchaseOrderRequest.getShippingCost());
return purchaseOrderRepository.save(purchaseOrder);

但是如何Beautifully更新List<PurchaseOrderDetail> purchaseOrderDetails

我有以下代码,它将删除现有列表项并插入丢失主键的新项目。但是 JPA way 是什么?这样做,以便在更新期间发生以下情况

1. If some items were deleted they should be deleted from DB.
2. If some items were updated they should be updated preserving the primary keys.
3. If some new items are added they should be created having new primary keys.

代码

purchaseOrder.setPurchaseOrderDetails(null);

        for (PurchaseOrderDetailRequest purchaseOrderDetailRequest : purchaseOrderRequest.getPurchaseOrderDetails())
        {
            PurchaseOrderDetail purchaseOrderDetail = new PurchaseOrderDetail();
            purchaseOrderDetail.setPartNumber(purchaseOrderDetailRequest.getPartNumber());
            purchaseOrderDetail.setDescription(purchaseOrderDetailRequest.getDescription());
            purchaseOrderDetail.setQuantity(purchaseOrderDetailRequest.getQuantity());
            purchaseOrderDetail.setUnitPriceInDollars(purchaseOrderDetailRequest.getUnitPriceInDollars());
            purchaseOrderDetail.setTotalPriceInDollars(purchaseOrderDetailRequest.getQuantity() * purchaseOrderDetailRequest.getUnitPriceInDollars());
            purchaseOrderDetail.setUnitPriceInSAR(purchaseOrder.getDollarRate() * purchaseOrderDetailRequest.getUnitPriceInDollars());
            purchaseOrderDetail.setTotalPriceInSAR(purchaseOrderDetailRequest.getQuantity() * purchaseOrderDetail.getUnitPriceInSAR());
            purchaseOrderDetail.setUnitCost(purchaseOrderDetail.getUnitPriceInSAR() + shippingFactor);
            purchaseOrder.addPurchaseOrderDetail(purchaseOrderDetail);
        }

采购订单

@Entity
@Table(name = "purchaseOrders")
public class PurchaseOrder extends UserDateAudit
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @Size(max = 140)
    private String supplier;

    @NotNull
    private Float dollarRate;

    @NotNull
    private Float amount;

    @NotNull
    private Float shippingCost;

    @OneToMany(
            mappedBy = "purchaseOrder",
            cascade = CascadeType.ALL,
            fetch = FetchType.EAGER,
            orphanRemoval = true
    )
    @Fetch(FetchMode.SELECT)
    @BatchSize(size = 150)
    private List<PurchaseOrderDetail> purchaseOrderDetails = new ArrayList<>();
}

采购订单详细信息

@Entity
@Table(name = "purchaseOrderDetails")
public class PurchaseOrderDetail
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @Size(max = 50)
    private String partNumber;

    @Size(max = 256)
    private String description;

    @NotNull
    private Integer quantity;

    @NotNull
    private Float unitPriceInDollars;

    @NotNull
    private Float totalPriceInDollars;

    @NotNull
    private Float unitPriceInSAR;

    @NotNull
    private Float totalPriceInSAR;

    @NotNull
    private  Float unitCost;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "purchaseOrder_id", nullable = false)
    private PurchaseOrder purchaseOrder;
}

最佳答案

JpaRepository.save() 根据实体状态执行 em.persist()em.merge()
并且您还配置了 @OneToMany 关系与 cascade = CascadeType.ALLorphanRemoval = true 的映射,因此您的方式是正确的.
您需要在代码中执行的操作是更新 PurchaseOrder 中定义的列表的 PurchaseOrderDetail 元素,而不是为它们创建新实例。
如果需要,您可以在映射中添加现有元素以加快查找处理速度。

// collect existing details into a map to fast up
Map<Long, PurchaseOrderDetail> purchaseOrderDetailByIdMap = 
purchaseOrder.getPurchaseOrderDetails()
             .stream()
             .collect(toMap(PurchaseOrderDetail::getId, p -> p);

//collect new details into a list
List<PurchaseOrderDetail> updatedDetails = 
purchaseOrderRequest.getPurchaseOrderDetails()
                    .stream()
                    .map(detailReq -> mapDetailRequestToDetail(detailReq, 
                                      purchaseOrderDetailByIdMap))
                    .collect(toList());

// overwrite the relationship with the updatedDetails var
purchaseOrder.setPurchaseOrderDetails(updatedDetails);

// ...      

// mapping function extracted to be clearer
private PurchaseOrderDetail mapDetailRequestToDetail(PurchaseOrderDetailRequest purchaseOrderDetailRequest, 
                         Map<Long, PurchaseOrderDetail> purchaseOrderDetailByIdMap) {             

    // Here you get the existing element with the defined id or you create a new one            
    PurchaseOrderDetail purchaseOrderDetail = 
    purchaseOrderDetailByIdMap.computeIfAbsent(purchaseOrderDetailRequest.getId(), k-> new PurchaseOrderDetail());

    // set fields
    purchaseOrderDetail.setPartNumber(purchaseOrderDetailRequest.getPartNumber());
    purchaseOrderDetail.setDescription(purchaseOrderDetailRequest.getDescription());
    purchaseOrderDetail.setQuantity(purchaseOrderDetailRequest.getQuantity());
    purchaseOrderDetail.setUnitPriceInDollars(purchaseOrderDetailRequest.getUnitPriceInDollars());
    purchaseOrderDetail.setTotalPriceInDollars(purchaseOrderDetailRequest.getQuantity() * purchaseOrderDetailRequest.getUnitPriceInDollars());
    purchaseOrderDetail.setUnitPriceInSAR(purchaseOrder.getDollarRate() * purchaseOrderDetailRequest.getUnitPriceInDollars());
    purchaseOrderDetail.setTotalPriceInSAR(purchaseOrderDetailRequest.getQuantity() * purchaseOrderDetail.getUnitPriceInSAR());
    purchaseOrderDetail.setUnitCost(purchaseOrderDetail.getUnitPriceInSAR() + shippingFactor);

    return purchaseOrderDetail;         
}

关于java - 使用 JpaRepository 更新实体中的列表项(保留主键),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54386667/

相关文章:

java - Hibernate HQL 查询子选择或联接

java - 如何在 Java 中比较字符串?

Spring Boot 过滤器模式匹配错误的 url

java - 推荐用于 Java、Hibernate 和 Spring 的记录器

java - 如何使用 mockmvc 处理 Controller 异常

java - 单击 RecyclerView 项目时出现 NullPointerException

spring-boot - 如何将 Spring WebClient 与 Jetty 一起使用,而不是 Netty?

java - 初始化错误: Unable to find a @SpringBootConfiguration

java - 在gradle中定义变量填充persistence.xml中的数据

java - 无法删除具有 ManyToOne 关系的父实体