日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

每日一博 - 常见的Spring事务失效事务不回滚案例集锦

發(fā)布時間:2025/3/21 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 每日一博 - 常见的Spring事务失效事务不回滚案例集锦 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 事務(wù)不生效
    • 方法內(nèi)部調(diào)用
      • 修復(fù)方法一 : 【新加一個Service方法】
      • 修復(fù)方法二:【在該Service類中注入自己】
      • 修復(fù)方法三:【通過AopContent類】<---- 推薦
    • 訪問權(quán)限問題
    • 方法用final修飾
    • 未被spring管理
    • 多線程調(diào)用
    • 表不支持事務(wù)
    • 未開啟事務(wù)
  • 事務(wù)不回滾
    • 錯誤的傳播特性
    • 自己吞了異常
    • 手動拋了別的異常
    • 自定義了回滾異常
    • 嵌套事務(wù)回滾多了
  • 其他常見問題
    • 編程式事務(wù)
    • 大事務(wù)問題
  • 小結(jié)


事務(wù)不生效

方法內(nèi)部調(diào)用

有時候我們需要在某個Service類的某個方法中,調(diào)用另外一個事務(wù)方法

@Service public class UserService {@Autowiredprivate UserMapper userMapper;@Transactionalpublic void add(UserModel userModel) {userMapper.insertUser(userModel);updateStatus(userModel);}@Transactionalpublic void updateStatus(UserModel userModel) {doSameThing();} }

在事務(wù)方法add中,直接調(diào)用事務(wù)方法updateStatus。 updateStatus方法擁有事務(wù)的能力是因?yàn)閟pring aop生成代理了對象,但是這種方法直接調(diào)用了this對象的方法,所以updateStatus方法不會生成事務(wù)

由此可見,在同一個類中的方法直接內(nèi)部調(diào)用,會導(dǎo)致事務(wù)失效。

那么問題來了,如果有些場景,確實(shí)想在同一個類的某個方法中,調(diào)用它自己的另外一個方法,該怎么辦呢?

修復(fù)方法一 : 【新加一個Service方法】

只需要新加一個Service方法,把@Transactional注解加到新Service方法上,把需要事務(wù)執(zhí)行的代碼移到新方法中。

具體代碼如下:

@Servcie public class ServiceA {@Autowiredprvate ServiceB serviceB;public void save(User user) {queryData1();queryData2();serviceB.doSave(user);}}@Servciepublic class ServiceB {@Transactional(rollbackFor=Exception.class)public void doSave(User user) {addData1();updateData2();}}

修復(fù)方法二:【在該Service類中注入自己】

如果不想再新加一個Service類,在該Service類中注入自己也是一種選擇。

具體代碼如下:

@Servcie public class ServiceA {@Autowiredprvate ServiceA serviceA;public void save(User user) {queryData1();queryData2();serviceA.doSave(user);}@Transactional(rollbackFor=Exception.class)public void doSave(User user) {addData1();updateData2();}}

注入自己 spring ioc內(nèi)部的三級緩存保證了它,不會出現(xiàn)循環(huán)依賴問題。


修復(fù)方法三:【通過AopContent類】<---- 推薦

在該Service類中使用AopContext.currentProxy()獲取代理對象

上面的方法2確實(shí)可以解決問題,但是代碼看起來并不直觀,還可以通過在該Service類中使用AOPProxy獲取代理對象,實(shí)現(xiàn)相同的功能。具體代碼如下:

@Servcie public class ServiceA {public void save(User user) {queryData1();queryData2();((ServiceA)AopContext.currentProxy()).doSave(user);}@Transactional(rollbackFor=Exception.class)public void doSave(User user) {addData1();updateData2();}}

訪問權(quán)限問題

@Service public class UserService {@Transactionalprivate void add(UserModel userModel) {saveData(userModel);updateData(userModel);} }

add方法的訪問權(quán)限被定義成了private,這樣會導(dǎo)致事務(wù)失效,spring要求被代理方法必須是public的。

源碼

AbstractFallbackTransactionAttributeSource#computeTransactionAttribute方法中有個判斷,如果目標(biāo)方法不是public,則TransactionAttribute返回null,即不支持事務(wù)。

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}............}

