Annotation Interface Transactional


@Target({METHOD,TYPE}) @Retention(RUNTIME) @Documented public @interface Transactional
Annotation to mark methods or classes for declarative transaction management.

When a method annotated with @Transactional is executed, it will automatically be wrapped in a database transaction. The transaction will be committed if the method completes normally, or rolled back if an exception is thrown.

Usage examples:


 // Basic usage - starts a new transaction or joins existing one
 @Transactional
 public void saveUser(User user) {
     userRepository.save(user);
     auditRepository.log("User saved: " + user.getName());
 }

 // Always create a new transaction
 @Transactional(propagation = Propagation.REQUIRES_NEW)
 public void logAudit(String message) {
     auditRepository.log(message);
 }

 // Read-only transaction (may allow optimizations)
 @Transactional(readOnly = true)
 public List<User> findAllUsers() {
     return userRepository.findAll();
 }

 // Custom rollback rules
 @Transactional(rollbackFor = {BusinessException.class})
 public void processOrder(Order order) throws BusinessException {
     // ...
 }
 

Important:

  • Self-invocation (calling a @Transactional method on this from within the same class) is intercepted: the framework's generated proxy is a subclass of the bean itself, not a delegate wrapping a separate target, so a call to this.method() dispatches virtually onto the proxy's override. Re-verified against three pre-existing shouldInterceptSelfInvocation test classes (2026-08-27)
  • Private, static, and final methods cannot be transactional - each is dispatched in a way that bypasses the proxy (respectively: invokespecial, invokestatic, and no override is possible); a package-private method is also ineligible when it is declared in a different package than the bean class
  • The class must not be final (subclass proxy limitation)
Since:
6.2.0
Author:
wisdomme
See Also:
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    The transaction isolation level.
    Class<? extends Throwable>[]
    Exception types that should NOT trigger a rollback, overriding the RuntimeException/ Error default for those types.
    The transaction propagation behavior.
    boolean
    Whether the transaction is read-only.
    Class<? extends Throwable>[]
    Exception types that should trigger a rollback, in addition to the RuntimeException/ Error default.
    int
    The transaction timeout in seconds.
  • Element Details

    • propagation

      Propagation propagation
      The transaction propagation behavior.

      Defaults to Propagation.REQUIRED, which joins an existing transaction or creates a new one if none exists.

      Returns:
      the propagation behavior
      Default:
      REQUIRED
    • isolation

      Isolation isolation
      The transaction isolation level.

      Defaults to Isolation.DEFAULT, which uses the database's default isolation level.

      Returns:
      the isolation level
      Default:
      DEFAULT
    • timeout

      int timeout
      The transaction timeout in seconds.

      Defaults to -1 (no timeout / use database default). Only a value greater than 0 is ever acted on at all -- the interceptor skips calling TransactionManager.setTimeout(int) entirely for -1 or 0, so both mean exactly "no timeout requested," on every backend.

      The bound is per statement against a shared, shrinking budget -- not a wall-clock limit on the method body. (D-10, Spring's approach.) When a value greater than 0 is set, the transaction gets a deadline N seconds out from when it began. Every individual JDBC statement issued afterward -- through the ORM's normal read/write methods, and through the direct batch paths in insertAll/updateAll -- is given a query timeout equal to whatever time is left in that budget when the statement is prepared, floored at 1 second so an exhausted budget still fails fast rather than becoming unlimited (setQueryTimeout(0) means "no limit" in the JDBC contract). This is deliberately not a wall-clock bound on the method as a whole: non-database work inside the method (a slow computation, a network call to something else) is never interrupted, because plain JDBC has no mechanism to cancel work already in flight, and this framework does not add one (see REQUIREMENTS.md's "out of scope" section).

      Per backend:

      • SQLite, MySQL (JDBC-backed): enforced exactly as described above.
      • JSON-backed: a positive value makes the transaction fail outright. JsonTransactionManager.setTimeout(int) throws UnsupportedOperationException unconditionally, since a snapshot-based rollback has no query or connection to bound.
      A method that leaves this attribute at its default is unaffected on every backend, since the interceptor never calls setTimeout for it.
      Returns:
      the timeout in seconds
      Default:
      -1
    • readOnly

      boolean readOnly
      Whether the transaction is read-only.

      A read-only transaction may allow database optimizations and is useful for methods that only query data.

      Returns:
      true if read-only
      Default:
      false
    • rollbackFor

      Class<? extends Throwable>[] rollbackFor
      Exception types that should trigger a rollback, in addition to the RuntimeException/ Error default.

      This is additive, not a replacement: an exception that matches neither rollbackFor nor noRollbackFor still falls through to the unchecked default, exactly as if neither attribute were set. When an exception matches both this and noRollbackFor(), the rule whose listed class is the shallower inheritance-depth match to the thrown exception wins (Spring's RuleBasedTransactionAttribute tiebreak); on an exact-depth tie - including the same class appearing in both arrays - the transaction rolls back.

      Returns:
      exception types to additionally rollback for
      Since:
      6.3.0 additive combination with noRollbackFor() and the depth tiebreak (D-06, D-07); before 6.3.0 a non-empty, unmatched rollbackFor silently committed instead of falling through to the default.
      Default:
      {}
    • noRollbackFor

      Class<? extends Throwable>[] noRollbackFor
      Exception types that should NOT trigger a rollback, overriding the RuntimeException/ Error default for those types.

      Combines with rollbackFor() by shallowest-inheritance-depth match, described there; on an exact-depth tie the transaction still rolls back.

      Returns:
      exception types to not rollback for
      Default:
      {}