AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當(dāng)前位置:首頁 > 科技  > 軟件

聊一聊Spring事務(wù)失效的12種場景

來源: 責(zé)編: 時(shí)間:2024-09-10 09:47:39 122觀看
導(dǎo)讀前言對于從事java開發(fā)工作的同學(xué)來說,spring的事務(wù)肯定再熟悉不過了。在某些業(yè)務(wù)場景下,如果一個(gè)請求中,需要同時(shí)寫入多張表的數(shù)據(jù)。為了保證操作的原子性(要么同時(shí)成功,要么同時(shí)失敗),避免數(shù)據(jù)不一致的情況,我們一般都會(huì)用到

qDR28資訊網(wǎng)——每日最新資訊28at.com

前言

對于從事java開發(fā)工作的同學(xué)來說,spring的事務(wù)肯定再熟悉不過了。qDR28資訊網(wǎng)——每日最新資訊28at.com

在某些業(yè)務(wù)場景下,如果一個(gè)請求中,需要同時(shí)寫入多張表的數(shù)據(jù)。為了保證操作的原子性(要么同時(shí)成功,要么同時(shí)失敗),避免數(shù)據(jù)不一致的情況,我們一般都會(huì)用到spring事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

確實(shí),spring事務(wù)用起來賊爽,就用一個(gè)簡單的注解:@Transactional,就能輕松搞定事務(wù)。我猜大部分小伙伴也是這樣用的,而且一直用一直爽。qDR28資訊網(wǎng)——每日最新資訊28at.com

但如果你使用不當(dāng),它也會(huì)坑你于無形。qDR28資訊網(wǎng)——每日最新資訊28at.com

今天我們就一起聊聊,事務(wù)失效的一些場景,說不定你已經(jīng)中招了。不信,讓我們一起看看。qDR28資訊網(wǎng)——每日最新資訊28at.com

qDR28資訊網(wǎng)——每日最新資訊28at.com

一、事務(wù)不生效

1.訪問權(quán)限問題

眾所周知,java的訪問權(quán)限主要有四種:private、default、protected、public,它們的權(quán)限從左到右,依次變大。qDR28資訊網(wǎng)——每日最新資訊28at.com

但如果我們在開發(fā)過程中,把有某些事務(wù)方法,定義了錯(cuò)誤的訪問權(quán)限,就會(huì)導(dǎo)致事務(wù)功能出問題,例如:qDR28資訊網(wǎng)——每日最新資訊28at.com

@Servicepublic class UserService {        @Transactional    private void add(UserModel userModel) {         saveData(userModel);         updateData(userModel);    }}

我們可以看到add方法的訪問權(quán)限被定義成了private,這樣會(huì)導(dǎo)致事務(wù)失效,spring要求被代理方法必須是public的。qDR28資訊網(wǎng)——每日最新資訊28at.com

說白了,在AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法中有個(gè)判斷,如果目標(biāo)方法不是public,則TransactionAttribute返回null,即不支持事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {    // Don't allow no-public methods as required.    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {      return null;    }    // The method may be on an interface, but we need attributes from the target class.    // If the target class is null, the method will be unchanged.    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);    // First try is the method in the target class.    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);    if (txAttr != null) {      return txAttr;    }    // Second try is the transaction attribute on the target class.    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {      return txAttr;    }    if (specificMethod != method) {      // Fallback is to look at the original method.      txAttr = findTransactionAttribute(method);      if (txAttr != null) {        return txAttr;      }      // Last fallback is the class of the original method.      txAttr = findTransactionAttribute(method.getDeclaringClass());      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {        return txAttr;      }    }    return null;  }

也就是說,如果我們自定義的事務(wù)方法(即目標(biāo)方法),它的訪問權(quán)限不是public,而是private、default或protected的話,spring則不會(huì)提供事務(wù)功能。qDR28資訊網(wǎng)——每日最新資訊28at.com

2. 方法用final修飾

有時(shí)候,某個(gè)方法不想被子類重新,這時(shí)可以將該方法定義成final的。普通方法這樣定義是沒問題的,但如果將事務(wù)方法定義成final,例如:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

我們可以看到add方法被定義成了final的,這樣會(huì)導(dǎo)致事務(wù)失效。qDR28資訊網(wǎng)——每日最新資訊28at.com

為什么?qDR28資訊網(wǎng)——每日最新資訊28at.com

如果你看過spring事務(wù)的源碼,可能會(huì)知道spring事務(wù)底層使用了aop,也就是通過jdk動(dòng)態(tài)代理或者cglib,幫我們生成了代理類,在代理類中實(shí)現(xiàn)的事務(wù)功能。qDR28資訊網(wǎng)——每日最新資訊28at.com

但如果某個(gè)方法用final修飾了,那么在它的代理類中,就無法重寫該方法,而添加事務(wù)功能。qDR28資訊網(wǎng)——每日最新資訊28at.com

注意:如果某個(gè)方法是static的,同樣無法通過動(dòng)態(tài)代理,變成事務(wù)方法。qDR28資訊網(wǎng)——每日最新資訊28at.com

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

有時(shí)候我們需要在某個(gè)Service類的某個(gè)方法中,調(diào)用另外一個(gè)事務(wù)方法,比如:qDR28資訊網(wǎng)——每日最新資訊28at.com

@Servicepublic class UserService {    @Autowired    private UserMapper userMapper;      public void add(UserModel userModel) {        userMapper.insertUser(userModel);        updateStatus(userModel);    }    @Transactional    public void updateStatus(UserModel userModel) {        doSameThing();    }}

我們看到在事務(wù)方法add中,直接調(diào)用事務(wù)方法updateStatus。從前面介紹的內(nèi)容可以知道,updateStatus方法擁有事務(wù)的能力是因?yàn)閟pring aop生成代理了對象,但是這種方法直接調(diào)用了this對象的方法,所以u(píng)pdateStatus方法不會(huì)生成事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

由此可見,在同一個(gè)類中的方法直接內(nèi)部調(diào)用,會(huì)導(dǎo)致事務(wù)失效。qDR28資訊網(wǎng)——每日最新資訊28at.com

那么問題來了,如果有些場景,確實(shí)想在同一個(gè)類的某個(gè)方法中,調(diào)用它自己的另外一個(gè)方法,該怎么辦呢?qDR28資訊網(wǎng)——每日最新資訊28at.com

(1) 新加一個(gè)Service方法

這個(gè)方法非常簡單,只需要新加一個(gè)Service方法,把@Transactional注解加到新Service方法上,把需要事務(wù)執(zhí)行的代碼移到新方法中。具體代碼如下:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

(2)在該Service類中注入自己

如果不想再新加一個(gè)Service類,在該Service類中注入自己也是一種選擇。具體代碼如下:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

可能有些人可能會(huì)有這樣的疑問:這種做法會(huì)不會(huì)出現(xiàn)循環(huán)依賴問題?qDR28資訊網(wǎng)——每日最新資訊28at.com

答案:不會(huì)。qDR28資訊網(wǎng)——每日最新資訊28at.com

其實(shí)spring ioc內(nèi)部的三級(jí)緩存保證了它,不會(huì)出現(xiàn)循環(huán)依賴問題。qDR28資訊網(wǎng)——每日最新資訊28at.com

(3)通過AopContent類

在該Service類中使用AopContext.currentProxy()獲取代理對象qDR28資訊網(wǎng)——每日最新資訊28at.com

上面的方法2確實(shí)可以解決問題,但是代碼看起來并不直觀,還可以通過在該Service類中使用AOPProxy獲取代理對象,實(shí)現(xiàn)相同的功能。具體代碼如下:qDR28資訊網(wǎng)——每日最新資訊28at.com

@Servciepublic 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();    } }

4.未被spring管理

在我們平時(shí)開發(fā)過程中,有個(gè)細(xì)節(jié)很容易被忽略。即使用spring事務(wù)的前提是:對象要被spring管理,需要?jiǎng)?chuàng)建bean實(shí)例。qDR28資訊網(wǎng)——每日最新資訊28at.com

通常情況下,我們通過@Controller、@Service、@Component、@Repository等注解,可以自動(dòng)實(shí)現(xiàn)bean實(shí)例化和依賴注入的功能。qDR28資訊網(wǎng)——每日最新資訊28at.com

如果有一天,你匆匆忙忙的開發(fā)了一個(gè)Service類,但忘了加@Service注解,比如:qDR28資訊網(wǎng)——每日最新資訊28at.com

//@Servicepublic class UserService {    @Transactional    public void add(UserModel userModel) {         saveData(userModel);         updateData(userModel);    }    }

從上面的例子,我們可以看到UserService類沒有加@Service注解,那么該類不會(huì)交給spring管理,所以它的add方法也不會(huì)生成事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

5.多線程調(diào)用

