Enterprise Java

Automatically Saving Child Entities in JPA

In JPA applications, we often encounter entities with parent-child relationships. Persisting these entities efficiently involves saving the parent and automatically persisting its associated child objects. This article explores how to achieve this automatic saving for both unidirectional and bidirectional relationships.

1. Understanding JPA Relationships

In JPA, relationships between entities are managed using annotations. There are several types of relationships:

  • One-to-One: A single instance of an entity is associated with a single instance of another entity.
  • One-to-Many: A single instance of an entity is associated with multiple instances of another entity.
  • Many-to-One: Multiple instances of an entity are associated with a single instance of another entity.
  • Many-to-Many: Multiple instances of an entity are associated with multiple instances of another entity.

For this article, we will focus on the One-to-Many and Many-to-One relationships.

1.1 Understanding CascadeType

The key to automatic child object persistence lies in the CascadeType property of JPA annotations (@OneToMany and @OneToOne). This property defines how persistence operations (persist, update, delete) on the parent entity cascade to its child entities.

Here are the main CascadeType options:

  • CascadeType.PERSIST: Saves child objects along with the parent.
  • CascadeType.MERGE: Updates existing child objects along with the parent.
  • CascadeType.REMOVE: Deletes child objects along with the parent.
  • CascadeType.ALL: Combines all the above behaviours.

2. Unidirectional Relationships

In a unidirectional relationship, the parent entity holds a collection of child entities, but the child entity has no reference back to the parent. Here is an example:

Parent Entity

Orders.java

@Entity
public class Orders {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    String customername;
    String Status;

    @OneToMany(cascade = CascadeType.PERSIST)
    private List<OrderItem> items = new ArrayList<OrderItem>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public List<OrderItem> getItems() {
        return items;
    }

    public void setItems(List<OrderItem> items) {
        this.items = items;
    }

    public String getCustomername() {
        return customername;
    }

    public void setCustomername(String customername) {
        this.customername = customername;
    }

    public String getStatus() {
        return Status;
    }

    public void setStatus(String Status) {
        this.Status = Status;
    }
}

Child Entity

OrderItem.java

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

  private String productname;

  private int quantity;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getProductname() {
        return productname;
    }

    public void setProductname(String productname) {
        this.productname = productname;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

}

In this example, the Orders entity has a List of OrderItem objects annotated with @OneToMany. The cascade property is set to CascadeType.PERSIST, ensuring that when an Order is saved, it’s associated OrderItem objects are also persisted automatically.

2.1 Saving Entities

Create a repository and a service to manage the saving of these entities.

2.1.1 Repository Interfaces

Parent Repository

OrderRepository.java

public interface OrderRepository extends JpaRepository<Orders, Long>{
    
}

Child Repository

OrderItemRepository.java

public interface OrderItemRepository extends JpaRepository<OrderItem, Long>{
    
}

2.1.2 Service Layer

Parent Service

OrderService.java

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public Orders saveParentWithChildren(Orders order) {
        return orderRepository.save(order);
    }
    
}

Below is a simple test to verify our implementation. Here is how to save the parent:

TestDataLoader.java

@Component
public class TestDataLoader implements CommandLineRunner {

    @Autowired
    private OrderService orderService;

    @Override
    public void run(String... args) throws Exception {
        Orders order = new Orders();

        order.setStatus("New Status");
        order.setCustomername("John Doe");

        // Create OrderItem objects
        OrderItem item1 = new OrderItem();
        item1.setProductname("Product A");
        item1.setQuantity(2);

        OrderItem item2 = new OrderItem();
        item2.setProductname("Product B");
        item2.setQuantity(1);

        order.getItems().add(item1);
        order.getItems().add(item2);

        orderService.saveParentWithChildren(order);

        // Verify that the parent and children are saved correctly
        System.out.println("Parent and children saved successfully");
    }

}

JPA will persist the Order and automatically issue the necessary SQL statements to persist the OrderItem objects with the correct foreign key references to the Order.

As shown in the log output below, Hibernate cascades the operation to the associated OrderItem entities and persists them automatically.

Fig 1: Log output of JPA automatically saving child objects
Fig 1: Log output of JPA automatically saving child objects

3. Bidirectional Relationships

In a bidirectional relationship, both the parent and child entities hold references to each other. Here’s an example:

Parent Entity

Department.java

@Entity
public class Department {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "department", cascade = CascadeType.PERSIST)
    private List<Employee> employees = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }
    
}

Child Entity

Employee.java

@Entity
public class Employee {
    
  @Id
  private Long id;

  private String name;

  @ManyToOne
  @JoinColumn(name = "department_id")
  private Department department;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
  
}

In this bidirectional example, the Child (Employee) entity has a reference to the Parent (Department) entity using the @ManyToOne annotation. The Parent entity’s @OneToMany annotation now uses mappedBy to specify the field in the Child entity that owns the relationship.

The Department entity has a List of Employee objects annotated with @OneToMany. Additionally, the Employee entity has a @ManyToOne relationship with Department. The cascade property remains set to CascadeType.PERSIST in the Department entity.

3.1 Saving Entities

2.1.1 Repository Interfaces

Parent Repository

DepartmentRepository.java

public interface DepartmentRepository extends JpaRepository<Department, Long>{
    
}

Child Repository

EmployeeRepository.java

public interface EmployeeRepository extends JpaRepository<Employee, Long>{
    
}

2.1.2 Service Layer

Parent Service

DepartmentService.java

@Service
public class DepartmentService {
    
    @Autowired
    private DepartmentRepository departmentRepository;

    @Transactional
    public Department saveParentWithChildren(Department department) {
        return departmentRepository.save(department);
    }
    
}

Saving the parent in this scenario requires setting the bidirectional relationship before persisting:

TestDataLoader.java

@Component
public class TestDataLoader implements CommandLineRunner {

    @Autowired
    private DepartmentService departmentService;

    @Override
    public void run(String... args) throws Exception {

        Department department = new Department();
        department.setName("Engineering");

        Employee emp1 = new Employee();
        emp1.setName("John Doe");
        emp1.setDepartment(department);

        Employee emp2 = new Employee();
        emp2.setName("Jane Doe");
        emp2.setDepartment(department);

        department.getEmployees().add(emp1);
        department.getEmployees().add(emp2);
        
        departmentService.saveParentWithChildren(department);
    }

}

By setting the department property in each Employee object and adding them to the department.employees list, we establish the bidirectional relationship. When department is persisted, JPA cascades the PERSIST operation to the Employee objects, creating them in the database with the correct foreign key references.

Log output:

Hibernate: 
    insert 
    into
        Department
        (name, id) 
    values
        (?, default)
Hibernate: 
    select
        next value for Employee_SEQ
Hibernate: 
    select
        next value for Employee_SEQ
Hibernate: 
    insert 
    into
        Employee
        (department_id, name, id) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        Employee
        (department_id, name, id) 
    values
        (?, ?, ?)

4. Conclusion

In this article, we explored how to save parent entities along with their child entities automatically using JPA. We covered both unidirectional and bidirectional relationships, providing code examples. With these techniques, you can manage complex entity relationships more efficiently in your JPA-based applications.

5. Download the Source Code

This article is a guide on how to automatically save child objects using JPA.

Download
You can download the full source code of this example here: JPA save child objects automatically

Omozegie Aziegbe

Omos holds a Master degree in Information Engineering with Network Management from the Robert Gordon University, Aberdeen. Omos is currently a freelance web/application developer who is currently focused on developing Java enterprise applications with the Jakarta EE framework.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button