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

时间:2019-01-27 09:19:25

标签: 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<>();
}

PurchaseOrderDetail

@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;
}

1 个答案:

答案 0 :(得分:1)

JpaRepository.save()根据实体状态执行em.persist()em.merge()
并且您还配置了@OneToManycascade = CascadeType.ALLorphanRemoval = true的映射,因此您的方法正确。
您在代码中要做的是更新PurchaseOrderDetail中定义的List的PurchaseOrder元素,而不是为其创建新实例。
如果需要,可以将现有元素添加到Map中以加快查找过程。

// 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;         
}