在實(shí)際項(xiàng)目開發(fā)中,多線程的使用場景還是挺多的。如果spring事務(wù)用在多線程場景中,會(huì)有問題嗎?qDR28資訊網(wǎng)——每日最新資訊28at.com

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

從上面的例子中,我們可以看到事務(wù)方法add中,調(diào)用了事務(wù)方法doOtherThing,但是事務(wù)方法doOtherThing是在另外一個(gè)線程中調(diào)用的。qDR28資訊網(wǎng)——每日最新資訊28at.com

這樣會(huì)導(dǎo)致兩個(gè)方法不在同一個(gè)線程中,獲取到的數(shù)據(jù)庫連接不一樣,從而是兩個(gè)不同的事務(wù)。如果想doOtherThing方法中拋了異常,add方法也回滾是不可能的。qDR28資訊網(wǎng)——每日最新資訊28at.com

如果看過spring事務(wù)源碼的朋友,可能會(huì)知道spring的事務(wù)是通過數(shù)據(jù)庫連接來實(shí)現(xiàn)的。當(dāng)前線程中保存了一個(gè)map,key是數(shù)據(jù)源,value是數(shù)據(jù)庫連接。qDR28資訊網(wǎng)——每日最新資訊28at.com

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

我們說的同一個(gè)事務(wù),其實(shí)是指同一個(gè)數(shù)據(jù)庫連接,只有擁有同一個(gè)數(shù)據(jù)庫連接才能同時(shí)提交和回滾。如果在不同的線程,拿到的數(shù)據(jù)庫連接肯定是不一樣的,所以是不同的事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

6.表不支持事務(wù)

周所周知,在mysql5之前,默認(rèn)的數(shù)據(jù)庫引擎是myisam。qDR28資訊網(wǎng)——每日最新資訊28at.com

它的好處就不用多說了:索引文件和數(shù)據(jù)文件是分開存儲(chǔ)的,對于查多寫少的單表操作,性能比innodb更好。qDR28資訊網(wǎng)——每日最新資訊28at.com

有些老項(xiàng)目中,可能還在用它。qDR28資訊網(wǎng)——每日最新資訊28at.com

在創(chuàng)建表的時(shí)候,只需要把ENGINE參數(shù)設(shè)置成MyISAM即可:qDR28資訊網(wǎng)——每日最新資訊28at.com

CREATE TABLE `category` (  `id` bigint NOT NULL AUTO_INCREMENT,  `one_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,  `two_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,  `three_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,  `four_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

myisam好用,但有個(gè)很致命的問題是:不支持事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

如果只是單表操作還好,不會(huì)出現(xiàn)太大的問題。但如果需要跨多張表操作,由于其不支持事務(wù),數(shù)據(jù)極有可能會(huì)出現(xiàn)不完整的情況。qDR28資訊網(wǎng)——每日最新資訊28at.com

此外,myisam還不支持行鎖和外鍵。qDR28資訊網(wǎng)——每日最新資訊28at.com

所以在實(shí)際業(yè)務(wù)場景中,myisam使用的并不多。在mysql5以后,myisam已經(jīng)逐漸退出了歷史的舞臺(tái),取而代之的是innodb。qDR28資訊網(wǎng)——每日最新資訊28at.com

有時(shí)候我們在開發(fā)的過程中,發(fā)現(xiàn)某張表的事務(wù)一直都沒有生效,那不一定是spring事務(wù)的鍋,最好確認(rèn)一下你使用的那張表,是否支持事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

7.未開啟事務(wù)

有時(shí)候,事務(wù)沒有生效的根本原因是沒有開啟事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

你看到這句話可能會(huì)覺得好笑。qDR28資訊網(wǎng)——每日最新資訊28at.com

開啟事務(wù)不是一個(gè)項(xiàng)目中,最最最基本的功能嗎?qDR28資訊網(wǎng)——每日最新資訊28at.com

為什么還會(huì)沒有開啟事務(wù)?qDR28資訊網(wǎng)——每日最新資訊28at.com

沒錯(cuò),如果項(xiàng)目已經(jīng)搭建好了,事務(wù)功能肯定是有的。qDR28資訊網(wǎng)——每日最新資訊28at.com

但如果你是在搭建項(xiàng)目demo的時(shí)候,只有一張表,而這張表的事務(wù)沒有生效。那么會(huì)是什么原因造成的呢?qDR28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)然原因有很多,但沒有開啟事務(wù),這個(gè)原因極其容易被忽略。qDR28資訊網(wǎng)——每日最新資訊28at.com

