【问题标题】:Spring Boot JPA how to handle child table updatesSpring Boot JPA 如何处理子表更新
【发布时间】:2020-07-22 20:04:37
【问题描述】:

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

我不知道应该如何处理子表的更新。我目前正在使用 Spring Crud Repository 来存储基本表,但已经有了我的第一个父子关系。

鉴于以下示例表结构,我应该如何管理更新?

+--------------+ |订购 | +--------------+ |订单号 | PK | +--------------+ +--------------+ |订单行 | +--------------+ |订单号 | PK | +--------------+ |行号 | PK | +--------------+

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

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

Spring Boot 和 JPA 的常规方法是什么?

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

【问题讨论】:

    标签: java spring-boot jpa data-persistence


    【解决方案1】:

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

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

    我可以先删除所有现有的 orderLines 并重新创建它们

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

    Spring Boot 和 JPA 的常规方法是什么? 我应该在父实体上使用某种级联设置吗?

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 字段名称并不重要,这只是我制作的一个示例表。但是应该提到,是的 orderLine.OrderNumber 是一个外键,我只是假设它们在我的示例中具有相同的字段名称。我会在创建表时应用 FK。
      【解决方案3】:

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

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

      【讨论】:

      • 所以对于这个例子,我是否只做一个 repository.save(order) 并且保存将通过基于关系持久化所有表来流动?我考虑首先删除所有 OrderLine 数据的主要原因是,如果删除了一行,比如 OrderLine 2,那么数据现在将只包含 OrderLine 1 和 3,并且数据中不会有 orderLine 2。删除所有内容可以使数据更清洁。可能是我原来问题的一个单独问题。
      • @mike8787:我经历过你的情况。我认为保持数据库清洁一点并不是真正必要的。当您的应用程序使用太大的数据进行扩展时,如果您试图逐字处理您的数据库,您将会感到疲倦。您只需要一个查询,数据就会完全按照您的预期返回。
      猜你喜欢
      • 2018-10-14
      • 1970-01-01
      • 2021-08-13
      • 1970-01-01
      • 1970-01-01
      • 2021-03-15
      • 1970-01-01
      • 2021-04-17
      • 1970-01-01
      相关资源
      最近更新 更多