【问题标题】:Updating List items(preserving primary keys) in an entity using JpaRepository使用 JpaRepository 更新实体中的列表项(保留主键)
【发布时间】:2019-06-20 13:13:30
【问题描述】:

最好用一个例子来问……

实体PurchaseOrder 有一个PurchaseOrderDetail 列表。 OneToMany 关系。 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;
}

【问题讨论】:

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


    【解决方案1】:

    JpaRepository.save() 根据实体状态执行em.persist()em.merge()
    而且您还配置了@OneToManycascade = CascadeType.ALLorphanRemoval = true 关系的映射,所以您的方法是正确的。
    您必须在代码中更新 PurchaseOrder 中定义的 List 的 PurchaseOrderDetail 元素,而不是为它们创建新实例。
    如果需要,您可以在 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;         
    }
    

    【讨论】:

    • 感谢您的回复。映射功能似乎不起作用。你能详细说明一下吗?或者我该如何实现它?
    • 现在我得到这个错误 HibernateException: A collection with cascade="all-delete-orphan" 不再被拥有实体实例引用
    • 如果您将purchaseOrder.setPurchaseOrderDetails(updatedDetails); 替换为purchaseOrder.getPurchaseOrderDetails().addAll(updatedDetails); 以获得拥有对象的引用?
    猜你喜欢
    • 2019-11-15
    • 1970-01-01
    • 1970-01-01
    • 2017-09-11
    • 2011-03-16
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    • 2017-06-28
    相关资源
    最近更新 更多