建設(shè)獨(dú)立商城網(wǎng)站上海最新新聞
概念
在MySQL中介紹過(guò),當(dāng)同一時(shí)間出現(xiàn)一起讀寫(xiě)數(shù)據(jù)的情況,可能會(huì)導(dǎo)致最終的結(jié)果出錯(cuò),因此可以使用事務(wù)來(lái)提高隔離級(jí)別
而Spring中也可以實(shí)現(xiàn)事務(wù)
手動(dòng)添加事務(wù)
使用SpringBoot中的DataSourceTransactionManager對(duì)象可以獲取事務(wù),提交事務(wù),回滾事務(wù)
TransactionDefinition是事務(wù)的屬性,在獲取事務(wù)時(shí)需要傳入這個(gè)參數(shù)
而TransactionStatus則是在獲取事務(wù)時(shí)獲取的對(duì)象,最終回滾時(shí)使用
@RestController
@RequestMapping("url")
public class 實(shí)現(xiàn)類 {@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("url")public 相關(guān)方法() {//開(kāi)啟事務(wù)TransactionStatus transactionStatus =transactionManager.getTransaction(transactionDefinition);//處理事務(wù)//回滾事務(wù)transactionManager.rollback(transactionStatus);}
例如:
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/add")public int add(UserInfo userInfo) {if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}//開(kāi)啟事務(wù)TransactionStatus transactionStatus =transactionManager.getTransaction(transactionDefinition);int result = userService.add(userInfo);//回滾事務(wù)transactionManager.rollback(transactionStatus);return result;}
}
在頁(yè)面中,傳輸了參數(shù)username = 1, password = 1
但是在數(shù)據(jù)庫(kù)中,并沒(méi)有出現(xiàn)這行數(shù)據(jù)
自動(dòng)添加事務(wù)
使用@Transactional注解可以快速添加事務(wù)
@Transactional
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result = userService.add(userInfo);}return result;
}
如果沒(méi)有異常,那么會(huì)自動(dòng)提交事務(wù),如果有異常,事務(wù)會(huì)進(jìn)行回滾
例如:在類中添加空指針異常
如果沒(méi)有添加@Transactional那么在異常出現(xiàn)前數(shù)據(jù)已經(jīng)上傳到數(shù)據(jù)庫(kù)
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result = userService.add(userInfo);int num = 1 / 0;return result;
}
而加了@Transactional注解,就可以自動(dòng)回滾事務(wù),數(shù)據(jù)庫(kù)中就沒(méi)有這行數(shù)據(jù)了
而如果我們添加了try,catch語(yǔ)句,spring就不會(huì)感知到異常的存在了,也就不會(huì)進(jìn)行事務(wù)的回滾了
@Transactional //聲明式事務(wù)(沒(méi)有錯(cuò)誤自動(dòng)提交)
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result = userService.add(userInfo);try{int num = 1 / 0;} catch (Exception e){e.printStackTrace()}return result;
}
如果我們還想要事務(wù)進(jìn)行自動(dòng)回滾,那么可以使用throw將異常拋出,這樣spring就能再次感知到異常的存在了
@Transactional //聲明式事務(wù)(沒(méi)有錯(cuò)誤自動(dòng)提交)
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result = userService.add(userInfo);try{int num = 10 / 0;} catch (Exception e){e.printStackTrace();throw e;}return result;
}
如果不想要頁(yè)面出現(xiàn)報(bào)錯(cuò)信息,那么可以不添加throw語(yǔ)句,而是手動(dòng)回滾事務(wù)
@Transactional //聲明式事務(wù)(沒(méi)有錯(cuò)誤自動(dòng)提交)
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result = userService.add(userInfo);try{int num = 10 / 0;} catch (Exception e){e.printStackTrace();TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()}return result;
}
事務(wù)隔離級(jí)別設(shè)置
當(dāng)不同事務(wù)同時(shí)對(duì)數(shù)據(jù)庫(kù)進(jìn)行數(shù)據(jù)操作,就會(huì)出現(xiàn)問(wèn)題,因此可以針對(duì)不同的情景設(shè)置不同的隔離級(jí)別
在@Transactional注解中可以設(shè)置事務(wù)的隔離級(jí)別
代碼 | 含義 | 問(wèn)題 |
---|---|---|
Isolation.DEFAULT | 默認(rèn)級(jí)別 以數(shù)據(jù)庫(kù)隔離級(jí)別執(zhí)行 | |
Isolation.READ_UNCOMMITTED | 讀未提交 可以讀未提交數(shù)據(jù) | 存在臟讀問(wèn)題 |
Isolation.READ_COMMITED | 讀已提交 只能讀到已經(jīng)提交事務(wù) | 存在不可重復(fù)讀問(wèn)題 |
Isolation.REPEATABLE_READ | 可重復(fù)讀 | 存在幻讀問(wèn)題 |
Isolation.SERIALIZABLE | 串行化 | 性能低 |
事務(wù)傳播機(jī)制
在項(xiàng)目中會(huì)存在多個(gè)方法鏈?zhǔn)秸{(diào)用,而每一個(gè)方法都有可能存在事務(wù),因此存在不同的事物傳播機(jī)制
分別有如下幾種事物傳播級(jí)別:
代碼 | 說(shuō)明 |
---|---|
Propagation.REQUIRED | (默認(rèn))如果當(dāng)前存在事務(wù)則加入事務(wù),都則創(chuàng)建一個(gè)事務(wù) |
Propagation.SUPPORTS | 如果當(dāng)前存在事務(wù)就加入事務(wù),否則就以非事務(wù)方式運(yùn)行 |
Propagation.MANDATORY | 如果當(dāng)前存在事務(wù)就加入事務(wù),否則拋出異常 |
Propagation.REQUIRES_NEW | 不管當(dāng)前有沒(méi)有事務(wù),都創(chuàng)建并使用新事務(wù) |
Propagation.NOT_SUPPORTED | 不管當(dāng)前有沒(méi)有事務(wù),都以非事務(wù)方式運(yùn)行 |
Propagation.NEVER | 以非事務(wù)形式運(yùn)行,如果當(dāng)前存在事務(wù)就拋出異常 |
Propagation.NESTED | 如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)運(yùn)行,當(dāng)作REQUIRED |