Search code examples
javaspring-bootjpadata-persistence

Spring Boot JPA how to handle child table updates


I've come from a background in my previous role of doing all data persistence with all custom SQL and JDBC. Just started a new job for a small business where I'm the only developer and have decided to develop the businesses systems using Spring Boot with JPA for data persistence.

I don't know how I should be handling updates on child tables. I'm currently using the Spring Crud Repository for basic tables but have got to my first parent child relationship.

Given the below example table structure, how should I be managing updates?

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

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

The users could update or delete existing order lines.

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

What is the normal approach with Spring Boot and JPA?

Should I be using some kind of cascade settings on the parent entity?


Solution

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

    updating only the orderlines that need an update is the best practice.

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

    That would cause to make a lot of delete queries and lot of insert queries which is bad. The autogenrated id of orderlines would also increase rapidly which is not what you want.

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

    Yes, you should. Here is an example:

    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
    }
    

    And finally an update example:

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