如果我們自定義的事務(wù)方法(即目標(biāo)方法),它的訪問權(quán)限不是public,而是private、default或protected的話,spring則不會提供事務(wù)功能。


方法用final修飾

有時候,某個方法不想被子類重新,這時可以將該方法定義成final的。普通方法這樣定義是沒問題的,但如果將事務(wù)方法定義成final,例如:

@Service public class UserService {@Transactionalpublic final void add(UserModel userModel){saveData(userModel);updateData(userModel);} }

我們可以看到add方法被定義成了final的,這樣會導(dǎo)致事務(wù)失效。

為什么?

spring事務(wù)底層使用了aop,也就是通過jdk動態(tài)代理或者cglib,幫我們生成了代理類,在代理類中實(shí)現(xiàn)的事務(wù)功能。

但如果某個方法用final修飾了,那么在它的代理類中,就無法重寫該方法,而添加事務(wù)功能。

注意:如果某個方法是static的,同樣無法通過動態(tài)代理,變成事務(wù)方法。


未被spring管理

使用spring事務(wù)的前提是:對象要被spring管理,需要創(chuàng)建bean實(shí)例。

通常情況下,我們通過@Controller、@Service、@Component、@Repository等注解,可以自動實(shí)現(xiàn)bean實(shí)例化和依賴注入的功能。

如果忘了加@Service注解,比如:

public class UserService {@Transactionalpublic void add(UserModel userModel) {saveData(userModel);updateData(userModel);} }

該類不會交給spring管理,所以它的add方法也不會生成事務(wù)。


多線程調(diào)用

@Slf4j @Service public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {userMapper.insertUser(userModel);new Thread(() -> {roleService.doOtherThing();}).start();} }@Service public class RoleService {@Transactionalpublic void doOtherThing() {System.out.println("保存role表數(shù)據(jù)");} }

事務(wù)方法add中,調(diào)用了事務(wù)方法doOtherThing,但是事務(wù)方法doOtherThing是在另外一個線程中調(diào)用的。

這樣會導(dǎo)致兩個方法不在同一個線程中,獲取到的數(shù)據(jù)庫連接不一樣,從而是兩個不同的事務(wù)。如果想doOtherThing方法中拋了異常,add方法也回滾是不可能的。

spring的事務(wù)是通過數(shù)據(jù)庫連接來實(shí)現(xiàn)的。當(dāng)前線程中保存了一個map,key是數(shù)據(jù)源,value是數(shù)據(jù)庫連接。

private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");

我們說的同一個事務(wù),其實(shí)是指同一個數(shù)據(jù)庫連接,只有擁有同一個數(shù)據(jù)庫連接才能同時提交和回滾。如果在不同的線程,拿到的數(shù)據(jù)庫連接肯定是不一樣的,所以是不同的事務(wù)。


表不支持事務(wù)

在mysql5之前,默認(rèn)的數(shù)據(jù)庫引擎是myisam。 索引文件和數(shù)據(jù)文件是分開存儲的,對于查多寫少的單表操作,性能比innodb更好。

myisam不支持事務(wù)。myisam還不支持行鎖和外鍵。

建議我們在開發(fā)的過程中,發(fā)現(xiàn)某張表的事務(wù)一直都沒有生效,那不一定是spring事務(wù)的鍋,最好確認(rèn)一下你使用的那張表,是否支持事務(wù)。


未開啟事務(wù)

有時候,事務(wù)沒有生效的根本原因是沒有開啟事務(wù)。

如果你使用的是springboot項(xiàng)目,那么你很幸運(yùn)。因?yàn)閟pringboot通過DataSourceTransactionManagerAutoConfiguration類,已經(jīng)默默的幫你開啟了事務(wù)。

你所要做的事情很簡單,只需要配置spring.datasource相關(guān)參數(shù)即可。

但如果你使用的還是傳統(tǒng)的spring項(xiàng)目,則需要在applicationContext.xml文件中,手動配置事務(wù)相關(guān)參數(shù)。如果忘了配置,事務(wù)肯定是不會生效的。

<!-- 配置事務(wù)管理器 --> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:advice id="advice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/></tx:attributes> </tx:advice> <!-- 用切點(diǎn)把事務(wù)切進(jìn)去 --> <aop:config> <aop:pointcut expression="execution(* com.susan.*.*(..))" id="pointcut"/> <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> </aop:config>

