Spring 引导 JPA 如何处理 child table 更新

Spring Boot JPA how to handle child table updates

我以前的工作背景是使用所有自定义 SQL 和 JDBC 进行所有数据持久化。刚开始为一家小型企业开展新工作,我是其中唯一的开发人员,并决定使用 Spring 使用 JPA 引导开发业务系统以实现数据持久性。

我不知道我应该如何处理 child table 上的更新。我目前正在使用 Spring Crud 存储库进行基本 tables,但我已经建立了我的第一个 parent child 关系。

给定以下示例 table 结构,我应该如何管理更新?

  +-------------------------+
  | Order                   |
  +-------------------------+
  | orderNumber      | PK   |
  +-------------------------+

  +-------------------------+
  | OrderLine               |
  +-------------------------+
  | orderNumber      | PK   |
  +-------------------------+
  | lineNumber       | PK   |
  +-------------------------+

用户可以更新或删除现有的订单行。

进行更新时,我可以先删除所有现有的 orderLine,然后重新创建它们,但不确定这是否是不好的做法?

使用 Spring Boot 和 JPA 的正常方法是什么?

我应该在 parent 实体上使用某种级联设置吗?

你能把 orderNumber 换成 orderId 吗?通过外键 orderIdOrder table 的 orderLine table 引用对您的代码具有更多意义。如果可以,使用连接列和级联配置,而不是删除所有 orderLine 并重新创建它们,让 JPA 为您完成。

您可以在 Spring Data JPA 中为您的实体执行以下映射以复制上述要求。您正在尝试的是 OrderOrderLine 之间的组合关系,其中前者是所有者而不是 parent/child 关系。

@Entity
@Table(name="Order")
class Order {
    @Id
    private Long orderNumber;

    @OneToMany(mappedBy="order")
    private List<OrderLine> orderLines;

}

@Entity
@Table(name="OrderLine")
class OrderLine {
    @ManyToOne
    @JoinColumn(name="orderNumber", nullable=false)
    private Order order;

    private Long lineNumber;

}

When doing an update, I could delete all of the existing orderLines first and recreate them but not sure if this is bad practice?

最好只更新需要更新的订单行。

I could delete all of the existing orderLines first and recreate them

这会导致进行大量删除查询和大量插入查询,这很糟糕。订单行的自动生成 id 也会迅速增加,这不是您想要的。

What is the normal approach with Spring Boot and JPA? Should I be using some kind of cascade settings on the parent entity?

是的,你应该。这是一个例子:

Order.java

@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long orderNumber;

    @Version
    private Long version = 0L;

    @OneToMany(mappedBy="order", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<OrderLine> orderLines;

    // equals() and hashcode() implementation
    // getter() and setter() implementation
}

OrderLine.java

@Entity
public class OrderLine {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long lineNumber;

    @ManyToOne
    @JoinColumn(name="order_id", nullable=false)
    private Order order;

    @Version
    private Long version = 0L;

    // equals() and hashcode() implementation
    // getter() and setter() implementation
}

最后是一个更新示例:

public boolean update(Order order, long orderLineId, OrderLine updatedOrderLine) {
    if(order == null || updatedOrderLine == null)
        return false;

    if(order.getOrderLines() == null)
        return false;

    Optional<OrderLine> optTargetOrderLine = order.getOrderLines().stream()
.filter(orderline -> orderLine.getLineNumber() == orderLineId).findFirst();

    if(!optTargetOrderLine.isPresent())
        return false;

    OrderLine targetOrderLine = optTargetOrderLine.get();

    // now implement the update function
    boolean status = update(targetOrderLine, updatedOrderLine);

    // if status is true, you also have to call save() on your OrderRepository
    return status;
}