spring事务之REQUIRED
准备
1.表
CREATE TABLE `t_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(64) NOT NULL COMMENT '用户名',
`password` varchar(64) DEFAULT NULL COMMENT '密码',
`mobile` varchar(64) NOT NULL COMMENT '手机号码',
`name` varchar(64) DEFAULT NULL COMMENT '真实姓名',
`is_delete` tinyint(1) DEFAULT NULL COMMENT 'is_delete',
`gmt_modified` datetime DEFAULT NULL COMMENT 'gmt_modified',
`gmt_create` datetime DEFAULT NULL COMMENT 'gmt_create',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `t_game` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`created` date DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4;
2.实体类
@Data
public class UserDo {
private Long id;
private String username;
private String password;
private String mobile;
private String name;
private Boolean isDelete;
private Date gmtModified;
private Date gmtCreate;
}
@Data
public class GameDo {
private Integer id;
private Long userId;
private Date created;
private String name;
}
单表
这些方法都在UserManager.java
中:
1.粟子一
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
UserDo userDo = new UserDo();
userDo.setName("user");
userDo.setUsername("user-aa");
userDo.setMobile("17370998767");
userDao.insert(userDo);
if (1 == 1) {
throw new RuntimeException("异常");
}
}
# junit
@Test
public void userMethodBTest(){
userManager.methodB();
}
测试结果:事务回滚,表中未插入数据
2.粟子二
@Transactional(propagation = Propagation.REQUIRED)
public void methodD(){
try {
UserDo userDo = new UserDo();
userDo.setName("user");
userDo.setUsername("user-aa");
userDo.setMobile("17370998767");
userDao.insert(userDo);
if (1 == 1) {
throw new RuntimeException("异常");
}
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
}
@Test
public void userMethodDTest(){
userManager.methodD();
}
测试结果:事务回滚,表中未插入数据,相当于粟子1
3.粟子三
@Transactional(propagation = Propagation.REQUIRED)
public void methodC(){
try {
UserDo userDo = new UserDo();
userDo.setName("user");
userDo.setUsername("user-aa");
userDo.setMobile("17370998767");
userDao.insert(userDo);
if (1 == 1) {
throw new RuntimeException("异常");
}
} catch (RuntimeException e) {
e.printStackTrace();
}
}
# junit
@Test
public void userMethodCTest(){
userManager.methodC();
}
测试结果:异常被处理,事务不回滚,表中插入数据
多表
在GameManager.java
中:
@Transactional(propagation = Propagation.REQUIRED)
public void meanB(){
GameDo gameDo = new GameDo();
gameDo.setName("测试");
gameDo.setUserId(1L);
gameDao.insert(gameDo);
if (1==1){
throw new RuntimeException("异常");
}
}
下面都是在UserManager.java
中,调用meanB
方法:
1.粟子一:
@Transactional(propagation = Propagation.REQUIRED)
public void methodE() {
UserDo userDo = new UserDo();
userDo.setName("user");
userDo.setUsername("user-aa");
userDo.setMobile("17370998767");
userDao.insert(userDo);
gameManager.meanB();
}
# junit
@Test
public void userMethodETest(){
userManager.methodE();
}
测试结果:事务回滚,表中未插入数据
2.粟子二
@Transactional(propagation = Propagation.REQUIRED)
public void methodG() {
UserDo userDo = new UserDo();
userDo.setName("user");
userDo.setUsername("user-aa");
userDo.setMobile("17370998767");
userDao.insert(userDo);
try {
gameManager.meanB();
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
}
# junit
@Test
public void userMethodGTest(){
userManager.methodG();
}
测试结果:事务回滚,表中未插入数据
3.粟子三:
@Transactional(propagation = Propagation.REQUIRED)
public void methodF() {
UserDo userDo = new UserDo();
userDo.setName("user");
userDo.setUsername("user-aa");
userDo.setMobile("17370998767");
userDao.insert(userDo);
try {
gameManager.meanB();
} catch (Exception e) {
e.printStackTrace();
}
}
# junit
@Test
public void userMethodFTest(){
userManager.methodF();
}
测试结果:事务回滚,表中未插入数据
这很奇怪呀,已经try catch了,讲道理是user表会插入数据,而game表中数据回滚,为什么呢?这就是spring事务传播级别Propagation.REQUIRED
了,使用了Propagation.REQUIRED
,如果本来有事务,则加入该事务,如果没有事务,则创建新的事务,本质上使用的是同一个连接connection。看异常有:
org.springframework.transaction.UnexpectedRollbackException:
Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:485)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
这个异常的意思是,由于内层抛出异常,已经将Transaction的状态标记为rollback-only
,所以外层事务也只好回滚,但这个特性是基于同一个事务的,如果是嵌套事务就不一定了