默默的說一句,如果在pointcut標(biāo)簽中的切入點(diǎn)匹配規(guī)則,配錯了的話,有些類的事務(wù)也不會生效。


事務(wù)不回滾

錯誤的傳播特性

在使用@Transactional注解時,是可以指定propagation參數(shù)的。

該參數(shù)的作用是指定事務(wù)的傳播特性,spring目前支持7種傳播特性:

  • REQUIRED 如果當(dāng)前上下文中存在事務(wù),那么加入該事務(wù),如果不存在事務(wù),創(chuàng)建一個事務(wù),這是默認(rèn)的傳播屬性值。
  • SUPPORTS 如果當(dāng)前上下文存在事務(wù),則支持事務(wù)加入事務(wù),如果不存在事務(wù),則使用非事務(wù)的方式執(zhí)行。
  • MANDATORY 如果當(dāng)前上下文中存在事務(wù),否則拋出異常。
  • REQUIRES_NEW 每次都會新建一個事務(wù),并且同時將上下文中的事務(wù)掛起,執(zhí)行當(dāng)前新建事務(wù)完成以后,上下文事務(wù)恢復(fù)再執(zhí)行。
  • NOT_SUPPORTED 如果當(dāng)前上下文中存在事務(wù),則掛起當(dāng)前事務(wù),然后新的方法在沒有事務(wù)的環(huán)境中執(zhí)行。
  • NEVER 如果當(dāng)前上下文中存在事務(wù),則拋出異常,否則在無事務(wù)環(huán)境上執(zhí)行代碼。
  • NESTED 如果當(dāng)前上下文中存在事務(wù),則嵌套事務(wù)執(zhí)行,如果不存在事務(wù),則新建事務(wù)。

如果我們在手動設(shè)置propagation參數(shù)的時候,把傳播特性設(shè)置錯了,比如:

@Service public class UserService {@Transactional(propagation = Propagation.NEVER)public void add(UserModel userModel) {saveData(userModel);updateData(userModel);} }

add方法的事務(wù)傳播特性定義成了Propagation.NEVER,這種類型的傳播特性不支持事務(wù),如果有事務(wù)則會拋異常。

目前只有這三種傳播特性才會創(chuàng)建新事務(wù):REQUIRED,REQUIRES_NEW,NESTED。


自己吞了異常

事務(wù)不會回滾,最常見的問題是:開發(fā)者在代碼中手動try…catch了異常。

@Slf4j @Service public class UserService {@Transactionalpublic void add(UserModel userModel) {try {saveData(userModel);updateData(userModel);} catch (Exception e) {log.error(e.getMessage(), e);}} }

這種情況下spring事務(wù)當(dāng)然不會回滾,因?yàn)殚_發(fā)者自己捕獲了異常,又沒有手動拋出,換句話說就是把異常吞掉了。

如果想要spring事務(wù)能夠正常回滾,必須拋出它能夠處理的異常。如果沒有拋異常,則spring認(rèn)為程序是正常的。


手動拋了別的異常

即使開發(fā)者沒有手動捕獲異常,但如果拋的異常不正確,spring事務(wù)也不會回滾。

@Slf4j @Service public class UserService {@Transactionalpublic void add(UserModel userModel) throws Exception {try {saveData(userModel);updateData(userModel);} catch (Exception e) {log.error(e.getMessage(), e);throw new Exception(e);}} }

上面的這種情況,開發(fā)人員自己捕獲了異常,又手動拋出了異常:Exception,事務(wù)同樣不會回滾。

因?yàn)閟pring事務(wù),默認(rèn)情況下只會回滾RuntimeException(運(yùn)行時異常)和Error(錯誤),對于普通的Exception(非運(yùn)行時異常),它不會回滾。


自定義了回滾異常

在使用@Transactional注解聲明事務(wù)時,有時我們想自定義回滾的異常,spring也是支持的。可以通過設(shè)置rollbackFor參數(shù),來完成這個功能。

但如果這個參數(shù)的值設(shè)置錯了,就會引出一些莫名其妙的問題,例如:

