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.
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.
You can download the full source code of this example here: JPA save child objects automatically





