@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@Transactional
from method A.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();
}
}
It has been inserted without being rolled back.
mysql> select * from item;
+---------+-----------+----------+
| ITEM_CD | ITEM_NAME | SELL_FLG |
+---------+-----------+----------+
| 1 | hoge | 0 |
+---------+-----------+----------+
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 ~.
@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.
@Transactional
to the service class@Transactional
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();
}
}
It has been rolled back. Calling method B from the controller was also rolled back.
mysql> select * from item;
Empty set (0.00 sec)
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.
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);
}
}
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.
rollbackFor
attributeBy 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.
Raises a ʻExceptionexception in the default state without
rollbackFor`.
@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();
}
}
It will not be rolled back.
mysql> select * from item;
+---------+-----------+----------+
| ITEM_CD | ITEM_NAME | SELL_FLG |
+---------+-----------+----------+
| 1 | hoge | 0 |
+---------+-----------+----------+
Set rollbackFor
to ʻException.class`.
@Transactional(rollbackFor = Exception.class)
public void insertMethodA() throws Exception{
insertMethodB();
}
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 classSince @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.
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();
}
}
}
Not rolled back.
mysql> select * from item;
+---------+-----------+----------+
| ITEM_CD | ITEM_NAME | SELL_FLG |
+---------+-----------+----------+
| 1 | hoge | 0 |
+---------+-----------+----------+
Add @Transactinal
to the inner class.
@Service
@Transactional
public class TestInnerService{
It was rolled back.
mysql> select * from item;
Empty set (0.00 sec)
Recommended Posts