@Slf4j @Service public class UserService {@Transactional(rollbackFor = BusinessException.class)public void add(UserModel userModel) throws Exception {saveData(userModel);updateData(userModel);} }

如果在執(zhí)行上面這段代碼,保存和更新數(shù)據(jù)時,程序報(bào)錯了,拋了SqlException、DuplicateKeyException等異常。而BusinessException是我們自定義的異常,報(bào)錯的異常不屬于BusinessException,所以事務(wù)也不會回滾。

即使rollbackFor有默認(rèn)值,但阿里巴巴開發(fā)者規(guī)范中,還是要求開發(fā)者重新指定該參數(shù)。

這是為什么呢?

因?yàn)槿绻褂媚J(rèn)值,一旦程序拋出了Exception,事務(wù)不會回滾,這會出現(xiàn)很大的bug。所以,建議一般情況下,將該參數(shù)設(shè)置成:Exception或Throwable。


嵌套事務(wù)回滾多了

public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {userMapper.insertUser(userModel);roleService.doOtherThing();} }@Service public class RoleService {@Transactional(propagation = Propagation.NESTED)public void doOtherThing() {System.out.println("保存role表數(shù)據(jù)");} }

這種情況使用了嵌套的內(nèi)部事務(wù),原本是希望調(diào)用roleService.doOtherThing方法時,如果出現(xiàn)了異常,只回滾doOtherThing方法里的內(nèi)容,不回滾 userMapper.insertUser里的內(nèi)容,即回滾保存點(diǎn)。。但事實(shí)是,insertUser也回滾了。

why?

因?yàn)閐oOtherThing方法出現(xiàn)了異常,沒有手動捕獲,會繼續(xù)往上拋,到外層add方法的代理方法中捕獲了異常。所以,這種情況是直接回滾了整個事務(wù),不只回滾單個保存點(diǎn)。

怎么樣才能只回滾保存點(diǎn)呢?

@Slf4j @Service public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {userMapper.insertUser(userModel);try {roleService.doOtherThing();} catch (Exception e) {log.error(e.getMessage(), e);}} }

可以將內(nèi)部嵌套事務(wù)放在try/catch中,并且不繼續(xù)往上拋異常。這樣就能保證,如果內(nèi)部嵌套事務(wù)中出現(xiàn)異常,只回滾內(nèi)部事務(wù),而不影響外部事務(wù)。


其他常見問題

編程式事務(wù)

上面聊的這些內(nèi)容都是基于@Transactional注解的,主要說的是它的事務(wù)問題,我們把這種事務(wù)叫做:聲明式事務(wù)

其實(shí),spring還提供了另外一種創(chuàng)建事務(wù)的方式,即通過手動編寫代碼實(shí)現(xiàn)的事務(wù),我們把這種事務(wù)叫做:編程式事務(wù)。例如:

@Autowiredprivate TransactionTemplate transactionTemplate;...public void save(final User user) {queryData1();queryData2();transactionTemplate.execute((status) => {addData1();updateData2();return Boolean.TRUE;})}

在spring中為了支持編程式事務(wù),專門提供了一個類:TransactionTemplate,在它的execute方法中,就實(shí)現(xiàn)了事務(wù)的功能。

相較于@Transactional注解聲明式事務(wù),我更建議大家使用,基于TransactionTemplate的編程式事務(wù)。主要原因如下:

  • 避免由于spring aop問題,導(dǎo)致事務(wù)失效的問題。
  • 能夠更小粒度的控制事務(wù)的范圍,更直觀。

大事務(wù)問題

在使用spring事務(wù)時,有個讓人非常頭疼的問題,就是大事務(wù)問題。

通常情況下,我們會在方法上@Transactional注解,填加事務(wù)功能,比如:

@Service public class UserService {@Autowired private RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {query1();query2();query3();roleService.save(userModel);update(userModel);} }@Service public class RoleService {@Autowired private RoleService roleService;@Transactionalpublic void save(UserModel userModel) throws Exception {query4();query5();query6();saveData(userModel);} }

但@Transactional注解,如果被加到方法上,有個缺點(diǎn)就是整個方法都包含在事務(wù)當(dāng)中了。

上面的這個例子中,在UserService類中,其實(shí)只有這兩行才需要事務(wù):

