经过我们上面一个章节的初步了解,我们已经知道了Spring事务的基本配置,今天我们一起接着讨论一下spring事务的传播
Spring是用枚举来表示事务传播行为的,
package org.springframework.transaction.annotation;
import org.springframework.transaction.TransactionDefinition;
public enum Propagation {
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
PPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
NEVER(TransactionDefinition.PROPAGATION_NEVER),
NESTED(TransactionDefinition.PROPAGATION_NESTED);
private final int value;
Propagation(int value) { this.value = value; }
public int value() { return this.value; }
}
Propagation.REQUIRED | 代表当前方法支持当前的事务,且与调用者处于同一事务上下文中,回滚统一回滚(如果当前方法是被其他方法调用的时候,且调用者本身即有事务),如果没有事务,则自己新建事务, |
Propagation.SUPPORTS | 代表当前方法支持当前的事务,且与调用者处于同一事务上下文中,回滚统一回滚(如果当前方法是被其他方法调用的时候,且调用者本身即有事务),如果没有事务,则该方法在非事务的上下文中执行 |
Propagation.MANDATORY | 代表当前方法支持当前的事务,且与调用者处于同一事务上下文中,回滚统一回滚(如果当前方法是被其他方法调用的时候,且调用者本身即有事务),如果没有事务,则抛出异常 |
Propagation.REQUIRES_NEW | 创建一个新的事务上下文,如果当前方法的调用者已经有了事务,则挂起调用者的事务,这两个事务不处于同一上下文,如果各自发生异常,各自回滚 |
Propagation.NOT_SUPPORTED | 该方法以非事务的状态执行,如果调用该方法的调用者有事务则先挂起调用者的事务 |
Propagation.NEVER | 该方法以非事务的状态执行,如果调用者存在事务,则抛出异常 |
Propagation.NESTED | 如果当前上下文中存在事务,则以嵌套事务执行该方法,也就说,这部分方法是外部方法的一部分,调用者回滚,则该方法回滚,但如果该方法自己发生异常,则自己回滚,不会影响外部事务,如果不存在事务,则与PROPAGATION_REQUIRED一样,(其实这是数据库带有保存点的事务的典型体现,举例来说:旅游行业来说,游客从上海飞巴厘岛,需要从香港进行转机,那么上海~香港就是一个方法且是一个事务,香港~巴厘岛就是嵌入的方法,如果香港~巴厘岛航班取消了,无需回滚上海~香港的,这样代价太大,因为游客已经到了香港,只需修改香港到巴厘岛的航班就可以了,如果上海~香港的飞机取消了,则需要全部事务回滚,其实香港到巴厘岛的航班没有问题) |
好了,介绍了基本概念,我们还是实战一下spring的传播行为吧
我们接着上篇博客的业务场景:
需求①:保存用户基本信息即可,如果保存用户的详细信息发生异常不需要全部回滚,我们修改代码
UserDetailServiceImpl.java
UserServiceImpl.java
测试类:
package org.study.spring.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.study.spring.transaction.entity.Profession;
import org.study.spring.transaction.entity.User;
import org.study.spring.transaction.entity.UserDetail;
import org.study.spring.transaction.service.UserService;
public class SpringTransactionTest {
@Test
public void test1() throws IllegalAccessException{
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-transaction.xml");
UserService userBussinessServiceImpl = applicationContext.getBean("userServiceImpl",UserService.class);
userBussinessServiceImpl.saveUserAllInfo(new User("Ted",26), new UserDetail("Ted@163.com", "重庆高级公寓", "重庆大学"), new Profession("war3解说","电子竞技"));
}
}
运行测试类,数据库的结果是:
说明我们的设置成功了
②保存用户详细信息的时候,必须是在事务的上下文中执行,否则保存用户详细信息失败
修改代码UserServiceImpl.java去掉事务
UserServiceImpl.java
运行测试类
失败了,提示没有检测到事务上下文
我们再看看数据库
我们可以看到用户详细信息没有保存成功,虽然我们在保存用户详细信息的时候,代码层面并没有发生任何异常,但依旧没有保存成功
③新来的实习生在写保存profession的模块时,没有分清出not_support和never的区别,实习生是用never去管理,他的业务逻辑是觉得保存职业信息应该与主业务逻辑无关
代码修改如下:
ProfessionServiceImpl.java
UserServiceImpl.java
运行测试类
提示当前方法不应该在有事务的上下文中执行
我们修改一下ProfessionServiceImpl
顺便我们修改一下测试类的测试数据
package org.study.spring.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.study.spring.transaction.entity.Profession;
import org.study.spring.transaction.entity.User;
import org.study.spring.transaction.entity.UserDetail;
import org.study.spring.transaction.service.UserService;
public class SpringTransactionTest {
@Test
public void test1() throws IllegalAccessException{
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-transaction.xml");
UserService userBussinessServiceImpl = applicationContext.getBean("userServiceImpl",UserService.class);
userBussinessServiceImpl.saveUserAllInfo(new User("Miss",26), new UserDetail("Miss@163.com", "上海浦东区", "上海大学"), new Profession("LOL解说","电子竞技"));
}
}
运行测试类,显示成功,数据库信息:
这样就算正确的写法了
好了,其他的用法大家自己去尝试一下,根据自己真实的业务场景,spring的事务传播行为在开发中占据了很重要的地位~