由于md文档部分hugo插件语法不兼容,建议访问作者网站查阅文章:wlizhi.cc
spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework 。
链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。
1 基础概念
spring声明式事务的传播行为,严格来说是spring特有的,数据库本身并没有这个概念。本文会列举出spring事务的七种传播行为,并以代码案例的方式展示不同的传播行为的表现是怎样的。
1.1 什么是事务传播行为
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法时事务如何传播。
伪代码:
public class A{
@AutoWired
private B b;
public void a(){
b.b();
// 业务代码...
}
}
public class B{
@AutoWired
private A a;
@Transaction(Propagation=XXX)
public void b(){
// 业务代码...
}
}
上面代码中 a() 嵌套了 b() 。b() 的事务传播行为由 @Transaction(Propagation=XXX) 决定。注意,a() 并没有开启事务。某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。
1.2 spring声明式事务中七种传播行为
传播类型 | 说明 |
---|---|
REQUIRED | 如果当前没有事务,就新建一个事务;如果当前存在事务,就加入到当前事务。 |
SUPPORTS | 如果当前没有事务,就以非事务方式运行;如果当前存在事务,就加入到当前事务。 |
MANDATORY | 如果当前没有事务,就抛出异常;如果当前存在事务,就加入到当前事务。 |
REQUIRES_NEW | 总是在新建事务中运行。如果当前已存在事务,就挂起当前事务。 |
NOT_SUPPORTED | 以非事务方式运行;如果当前存在事务,挂起当前事务。 |
NEVER | 以非事务的方式运行。如果当前存在事务,就抛出异常。 |
NESTED | 如果当前存在事务,则在嵌套事务内运行;如果当前不存在事务,则新建一个事务(这时与 REQUIRED 类似)。 |
2 基础代码准备
文中代码以 Service 和 Dao 两层展示。使用测试用例调用 Service 的方式,框架采用 spring + mybatis ,数据库 mysql。dao 层及实体类代码使用插件生成。
自动代理激活及扫描包配置类 AspectAnoForTransactionConfiguration :
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {
"top.wlz922.service"})
public class AspectAnoForTransactionConfiguration {
}
事务配置类:
@Configuration
@MapperScan(basePackages = "top.wlz922.dao")
@EnableTransactionManagement
public class GlobalTransactionConfig {
@Autowired
ApplicationContext context;
@Bean
public DataSourceTransactionManager getTransactionManager(@Autowired DataSource ds){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(ds);
return manager;
}
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource ds) throws IOException {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);
ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(context);
Resource[] resources = resolver.getResources("classpath:sqlmaps/*.xml");
fb.setMapperLocations(resources);
return fb;
}
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setUsername("root");
ds.setPassword("123456");
ds.setUrl("jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai");
ds.setDriverClassName("com.mysql.jdbc.Driver");
return ds;
}
}
实体类 PropagationUser :
@Data
@NoArgsConstructor
public class PropagationUser implements Serializable {
private Long id;
private String name;
private static final long serialVersionUID = 1L;
public PropagationUser(String name) {
this.name = name;
}
}
PropagationUserService (其实现类的事务注解配置与方法名一致):
public interface PropagationUserService {
void addRequired(PropagationUser user);
void addRequiredException(PropagationUser user);
void addRequiredNew(PropagationUser user);
void addRequiredNewException(PropagationUser user);
void addSupports(PropagationUser user);
void addSupportsException(PropagationUser user);
void addNotSupported(PropagationUser user);
void addNotSupportedException(PropagationUser user);
void addMadatory(PropagationUser user);
void addMadatoryException(PropagationUser user);
void addNever(PropagationUser user);
void addNeverException(PropagationUser user);
void addNestead(PropagationUser user);
void addNesteadException(PropagationUser user);
}
PropagationUserDao :
public interface PropagationUserDao {
int insertSelective(PropagationUser record);
// 省略...
}
测试类:
public class TransactionPropagationServiceTest {
private TransactionPropagationService service;
@Before
public void before() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
GlobalTransactionConfig.class, AspectAnoForTransactionConfiguration.class);
service = context.getBean(TransactionPropagationService.