roleService.save(userModel); update(userModel);

在RoleService類中,只有這一行需要事務(wù):

saveData(userModel);

現(xiàn)在的這種寫法,會導(dǎo)致所有的query方法也被包含在同一個事務(wù)當(dāng)中。

如果query方法非常多,調(diào)用層級很深,而且有部分查詢方法比較耗時的話,會造成整個事務(wù)非常耗時,而從造成大事務(wù)問題。

  • 死鎖
  • 回滾時間長
  • 并發(fā)情況下,連接池被占滿
  • 鎖等待
  • 接口超時
  • 主從延遲

小結(jié)

建議在項(xiàng)目中少使用@Transactional注解開啟事務(wù)。但并不是說一定不能用它,如果項(xiàng)目中有些業(yè)務(wù)邏輯比較簡單,而且不經(jīng)常變動,使用@Transactional注解開啟事務(wù)開啟事務(wù)也無妨,因?yàn)樗唵?#xff0c;開發(fā)效率更高,但是千萬要小心事務(wù)失效的問題。

總結(jié)

以上是生活随笔為你收集整理的每日一博 - 常见的Spring事务失效事务不回滚案例集锦的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 五月综合激情 | 丝袜国产视频 | 午夜看片在线观看 | 人人爽人人爽人人爽人人爽 | 国产精品一区二三区 | 久久久久久99精品 | 亚洲天堂2021av | 北条麻妃青青久久 | 成人亚洲电影 | 欧美区国产区 | 最新日韩一区 | 成人国产视频在线观看 | 黑人一级片| 风间由美一区二区 | 国产成人一区二区三区别 | 黄色调教视频 | 午夜免费网址 | 日日好av | 久久久免费高清视频 | 精品视频久久久久久 | 毛片啪啪啪 | 亚洲成人av一区二区 | 亲切的金子餐桌片段的金子 | 免费观看污视频 | 日本不卡在线播放 | 伊人色影院 | 日韩少妇一区 | 亚洲短视频 | 亚洲丝袜在线视频 | 九九热最新视频 | 黄色国产视频网站 | 日本免费在线观看 | 亚洲网色| 国产永久在线观看 | 亚洲天堂黄 | 欧美国产日韩在线观看成人 | 26uuu亚洲国产精品 | 久久亚洲精品国产精品黑人v | 黄色激情视频在线观看 | 黄色动漫免费在线观看 | www啪啪 | 91精品久久香蕉国产线看观看 | 欧美在线一卡 | 亚洲一区精品在线 | 亚洲视频 一区 | 精品视频站长推荐 | 亚洲天堂av电影 | 69视频在线观看免费 | 国产精品久久久久三级无码 | 天堂在线8| av激情在线观看 | 欧美色吊丝 | 日韩精品免费视频 | 日韩高清在线播放 | 亚洲欧洲中文 | 中文字幕在线观看亚洲 | 精品国产乱码久久久久久牛牛 | 天天操网站 | 人妻洗澡被强公日日澡电影 | 久久久久久久久久久久久久 | 99成人国产精品视频 | 日本色视频 | 一本色道综合久久欧美日韩精品 | 亚洲 欧美 激情 另类 校园 | 无码人妻一区二区三区在线 | 激情久久久久久 | 久久精品这里 | 成年人在线观看视频 | av自拍偷拍 | 天美视频在线观看 | 女人被男人操 | 俺也去网站 | 日韩欧美在线一区二区 | 久久影视大全 | 26uuu欧美日本| av在线官网 | 伊人一区二区三区四区 | 毛片毛片 | 免费看a网站 | 日韩 国产 欧美 | 中文字幕在线观看视频一区 | 欧美激情免费视频 | 久草视频在线免费 | 亚洲成人av影片 | 国产日韩欧美自拍 | 国产精品国产三级国产普通话蜜臀 | 色在线免费视频 | 一区二区日韩av | 人妻无码久久一区二区三区免费 | 亚洲福利一区二区三区 | 日xxxx| www.色综合| 久久国产免费观看 | 欧美日韩大片在线观看 | 国产中文在线视频 | 光明影院手机版在线观看免费 | 日本一二三区视频 | 男男成人高潮片免费网站 | 亚洲欧美日韩久久 |