如果你使用的是springboot項(xiàng)目,那么你很幸運(yùn)。因?yàn)閟pringboot通過DataSourceTransactionManagerAutoConfiguration類,已經(jīng)默默的幫你開啟了事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

你所要做的事情很簡單,只需要配置spring.datasource相關(guān)參數(shù)即可。qDR28資訊網(wǎng)——每日最新資訊28at.com

但如果你使用的還是傳統(tǒng)的spring項(xiàng)目,則需要在applicationContext.xml文件中,手動(dòng)配置事務(wù)相關(guān)參數(shù)。如果忘了配置,事務(wù)肯定是不會(huì)生效的。qDR28資訊網(wǎng)——每日最新資訊28at.com

具體配置如下信息:qDR28資訊網(wǎng)——每日最新資訊28at.com

<!-- 配置事務(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="*" propagatinotallow="REQUIRED"/>    </tx:attributes> </tx:advice> <!-- 用切點(diǎn)把事務(wù)切進(jìn)去 --> <aop:config>     <aop:pointcut expressinotallow="execution(* com.susan.*.*(..))" id="pointcut"/>     <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> </aop:config>

默默的說一句,如果在pointcut標(biāo)簽中的切入點(diǎn)匹配規(guī)則,配錯(cuò)了的話,有些類的事務(wù)也不會(huì)生效。qDR28資訊網(wǎng)——每日最新資訊28at.com

二、事務(wù)不回滾

1.錯(cuò)誤的傳播特性

其實(shí),我們在使用@Transactional注解時(shí),是可以指定propagation參數(shù)的。qDR28資訊網(wǎng)——每日最新資訊28at.com

該參數(shù)的作用是指定事務(wù)的傳播特性,spring目前支持7種傳播特性:qDR28資訊網(wǎng)——每日最新資訊28at.com

  • REQUIRED 如果當(dāng)前上下文中存在事務(wù),那么加入該事務(wù),如果不存在事務(wù),創(chuàng)建一個(gè)事務(wù),這是默認(rèn)的傳播屬性值。
  • SUPPORTS 如果當(dāng)前上下文存在事務(wù),則支持事務(wù)加入事務(wù),如果不存在事務(wù),則使用非事務(wù)的方式執(zhí)行。
  • MANDATORY 如果當(dāng)前上下文中存在事務(wù),否則拋出異常。
  • REQUIRES_NEW 每次都會(huì)新建一個(gè)事務(wù),并且同時(shí)將上下文中的事務(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ù)。

如果我們在手動(dòng)設(shè)置propagation參數(shù)的時(shí)候,把傳播特性設(shè)置錯(cuò)了,比如:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

我們可以看到add方法的事務(wù)傳播特性定義成了Propagation.NEVER,這種類型的傳播特性不支持事務(wù),如果有事務(wù)則會(huì)拋異常。qDR28資訊網(wǎng)——每日最新資訊28at.com

目前只有這三種傳播特性才會(huì)創(chuàng)建新事務(wù):REQUIRED,REQUIRES_NEW,NESTED。qDR28資訊網(wǎng)——每日最新資訊28at.com

2.自己吞了異常

事務(wù)不會(huì)回滾,最常見的問題是:開發(fā)者在代碼中手動(dòng)try...catch了異常。比如:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

這種情況下spring事務(wù)當(dāng)然不會(huì)回滾,因?yàn)殚_發(fā)者自己捕獲了異常,又沒有手動(dòng)拋出,換句話說就是把異常吞掉了。qDR28資訊網(wǎng)——每日最新資訊28at.com

如果想要spring事務(wù)能夠正常回滾,必須拋出它能夠處理的異常。如果沒有拋異常,則spring認(rèn)為程序是正常的。qDR28資訊網(wǎng)——每日最新資訊28at.com

3.手動(dòng)拋了別的異常

即使開發(fā)者沒有手動(dòng)捕獲異常,但如果拋的異常不正確,spring事務(wù)也不會(huì)回滾。qDR28資訊網(wǎng)——每日最新資訊28at.com

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

上面的這種情況,開發(fā)人員自己捕獲了異常,又手動(dòng)拋出了異常:Exception,事務(wù)同樣不會(huì)回滾。qDR28資訊網(wǎng)——每日最新資訊28at.com

因?yàn)閟pring事務(wù),默認(rèn)情況下只會(huì)回滾RuntimeException(運(yùn)行時(shí)異常)和Error(錯(cuò)誤),對于普通的Exception(非運(yùn)行時(shí)異常),它不會(huì)回滾。qDR28資訊網(wǎng)——每日最新資訊28at.com

4.自定義了回滾異常

在使用@Transactional注解聲明事務(wù)時(shí),有時(shí)我們想自定義回滾的異常,spring也是支持的。可以通過設(shè)置rollbackFor參數(shù),來完成這個(gè)功能。qDR28資訊網(wǎng)——每日最新資訊28at.com

但如果這個(gè)參數(shù)的值設(shè)置錯(cuò)了,就會(huì)引出一些莫名其妙的問題,例如:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

如果在執(zhí)行上面這段代碼,保存和更新數(shù)據(jù)時(shí),程序報(bào)錯(cuò)了,拋了SqlException、DuplicateKeyException等異常。而BusinessException是我們自定義的異常,報(bào)錯(cuò)的異常不屬于BusinessException,所以事務(wù)也不會(huì)回滾。qDR28資訊網(wǎng)——每日最新資訊28at.com

即使rollbackFor有默認(rèn)值,但阿里巴巴開發(fā)者規(guī)范中,還是要求開發(fā)者重新指定該參數(shù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

這是為什么呢?qDR28資訊網(wǎng)——每日最新資訊28at.com

因?yàn)槿绻褂媚J(rèn)值,一旦程序拋出了Exception,事務(wù)不會(huì)回滾,這會(huì)出現(xiàn)很大的bug。所以,建議一般情況下,將該參數(shù)設(shè)置成:Exception或Throwable。qDR28資訊網(wǎng)——每日最新資訊28at.com

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

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

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

why?qDR28資訊網(wǎng)——每日最新資訊28at.com

因?yàn)閐oOtherThing方法出現(xiàn)了異常,沒有手動(dòng)捕獲,會(huì)繼續(xù)往上拋,到外層add方法的代理方法中捕獲了異常。所以,這種情況是直接回滾了整個(gè)事務(wù),不只回滾單個(gè)保存點(diǎn)。qDR28資訊網(wǎng)——每日最新資訊28at.com

怎么樣才能只回滾保存點(diǎn)呢?qDR28資訊網(wǎng)——每日最新資訊28at.com

@Slf4j@Servicepublic class UserService {    @Autowired    private UserMapper userMapper;    @Autowired    private RoleService roleService;    @Transactional    public 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ù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

三、其他

1.大事務(wù)問題

在使用spring事務(wù)時(shí),有個(gè)讓人非常頭疼的問題,就是大事務(wù)問題。qDR28資訊網(wǎng)——每日最新資訊28at.com

通常情況下,我們會(huì)在方法上@Transactional注解,填加事務(wù)功能,比如:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

但@Transactional注解,如果被加到方法上,有個(gè)缺點(diǎn)就是整個(gè)方法都包含在事務(wù)當(dāng)中了。qDR28資訊網(wǎng)——每日最新資訊28at.com

上面的這個(gè)例子中,在UserService類中,其實(shí)只有這兩行才需要事務(wù):qDR28資訊網(wǎng)——每日最新資訊28at.com

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

在RoleService類中,只有這一行需要事務(wù):qDR28資訊網(wǎng)——每日最新資訊28at.com

saveData(userModel);

現(xiàn)在的這種寫法,會(huì)導(dǎo)致所有的query方法也被包含在同一個(gè)事務(wù)當(dāng)中。qDR28資訊網(wǎng)——每日最新資訊28at.com

如果query方法非常多,調(diào)用層級(jí)很深,而且有部分查詢方法比較耗時(shí)的話,會(huì)造成整個(gè)事務(wù)非常耗時(shí),而從造成大事務(wù)問題。qDR28資訊網(wǎng)——每日最新資訊28at.com

qDR28資訊網(wǎng)——每日最新資訊28at.com

2.編程式事務(wù)

上面聊的這些內(nèi)容都是基于@Transactional注解的,主要說的是它的事務(wù)問題,我們把這種事務(wù)叫做:聲明式事務(wù)。qDR28資訊網(wǎng)——每日最新資訊28at.com

其實(shí),spring還提供了另外一種創(chuàng)建事務(wù)的方式,即通過手動(dòng)編寫代碼實(shí)現(xiàn)的事務(wù),我們把這種事務(wù)叫做:編程式事務(wù)。例如:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

在spring中為了支持編程式事務(wù),專門提供了一個(gè)類:TransactionTemplate,在它的execute方法中,就實(shí)現(xiàn)了事務(wù)的功能。qDR28資訊網(wǎng)——每日最新資訊28at.com

相較于@Transactional注解聲明式事務(wù),我更建議大家使用,基于TransactionTemplate的編程式事務(wù)。主要原因如下:qDR28資訊網(wǎng)——每日最新資訊28at.com

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

建議在項(xiàng)目中少使用@Transactional注解開啟事務(wù)。但并不是說一定不能用它,如果項(xiàng)目中有些業(yè)務(wù)邏輯比較簡單,而且不經(jīng)常變動(dòng),使用@Transactional注解開啟事務(wù)開啟事務(wù)也無妨,因?yàn)樗唵危_發(fā)效率更高,但是千萬要小心事務(wù)失效的問題。qDR28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-112728-0.html聊一聊Spring事務(wù)失效的12種場景

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 面試官:你對插件化有什么了解?

