[JAVA] I tried to verify this and that of Spring @ Transactional

@Transactional in spring is a convenient annotation that automatically rolls back when an exception occurs. However, (I) don't check the rollback in unit tests, and sometimes I'm worried whether it really works.

So I tried to verify the movement of @Transactional in various ways.

** Confirmation environment ** JDK 1.8.0 Spring Boot 2.0.5.RELEASE Doma2.0

@Transactional will not work unless you attach it to a method called directly from the DI class

Verification procedure

  1. Call method A in the service that is DI from the controller.
  2. Call method B with @Transactional from method A.
  3. Raise an exception immediately after inserting a record into the DB with method B.

verification code

controller


public class TestController {

    @Autowired
    TestService testService;

    @PostMapping("/post")
    public void postTest() {
        testService.insertMethodA();
    }
}

service


@Service
public class TestService {

    @Autowired
    ItemDao itemDao;

    public void insertMethodA() {
        insertMethodB();
    }

    @Transactional
    public void insertMethodB() {

        //Insert record ready
        Item item = new Item();
        item.setItemCd("1");
        item.setItemName("hoge");
        item.setSellFlg("0");

        //Record insertion
        itemDao.insert(item);

        //Exception occurred
        throw new RuntimeException();
    }
}

Execution result

It has been inserted without being rolled back.

mysql> select * from item;
+---------+-----------+----------+
| ITEM_CD | ITEM_NAME | SELL_FLG |
+---------+-----------+----------+
| 1       | hoge      | 0        |
+---------+-----------+----------+

Code fix

Add @Transactional to method A.


    @Transactional
    public void insertMethodA() {
        insertMethodB();
    }

    public void insertMethodB() {

        //Insert record ready
        Item item = new Item();
        item.setItemCd("1");
        item.setItemName("hoge");
        item.setSellFlg("0");

        //Record insertion
        itemDao.insert(item);

        //Exception occurred
        throw new RuntimeException();
    }

Execution result

mysql> select * from item;
Empty set (0.00 sec)

It was rolled back. Yes ok ~.

If you add @Transactinal to a class, it will be attached to all methods in the class.

Adding @Transactional to a class implicitly adds @Transactional to all methods in the class.

Verification procedure

  1. Add @Transactional to the service class
  2. Call method A from the controller without @Transactional

verification code

The controller is the same as the one above (call method A from the service class).

service


@Service
@Transactional
public class TestService {

    @Autowired
    ItemDao itemDao;

    public void insertMethodA() {
        insertMethodB();
    }

    public void insertMethodB() {

        //Insert record ready
        Item item = new Item();
        item.setItemCd("1");
        item.setItemName("hoge");
        item.setSellFlg("0");

        //Record insertion
        itemDao.insert(item);

        //Exception occurred
        throw new RuntimeException();
    }
}

Execution result

It has been rolled back. Calling method B from the controller was also rolled back.

mysql> select * from item;
Empty set (0.00 sec)

An exception is thrown when trying to manipulate a record with the readonly attribute set to true

@Transactinal has an attribute called readonly, and if you set it to true, it will throw an exception when the record operation process runs.

verification code

The controller used above.

service


@Service
public class TestService {

    @Autowired
    ItemDao itemDao;

    @Transactional(readOnly = true)
    public void insertMethodA() {
        insertMethodB();
    }

    public void insertMethodB() {

        //Insert record ready
        Item item = new Item();
        item.setItemCd("1");
        item.setItemName("hoge");
        item.setSellFlg("0");

        //Record insertion
        itemDao.insert(item);
    }
}

Execution result

java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

It is better to set it as an acquisition service that is not supposed to be updated.

You can set a rollback target occurrence exception with the rollbackFor attribute

By default, the rolledback exceptions are RuntimeException and its subclasses. The rollbackFor attribute changes the exception class set in the value and its subclass to the raised exception to be rolled back.

verification code

Raises a ʻExceptionexception in the default state withoutrollbackFor`.

@Service
public class TestService {

    @Autowired
    ItemDao itemDao;

    @Transactional
    public void insertMethodA() throws Exception{
        insertMethodB();
    }

    public void insertMethodB() throws Exception{

        //Insert record ready
        Item item = new Item();
        item.setItemCd("1");
        item.setItemName("hoge");
        item.setSellFlg("0");

        //Record insertion
        itemDao.insert(item);

        //Exception occurred
        throw new Exception();
    }
}

Execution result

It will not be rolled back.

mysql> select * from item;
+---------+-----------+----------+
| ITEM_CD | ITEM_NAME | SELL_FLG |
+---------+-----------+----------+
| 1       | hoge      | 0        |
+---------+-----------+----------+

Code change

Set rollbackFor to ʻException.class`.


    @Transactional(rollbackFor = Exception.class)
    public void insertMethodA() throws Exception{
        insertMethodB();
    }

Execution result (after change)

It is now rolled back.

mysql> select * from item;
Empty set (0.00 sec)

@Transactional attached to the outer class does not apply to the inner class

Since @Transactional attached to the outer class does not apply to the inner class, it is necessary to attach it to the inner class separately. Be careful when hierarchizing test classes using inner classes in Junit.

verification code

controller

public class TestController {

    @Autowired
    TestService testService;

    @Autowired
    TestService.TestInnerService testInnerService;

    @PostMapping("/post")
    public void postTest() throws Exception{
       testInnerService.insertMethodA();
    }
}

service


@Service
@Transactional
public class TestService {

    @Autowired
    ItemDao itemDao;

    @Service
    public class TestInnerService{

        public void insertMethodA() throws Exception{
            insertMethodB();
        }

        public void insertMethodB(){

            //Insert record ready
            Item item = new Item();
            item.setItemCd("1");
            item.setItemName("hoge");
            item.setSellFlg("0");

            //Record insertion
            itemDao.insert(item);

            //Exception occurred
            throw new RuntimeException();
        }
    }
}

Execution result

Not rolled back.

mysql> select * from item;
+---------+-----------+----------+
| ITEM_CD | ITEM_NAME | SELL_FLG |
+---------+-----------+----------+
| 1       | hoge      | 0        |
+---------+-----------+----------+

Code change

Add @Transactinal to the inner class.


    @Service
    @Transactional
    public class TestInnerService{

Execution result (after change)

It was rolled back.

mysql> select * from item;
Empty set (0.00 sec)

Recommended Posts

I tried to verify this and that of Spring @ Transactional
I tried to link JavaFX and Spring Framework.
I tried to verify yum-cron
I tried to reduce the capacity of Spring Boot
I tried to summarize the basics of kotlin and java
This and that of the JDK
This and that of Core Graphics
This and that of exclusive control
I tried to summarize the methods of Java String and StringBuilder
This and that of Swift corner Radius
I tried Spring.
I want to display images with REST Controller of Java and Spring!
I tried to summarize the key points of gRPC design and development
I introduced OpenAPI (Swagger) to Spring Boot (gradle) and tried various settings
I tried to measure and compare the speed of GraalVM with JMH
I tried to verify AdoptOpenJDK 11 (11.0.2) with Docker image
I tried to link grafana and postgres [docker-compose]
I tried to chew C # (basic of encapsulation)
This and that of conditional branching of rails development
I tried to verify whether it would be fun to combine "programming" and "hobbies".
I tried to clone a web application full of bugs with Spring Boot
I tried to convert JavaBean and XML with Jackson formatter XML in this era
[I tried] Spring tutorial
I tried Spring Batch
I tried to implement file upload with Spring MVC
I tried to read and output CSV with Outsystems
I tried to summarize the state transition of docker
I tried to integrate AWS I oT button and Slack
I tried to get started with Spring Data JPA
I tried to make FizzBuzz that is uselessly flexible
I tried to summarize various link_to used this time
I tried to chew C # (reading and writing files)
I tried to create a shopping site administrator function / screen with Java and Spring
I tried using Wercker to create and publish a Docker image that launches GlassFish 5.
Sazae-san's rock-paper-scissors I tried to verify the theoretical value and the measured value of the probability of the same hand 5 consecutive times with Ruby
Since the argument of link_to is nil (null) and an unexpected link was generated, I tried to verify it
Docker settings and this and that
I tried to collect and solve Ruby's "class" related problems.
[Swift] I tried to implement the function of the vending machine
I tried Spring State machine
I tried to develop a web application from a month and a half of programming learning history
I tried JAX-RS and made a note of the procedure
I tried to make Java Optional and guard clause coexist
I tried to summarize the basic grammar of Ruby briefly
I tried to summarize personally useful apps and development tools (development tools)
I tried to build the environment of WSL2 + Docker + VSCode
I tried to make an app that allows you to post and chat by genre ~ App overview ~
[For Swift beginners] I tried to summarize the messy layout cycle of ViewController and View
I tried to summarize personally useful apps and development tools (Apps)
[Rails] strftime this and that
I tried to make a client of RESAS-API in Java
Digital certificate this and that
[Rails] I tried to implement "Like function" using rails and js
I tried to get started with Swagger using Spring Boot
Base64 encoder this and that
I tried to express the result of before and after of Date class with a number line
I tried printing a form with Spring MVC and JasperReports 1/3 (JasperReports settings)
I tried to summarize the words that I often see in docker-compose.yml
I tried to implement Ajax processing of like function in Rails
I want to understand the flow of Spring processing request parameters
I tried to integrate Docker and Maven / Netbean nicely using Jib