Loslegen
Javaneer
Zurück zur Stufe
Modul 3·Daten & Persistenz

Transaktionen mit @Transactional

Mehrschrittige Änderungen ganz oder gar nicht durchführen, Propagation und Rollback verstehen und typische Fallen vermeiden.

18 Min. LesezeitExperte

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

Borrowing a book in BookVault is two changes that must happen together: decrement the book's available copies and create a loan record. If the first succeeds and the second fails, you've reserved a copy for a loan that doesn't exist - corrupt data. A transaction makes the pair all-or-nothing.

@Transactional

Annotate the method that must be atomic:

@Service
class LendingService {

    @Transactional
    public Loan borrow(String isbn, Long memberId) {
        Book book = bookRepository.findByIsbn(isbn).orElseThrow(...);
        if (book.getCopies() < 1) throw new NoCopiesAvailableException(isbn);

        book.setCopies(book.getCopies() - 1);          // change 1
        return loanRepository.save(new Loan(book, memberId));  // change 2
    }
}

Spring wraps the method in a database transaction: it commits if the method returns normally, and rolls back if it throws. Either both changes land, or neither does.

A transaction is all-or-nothing@Transactional wraps these operations in one unit: they all commit together, or all roll back.
0BEGIN
1SELECT * FROM book WHERE isbn = ?
2UPDATE book SET copies_available = copies_available - 1
3INSERT INTO loan (book_id, member_id, ...)

Rollback rules

By default, Spring rolls back on unchecked exceptions (RuntimeException and its subclasses) and commits otherwise. Your domain exceptions - like NoCopiesAvailableException - should extend RuntimeException, so a business failure automatically undoes the transaction.

Read-only for pure reads

Mark query-only methods @Transactional(readOnly = true). It lets the database and Hibernate optimize (no dirty-checking, possible replica routing) and documents intent: this method changes nothing.

Two pitfalls to remember

  • Self-invocation (from the AOP lesson): @Transactional works through a proxy, so one method in a class calling another this.method() bypasses it - no transaction starts. Call across beans, or restructure.
  • Propagation: if borrow() calls another @Transactional method, the default (REQUIRED) joins the same transaction - so an inner failure rolls back the whole thing. You rarely need to change this, but know it exists.
A transaction is a bank transfer

Moving money between accounts is a debit and a credit. If the debit happens but the credit fails, money vanishes. Banks make the pair a single transaction: both post, or neither does, and the books always balance. @Transactional gives your borrow-a-book operation the same guarantee - the copy count and the loan record can never disagree.

Why one annotation saves the data

In BookVault's borrow method, the copy-count decrement succeeds but the loan INSERT fails (say, a constraint violation). Explain what @Transactional does at that moment, and what the catalog looks like afterward compared with if the method had no transaction.

What does @Transactional do when a method throws a RuntimeException partway through?

Key takeaways

  • @Transactional makes a method's multiple database changes atomic: all commit together on success, or all roll back on failure.
  • By default Spring rolls back on unchecked (RuntimeException) exceptions; make domain exceptions unchecked so business failures undo the transaction.
  • Use @Transactional(readOnly = true) for query-only methods to optimize and document intent.
  • Self-invocation bypasses the proxy, so an internal this.method() call won't start a transaction.
  • Default propagation REQUIRED joins an existing transaction, so an inner failure rolls back the whole operation.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten