Implement Delete Task
Now that we can create, retrieve, and update tasks, let's implement the ability to delete them. However, rather than just a simple delete like we used in TaskLists, let’s make use of transactions as it’s a great topic to move onto once you’ve completed this project.
Updating the Service Interface
First, let's add the delete method to our TaskService interface:
public interface TaskService {
List<Task> listTasks(UUID taskListId);
Task createTask(UUID taskListId, Task task);
Optional<Task> getTask(UUID taskListId, UUID taskId);
Task updateTask(UUID taskListId, UUID taskId, Task task);
void deleteTask(UUID taskListId, UUID taskId); // Add this method
}Implementing the Service Method
Now let's implement the delete functionality in TaskServiceImpl:
@Service
public class TaskServiceImpl implements TaskService {
private final TaskListRepository taskListRepository;
private final TaskRepository taskRepository;
public TaskServiceImpl(TaskListRepository taskListRepository,
TaskRepository taskRepository) {
this.taskListRepository = taskListRepository;
this.taskRepository = taskRepository;
}
@Transactional
@Override
public void deleteTask(UUID taskListId, UUID taskId) {
taskRepository.deleteByTaskListIdAndId(taskListId, taskId);
}
}Let's examine the key aspects of this implementation:
-
@Transactional Annotation:
- The
@Transactionalannotation ensures the delete operation happens within a database transaction - If any part of the delete operation fails, the entire operation is rolled back
- This maintains database consistency even if errors occur during deletion
- Particularly important when deleting related data or updating multiple records
- The
-
Method Parameters:
taskListIdto scope the operation to a specific task listtaskIdto identify the specific task to delete
-
Repository Method:
- Uses a specific
deleteByTaskListIdAndIdmethod to ensure task ownership - Spring Data JPA generates the appropriate DELETE query
- The transaction ensures atomicity of the delete operation
- Uses a specific
Why @Transactional is Important
The @Transactional annotation is crucial for database operations like deletion because it:
-
Ensures Atomicity:
- The operation either completely succeeds or completely fails
- No partial deletions that could leave the database in an inconsistent state
-
Manages Database Connections:
- Automatically handles database connection acquisition and release
- Prevents connection leaks even if exceptions occur
-
Provides Rollback Support:
- Automatically rolls back changes if an exception occurs
- Maintains data consistency in error scenarios
-
Handles Related Data:
- Ensures proper cleanup of related records
- Maintains referential integrity
Updating the Controller
Now let's add the delete endpoint to our TasksController:
@RestController
@RequestMapping(path = "/task-lists/{task_list_id}/tasks")
public class TasksController {
private final TaskService taskService;
private final TaskMapper taskMapper;
public TasksController(TaskService taskService, TaskMapper taskMapper) {
this.taskService = taskService;
this.taskMapper = taskMapper;
}
@DeleteMapping(path = "/{task_id}")
public void deleteTask(
@PathVariable("task_list_id") UUID taskListId,
@PathVariable("task_id") UUID taskId) {
taskService.deleteTask(taskListId, taskId);
}
}Let's break down the key components:
-
URL Mapping:
@DeleteMappingmaps this to HTTP DELETE requests- Path includes both task list ID and task ID
- Maintains REST resource hierarchy (/task-lists/{task_list_id}/tasks/{task_id})
-
Parameters:
- Two
@PathVariableannotations extract IDs from the URL - No request body needed for delete operations
- Two
-
Implementation:
- Simply delegates to the service layer
- Returns void, which Spring will convert to a 200 OK response
- No response body needed for DELETE operations
How It Works Together
When a DELETE request arrives at /task-lists/{task_list_id}/tasks/{task_id}:
- Spring extracts both IDs from the URL path
- The controller calls
taskService.deleteTask() - A transaction begins
- The service delegates to
taskRepository.deleteByTaskListIdAndId() - The repository:
- Deletes the task if it exists
- Does nothing if the task doesn't exist
- The transaction commits
- Spring sends a 200 OK response with no body
For example, a DELETE request to /task-lists/123/tasks/789 will:
- Begin a transaction
- Delete the task with ID "789" if it exists within task list "123"
- Commit the transaction
- Return a 200 OK status with no body
Testing the Implementation
We can test our delete functionality through the frontend:
- Create a task list and add some tasks
- Select a task you want to delete
- Send a DELETE request to the appropriate endpoint
- Verify the task is removed by listing the tasks again
The frontend should see the task disappear from the list after a successful deletion.
Summary
- Added delete functionality to the
TaskServicewith proper transaction management - Used
@Transactionalto ensure atomic delete operations - Implemented a DELETE endpoint in the controller
- Maintained proper task list scoping for the delete operation