下一篇: SpringBoot與XXL-JOB高效定時(shí)任務(wù)管理指南

標(biāo)簽:
  • 熱門焦點(diǎn)
  • 對標(biāo)蘋果的靈動(dòng)島 華為帶來實(shí)況窗功能

    繼蘋果的靈動(dòng)島之后,華為也在今天正式推出了“實(shí)況窗”功能。據(jù)今天鴻蒙OS 4.0的現(xiàn)場演示顯示,華為的實(shí)況窗可以更高效的展現(xiàn)出實(shí)時(shí)通知,比如鎖屏上就能看到外賣、打車、銀行
  • 分布式系統(tǒng)中的CAP理論,面試必問,你理解了嘛?

    對于剛剛接觸分布式系統(tǒng)的小伙伴們來說,一提起分布式系統(tǒng),就感覺高大上,深不可測。而且看了很多書和視頻還是一臉懵逼。這篇文章主要使用大白話的方式,帶你理解一下分布式系統(tǒng)
  • 如何正確使用:Has和:Nth-Last-Child

    我們可以用CSS檢查,以了解一組元素的數(shù)量是否小于或等于一個(gè)數(shù)字。例如,一個(gè)擁有三個(gè)或更多子項(xiàng)的grid。你可能會(huì)想,為什么需要這樣做呢?在某些情況下,一個(gè)組件或一個(gè)布局可能會(huì)
  • 學(xué)習(xí)JavaScript的10個(gè)理由...

    作者 | Simplilearn編譯 | 王瑞平當(dāng)你決心學(xué)習(xí)一門語言的時(shí)候,很難選擇到底應(yīng)該學(xué)習(xí)哪一門,常用的語言有Python、Java、JavaScript、C/CPP、PHP、Swift、C#、Ruby、Objective-
  • 新電商三兄弟,“抖快紅”成團(tuán)!

    來源:價(jià)值研究所作 者:Hernanderz 隨著內(nèi)容電商的概念興起,抖音、快手、小紅書組成的&ldquo;新電商三兄弟&rdquo;成為業(yè)內(nèi)一股不可忽視的勢力,給阿里、京東、拼多多帶去了巨大壓
  • 重估百度丨大模型,能撐起百度的“今天”嗎?

    自象限原創(chuàng) 作者|程心 羅輯2023年之前,對于自己的&ldquo;今天&rdquo;,百度也很迷茫。&ldquo;新業(yè)務(wù)到 2022 年底還是 0,希望 2023 年出來一個(gè) 1。&rdquo;這是2022年底,李彥宏
  • 微博大門常打開,迎接海外畫師漂洋東渡

    作者:互聯(lián)網(wǎng)那些事&ldquo;起猛了,我能看得懂日語了&rdquo;。&ldquo;為什么日本人說話我能聽懂?&rdquo;&ldquo;中文不像中文,日語不像日語,但是我竟然看懂了&rdquo;&hellip;&hell
  • 蘋果、三星、惠普等暫停向印度出口筆記本和平板電腦

    集微網(wǎng)消息,據(jù)彭博社報(bào)道,在8月3日印度突然禁止在沒有許可證的情況下向印度進(jìn)口電腦/平板及顯示器等產(chǎn)品后,蘋果、三星電子和惠普等大公司暫停向印度
  • 華為舉行春季智慧辦公新品發(fā)布會(huì) 首次推出電子墨水屏平板

    北京時(shí)間2月27日晚,華為在巴塞羅那舉行春季智慧辦公新品發(fā)布會(huì),在海外市場推出之前已經(jīng)在中國市場上市的筆記本、平板、激光打印機(jī)等辦公產(chǎn)品,并首次推出搭載
Top