> Markdown version of [Declarative Transactions](https://vaadin.com/docs/next/building-apps/forms-data/consistency/transactions/declarative). Section index: [llms.txt](https://vaadin.com/docs/next/building-apps/llms.txt)

# Declarative Transactions

The easiest way to manage transactions in a Spring application is by using the `@Transactional` annotation. You can place it directly on your class, or on individual methods. Don’t use the annotation on interfaces, except for [Spring Data repositories](https://vaadin.com/docs/next/building-apps/forms-data/persistence/spring-data.md).

The following example instructs Spring to run all methods of the application service inside a transaction:

```java
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class MyApplicationService {
    ...
}
```

If you use `@Transactional` on both the class and individual methods, the method level annotation takes precedence. The following example tells Spring to use the `REQUIRED` (the default) propagation level for all application service methods, except for `myMethod()` that should use `SUPPORTS`:

```java
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class MyApplicationService {
    ...

    @Transactional(propagation = Propagation.SUPPORTS)
    public void myMethod() {
        ...
    }
}
```

> **Important:** In earlier versions of Spring, you could only use `@Transactional` on `public` methods. As of Spring version 6.0, you can also use it on `protected` and package-visible methods if you’re using class-based proxies. For interface-based proxies, the methods must always be `public`, and defined in the proxied interface. Always make your transactional methods `public`.

For more information about declarative transaction management, see the [Spring Documentation](https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative.html).

## <a id="committing"></a>Committing

You don’t have to do anything special to commit a transaction. Spring commits the transaction after the method returns, unless it has been marked for rollback. This is described in the following section.

## <a id="rolling-back"></a>Rolling Back

You mark the transaction for rollback by throwing an unchecked exception. The following example causes Spring to rollback the transaction if validation fails:

```java
@Service
public class MyApplicationService {
    ...

    @Transactional
    public void myMethod(MyInput input) {
        if (!isValid(input)) {
            throw new IllegalArgumentException("Bad input");
        }
        ...
    }
}
```

A checked exception doesn’t cause the transaction to rollback by default. You can override this, though, by using the `rollbackFor` annotation attribute. The following example instructs Spring to rollback the transaction if `myMethod` throws a `MyCheckedException`:

```java
@Service
public class MyApplicationService {
    ...

    @Transactional(rollbackFor = MyCheckedException.class)
    public void myMethod() throws MyCheckedException {
        ...
    }
}
```

## <a id="read-only-transactions"></a>Read-Only Transactions

If a method only reads data, you can mark its transaction as read-only by using the `readOnly` annotation attribute. Spring passes this on to your persistence technology and database driver, which can use it to optimize the transaction. Don’t rely on it to prevent writes, though. Whether it does depends on the persistence technology and the database.

The following example makes all methods of the application service read-only by default, and overrides this for `save()`, which writes data:

```java
@Service
@Transactional(readOnly = true)
public class MyApplicationService {

    public Optional<MyData> findById(long id) {
        ...
    }

    @Transactional
    public void save(MyData data) {
        ...
    }
}
```

Spring ignores the read-only setting when the method joins a transaction that has already been started, for example by another service.

## <a id="isolation-level"></a>Isolation Level

You can use the `@Transactional` annotation to specify the isolation level of the transaction. By default, it uses the database implementation’s own default transaction level. The following example has Spring execute `myMethod()` using the *repeatable read* isolation level, so that data the method has already read doesn’t change before the transaction completes:

```java
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyApplicationService {
    ...

    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void myMethod() {
        ...
    }
}
```

Raising the isolation level makes it more likely that the database blocks concurrent transactions, or rolls them back. For more information about the isolation levels, see the [Transactions](https://vaadin.com/docs/next/building-apps/forms-data/consistency/transactions.md#transaction-isolation) documentation page.

## <a id="caveats"></a>Caveats

When working with declarative transactions, it’s important to remember that the annotations themselves don’t manage any transactions. They’re merely instructions for how Spring should manage the transactions.

During application startup, Spring detects the `@Transactional` annotation and turns the service into a proxy. When a client calls the proxy, the call is routed through a *method interceptor*. The interceptor starts the transaction, calls the actual method, and then commits the transaction when the method returns, as illustrated in this diagram:

[Image: Diagram of Client Calling a Service through a Proxy]

The `@Transactional` annotation is ignored if a service calls itself. This happens because the call doesn’t go via the proxy, as illustrated in the following diagram:

[Image: Diagram of a Service Calling Itself]

In the following example, `myFirstMethod()` executes inside its own transaction if a client calls it, directly. However, if a client calls `mySecondMethod()`, `myFirstMethod()` executes inside the transaction of `mySecondMethod()` despite being annotated differently:

```java
@Service
public class MyApplicationService {

    @Transactional(propagation = REQUIRES_NEW)
    public void myFirstMethod() {
    }

    @Transactional
    public void mySecondMethod() {
        // myFirstMethod() will participate in the transaction of mySecondMethod(),
        // even though it has been annotated as REQUIRES_NEW.
        myFirstMethod();
    }
}
```

You can fix this by managing the transactions, [programmatically](https://vaadin.com/docs/next/building-apps/forms-data/consistency/transactions/programmatic.md).
