Spring Boot | Database Access | Using Declarative Transactions

Using Declarative Transactions

  • Applying @Transactional to a method makes the method invocation a transaction boundary.
  • If a RuntimeException or subclass is thrown within the transaction boundary, the transaction is rolled back.
  • An Exception or subclass does not trigger rollback by default.
  • To roll back when an Exception occurs, configure @Transactional(rollbackFor = Exception.class).

Writing the Code

src/main/java/sample/springboot/jpa/MyService.java

package sample.springboot.jpa;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class MyService {

    @Autowired
    private HogeRepository repository;

    public void save(String value) {
        Hoge hoge = new Hoge(value);
        this.repository.save(hoge);
    }

    public void saveAndThrowRuntimeException(String value) {
        this.save(value);
        throw new RuntimeException("test");
    }

    @Transactional
    public void saveAndThrowRuntimeExceptionWithTransactional(String value) {
        this.saveAndThrowRuntimeException(value);
    }

    @Transactional
    public void saveAndThrowExceptionWithTransactional(String value) throws Exception {
        this.save(value);
        throw new Exception("test");
    }

    public void show() {
        this.repository.findAll().forEach(System.out::println);
    }
}

src/main/java/sample/springboot/jpa/Main.java

package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.MyService;

@SpringBootApplication
public class Main {

    public static void main(String[] args) throws Exception {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            MyService s = ctx.getBean(MyService.class);

            s.save("normal");

            try {
                s.saveAndThrowRuntimeException("runtime exception without @Transactional");
            } catch (Exception e) {}

            try {
                s.saveAndThrowRuntimeExceptionWithTransactional("runtime exception with @Transactional");
            } catch (Exception e) {}

            try {
                s.saveAndThrowExceptionWithTransactional("exception with @Transactional");
            } catch (Exception e) {}

            s.show();
        }
    }
}

Result

Hoge [id=1, value=normal]
Hoge [id=2, value=runtime exception without @Transactional]
Hoge [id=4, value=exception with @Transactional]