开发中有多个事务方法嵌套调用时,就涉及到了 Spring 的事务传播机制。
下面梳理一下 7 种传播机制的含义,做一些测试以加深理解,也方便开发中灵活使用。
1. Spring事务传播类型枚举Propagation
- REQUIRED: 没有事务就开启,有事务就加入,不指定的话默认为该类型
- SUPPORTS: 有事务就加入,没有就无事务运行
- MANDATORY: 加入当前事务,如果不存在则抛出异常
- REQUIRES_NEW: 没有就开启,有了挂起原来的,开启新的
- NOT_SUPPORTED: 有了挂起,没有就无事务运行
- NEVER: 以非事务方式执行,如果存在事务则抛出异常
- NESTED: 如果当前事务存在,则在嵌套事务中执行,否则行为类似于REQUIRED
大多数情况下,用默认的 REQUIRED 就足够了,其它的用的较少。
2. 传播机制测试
2.1 测试准备
Spring Boot,JPA,Lombok,MySQL
建表:
CREATE TABLE `region` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
建实体:
package com.jiangxb.transactional;
import lombok.Builder;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 地区实体
*
* @author: jiangxiangbo
* @date: 2021/6/16
*/
@Entity
@Table(name = "region")
@Data
@Builder
public class Region {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;
}
repository:
package com.jiangxb.transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* region repository
*
* @author: jiangxiangbo
* @date: 2021/6/16
*/
@Repository
public interface RegionRepository extends JpaRepository<Region, Long> {
}
service:
package com.jiangxb.transactional;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 地区service
*
* @author: jiangxiangbo
* @date: 2021/6/16
*/
@Service
public class RegionService {
@Autowired
private RegionRepository regionRepository;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertRegion() {
regionRepository.save(Region.builder().name("A").build());
// 调用同个类中的事务方法需要使用代理对象调用,否则事务无效
RegionService regionService = (RegionService)AopContext.currentProxy();
regionService.insertRegionB();
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertRegionB() {
regionRepository.save(Region.builder().name("B").build());
// throw new RuntimeException();
}
}
controller: