Loading published curriculum…

13 · Persistence / LESSON 61 OF 102

Service transactions and durable CRUD

Group changes into one business operation.

Before this lesson: Repositories and pagination

Understand the topic

@Transactional wraps a proxied call in a transaction. Loaded entities are managed; changed fields flush at commit. Runtime exceptions normally roll back. Calling your own method directly can bypass the usual proxy interception.

Inject this service into the controller. Add POST /api/tasks returning a response DTO, 201, and Location using the persisted ID. Add read, paginated list, update, and delete routes. Ownership checks are introduced in the security section.

EXAMPLE · JAVA

src/main/java/com/example/tasks/TaskService.java

package com.example.tasks;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.http.HttpStatus;
@Service
public class TaskService {
    private final TaskRepository repository;
    private final TitleService titles;
    public TaskService(TaskRepository repository, TitleService titles) {
        this.repository = repository; this.titles = titles;
    }
    @Transactional
    public Task create(String title) {
        return repository.save(new Task(titles.normalize(title)));
    }
    @Transactional
    public void complete(long id) {
        Task task = repository.findById(id).orElseThrow(() ->
            new ResponseStatusException(HttpStatus.NOT_FOUND, "Task not found"));
        task.complete();
    }
}

What to expect

create stores a row; complete updates a managed entity. Call through the injected service bean.

YOUR TURN

Put it into practice

  1. 01Connect create to POST and return a DTO.
  2. 02Throw a runtime exception after an update and verify rollback.
  3. 03Explore @Version for conflicting updates.

Pass a quiz or complete a lab to check today’s streak. Each lesson counts once per day, including when you review it later.

UNDER THE HOOD

Build the mental model

A transaction defines an atomic business change, while isolation and locking govern competing changes.

  1. 01A managed entity can be dirty-checked and flushed at commit. save is not required after every setter on that managed instance.
  2. 02Proxy-based transactions apply when calls cross the managed proxy. A direct call from one method to another on the same instance can bypass interception.
  3. 03Optimistic locking with a version column detects stale updates. It does not automatically decide how the client should resolve the conflict.

A mistake worth avoiding

Do not keep database transactions open during slow external HTTP calls without a deliberate reason.

DEBUGGING ROOM

Something broke. Where do you look?

A method annotated transactional does not roll back as expected when called internally.

Before revealing the diagnosis, write down your first investigation step.

CHECK YOUR UNDERSTANDING

Why can self-invocation bypass transactional behavior?

Choose one answer

BUILD IT YOURSELF

Prove atomicity and detect conflicts

  1. 1.Update two related rows in one service operation.
  2. 2.Fail between the writes and verify rollback.
  3. 3.Add a version field and exercise two competing updates.

Acceptance checklist

Lab completion is your self-report. Knowledge checks do not certify project quality.

RETRIEVE IT FROM MEMORY

What is the smallest complete business operation that must be atomic?

Revisit this lesson in three days after a successful check or lab.

Ready for the next step?

Explain commit, rollback, and the service boundary.

Reading is separate from passing a knowledge check or completing a lab.

Go deeper · official references