Spring聲明式事務(wù)操作簡單,我們平常開發(fā)過程中,只需要在需要事務(wù)控制的方法上面加上@Transactional注解就可以綁定事務(wù)控制。但是其中的參數(shù)配置今天給大家捋一捋,并且有個AOP的神坑需要大家注意。
| 傳播屬性 | 特點 |
|---|---|
| REQUIRED | 默認的傳播屬性,表示如果當(dāng)前環(huán)境存在事務(wù)就保持此事務(wù)執(zhí)行,否則新開一個事務(wù)并且在新事務(wù)中執(zhí)行 |
| REQUIRES_NEW | 表示不管當(dāng)前環(huán)境是否存在事務(wù),都新建一個事務(wù)并在新事務(wù)中執(zhí)行,將原有事務(wù)進行掛起 |
| SUPPORTS | 表示如果當(dāng)前環(huán)境存在事務(wù),就在此事務(wù)中執(zhí)行,否則不以事務(wù)方式執(zhí)行 |
| NOT_SUPPORTED | 表示此方法不進行事務(wù)控制,如果當(dāng)前環(huán)境存在事務(wù),則掛起 |
| MANDATORY | 表示此方法必須在一個事務(wù)中進行,如果當(dāng)前環(huán)境沒有事務(wù)則拋出異常 |
| NEVER | 表示此方法運行不能有事務(wù)控制,一旦有事務(wù)傳播至此就拋出異常 |
| NESTED | 表示如果事務(wù)存在,則運行在一個嵌套的事務(wù)中,如果沒有事務(wù),則按REQUIRED屬性執(zhí)行 |
常用的屬性一般是REQUIRED和REQUIRES_NEW這兩個。
下面我們用代碼來驗證一下這常見的兩個屬性:
1.開啟事務(wù)支持 @EnableTransactionManagement
/**
* @author wangzhi
*/
@SpringBootApplication
@EnableTransactionManagement
public class DemoApplication {
public static void main(String[] args) {
new SpringApplication(DemoApplication.class).run(args);
}
}
2.實體類和數(shù)據(jù)庫
/**
* @author wangzhi
*/
@Data
@TableName("course")
public class CourseEntity {
@TableId(type = IdType.AUTO)
private Integer id;
private String courseName;
private BigDecimal price;
}
在這里插入圖片描述
3.service層業(yè)務(wù)代碼
我們先檢驗一下REQUIRED
/**
* @author wangzhi
*/
@Service
public class TransactionService {
@Autowired
private CourseMapper courseMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void save() {
CourseEntity course = new CourseEntity();
course.setCourseName("語文");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
//故意制造異常
System.out.println(1/0);
}
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void saveInit() {
CourseEntity course = new CourseEntity();
course.setCourseName("數(shù)學(xué)");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
//調(diào)用save方法
try{
save();
}catch (Exception e){
e.printStackTrace();
}
}
}
4.測試一下,你們猜數(shù)據(jù)庫有幾條記錄?
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = DemoApplication.class)
class DemoApplicationTests {
@Autowired
TransactionService transactionService;
@Test
public void transactionTest() throws Exception {
transactionService.saveInit();
}
}
5.看結(jié)果

在這里插入圖片描述
4.別急,再看看REQUIRES_NEW
/**
* @author wangzhi
*/
@Service
public class TransactionService {
@Autowired
private CourseMapper courseMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void save() {
CourseEntity course = new CourseEntity();
course.setCourseName("語文");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
//故意制造異常
System.out.println(1/0);
}
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void saveInit() {
CourseEntity course = new CourseEntity();
course.setCourseName("數(shù)學(xué)");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
//調(diào)用save方法
try {
save();
}catch (Exception e){
e.printStackTrace();
}
}
}

在這里插入圖片描述
5.之所以出現(xiàn)如此情況,并不是事務(wù)傳播有問題,而是動態(tài)代理造成的。在同一個類里面互相調(diào)用事務(wù)方法,切面切的是發(fā)起事務(wù)的方法,而內(nèi)部不管調(diào)用的什么事務(wù)方法都會默認為this當(dāng)前對象去調(diào)動普通方法,這些事務(wù)注解說白了就是不管用。事務(wù)是根據(jù)動態(tài)代理生成的動態(tài)對象去執(zhí)行事務(wù)的控制,所以在同類方法內(nèi)部調(diào)用其他事務(wù)方法必須要獲取其對應(yīng)的代理對象去調(diào)用才生效,否則必須放在不同的類里面。結(jié)局方法:
6.引入AOP
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
7.@EnableAspectJAutoProxy(exposeProxy = true)
/**
* @author wangzhi
*/
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.example.mapper")
@EnableAspectJAutoProxy(exposeProxy = true)
public class DemoApplication {
public static void main(String[] args) {
new SpringApplication(DemoApplication.class).run(args);
}
}
8.改造一下service
/**
* @author wangzhi
*/
@Service
public class TransactionService {
@Autowired
private CourseMapper courseMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void save() {
CourseEntity course = new CourseEntity();
course.setCourseName("語文");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
//故意制造異常
System.out.println(1/0);
}
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void saveInit() {
CourseEntity course = new CourseEntity();
course.setCourseName("數(shù)學(xué)");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
//調(diào)用save方法
try {
TransactionService proxy = (TransactionService)AopContext.currentProxy();
proxy.save();
}catch (Exception e){
e.printStackTrace();
}
}
}
看看結(jié)果:

在這里插入圖片描述
這樣的結(jié)果就對了!所以我們在開發(fā)過程中遇到A方法調(diào)用B方法,如果AB方法都在同一個類里面,想要B方法的事務(wù)生效必須用代理方式執(zhí)行。當(dāng)然AB方法不在同一個類里面可以生效,因為動態(tài)代理是運行時才構(gòu)造的代理對象。而且,類似的技術(shù)點不僅僅出現(xiàn)在事務(wù)這里,比如@Async注解同樣還是這樣,同一個類里面調(diào)用依然不是異步執(zhí)行,當(dāng)涉及到動態(tài)代理相關(guān)都要注意此點。