Task Task List Relationship

Now that we have our basic Task and TaskList entities, let's set up the relationship between them. Looking at our domain model, we can see that a TaskList contains many Tasks, and each Task belongs to exactly one TaskList.

Adding Tasks to TaskList

First, let's add the tasks field to our TaskList class. We'll use a List to hold the Task objects:

@OneToMany(mappedBy = "taskList", cascade = {CascadeType.REMOVE, CascadeType.PERSIST}) private List<Task> tasks;

Let's break down the annotations:

  • @OneToMany indicates that one TaskList can have many Tasks
  • mappedBy = "taskList" tells JPA that the Task entity owns the relationship through its taskList field
  • cascade = {CascadeType.REMOVE, CascadeType.PERSIST} means:
    • When we delete a TaskList, all its Tasks will be deleted too (REMOVE)
    • When we save a TaskList, any new Tasks it contains will be saved too (PERSIST)

Adding TaskList to Task

Now in our Task class, we need to add the reference back to TaskList:

@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "task_list_id") private TaskList taskList;

Let's break down these annotations:

  • @ManyToOne indicates that many Tasks can belong to one TaskList
  • fetch = FetchType.LAZY means the TaskList won't be loaded from the database until it's actually needed
  • @JoinColumn(name = "task_list_id") specifies the foreign key column name in the tasks table

Updating Constructors and Methods

Now we need to update our constructors, getters, and setters in both classes. Let's start with TaskList:

  1. Update the all-args constructor to include tasks:
public TaskList(UUID id, String title, String description, List<Task> tasks, LocalDateTime created, LocalDateTime updated) { this.id = id; this.title = title; this.description = description; this.tasks = tasks; this.created = created; this.updated = updated; }
  1. Add getter and setter for tasks:
public List<Task> getTasks() { return tasks; } public void setTasks(List<Task> tasks) { this.tasks = tasks; }

And for the Task class:

  1. Update the all-args constructor to include taskList:
public Task(UUID id, String title, String description, LocalDateTime dueDate, TaskPriority priority, TaskStatus status, LocalDateTime created, LocalDateTime updated, TaskList taskList) { this.id = id; this.title = title; this.description = description; this.dueDate = dueDate; this.priority = priority; this.status = status; this.created = created; this.updated = updated; this.taskList = taskList; }
  1. Add getter and setter for taskList:
public TaskList getTaskList() { return taskList; } public void setTaskList(TaskList taskList) { this.taskList = taskList; }

Don't forget to update your equals(), hashCode(), and toString() methods in both classes to include the new fields. Your IDE can help generate these for you.

A Note on Bi-directional Relationships

With this bi-directional relationship set up, when we load a TaskList, we can access its Tasks, and when we load a Task, we can access its TaskList

Summary

  • We added a one-to-many relationship in TaskList to manage its Tasks
  • We added a many-to-one relationship in Task to reference its TaskList
  • We configured cascading to handle Task lifecycle with its TaskList
  • We updated constructors and methods to handle the new relationship fields
© 2026 Devtiro Ltd. All rights reserved