Spring事務(wù)的實(shí)現(xiàn)

我們看事務(wù)的源碼莱找,不僅是為了更好地使用Spring酬姆,而且能夠從源碼中學(xué)習(xí)到編程思想,設(shè)計(jì)思想奥溺。這篇文章框架如圖所示:


Spring事務(wù)

一辞色、相關(guān)知識(shí)

MySQL事務(wù)

  1. 事務(wù)的啟動(dòng)
  • 顯式啟動(dòng)事務(wù)語句, begin 或 start transaction浮定。配套的提交語句是 commit相满,回滾語句是 rollback。
  • set autocommit=0桦卒,這個(gè)命令會(huì)將這個(gè)線程的自動(dòng)提交關(guān)掉立美。意味著如果你只執(zhí)行一個(gè) select 語句,這個(gè)事務(wù)就啟動(dòng)了方灾,而且并不會(huì)自動(dòng)提交悯辙。這個(gè)事務(wù)持續(xù)存在直到你主動(dòng)執(zhí)行 commit 或 rollback 語句,或者斷開連接迎吵。建議設(shè)置set autocommit=1躲撰,避免造成長事務(wù)。
  1. 事務(wù)的隔離性:未提交击费、提交讀拢蛋、可重復(fù)讀、串行化
  2. MVCC
    MySQL的默認(rèn)隔離級(jí)別是可重復(fù)讀蔫巩,下面以可重復(fù)度隔離級(jí)別談?wù)凪VCC是怎么實(shí)現(xiàn)可重復(fù)讀的谆棱。

MySQL45講
begin/start transaction 命令并不是一個(gè)事務(wù)的起點(diǎn),在執(zhí)行到它們之后的第一個(gè)操作 InnoDB 表的語句圆仔,事務(wù)才真正啟動(dòng)垃瞧。如果你想要馬上啟動(dòng)一個(gè)事務(wù),可以使用 start transaction with consistent snapshot 這個(gè)命令坪郭。
第一種啟動(dòng)方式个从,一致性視圖是在執(zhí)行第一個(gè)快照讀語句時(shí)創(chuàng)建的;第二種啟動(dòng)方式歪沃,一致性視圖是在執(zhí)行 start transaction with consistent snapshot 時(shí)創(chuàng)建的嗦锐。

對(duì)于MySQL的每一行上,有一個(gè)row trx_id沪曙,表示當(dāng)前的版本號(hào)奕污,每次事務(wù)提交之后,會(huì)新增一個(gè)版本行液走,新的版本行的row trx_id更新為事務(wù)的id


image.png

每個(gè)事務(wù)所能看到的一致性視圖碳默,就是事務(wù)啟動(dòng)瞬間已提交的行贾陷。如果事務(wù)的行還未提交,那么需要回滾到上一個(gè)版本嘱根。

  1. undo log回滾
    我們看到的上圖的箭頭昵宇,就是 undo log;而 V1儿子、V2瓦哎、V3 并不是物理上真實(shí)存在的,而是每次需要的時(shí)候根據(jù)當(dāng)前版本和 undo log 計(jì)算出來的柔逼。比如蒋譬,需要 V2 的時(shí)候,就是通過 V4 依次執(zhí)行 兩次undo log U3愉适、U2 算出來犯助。

ThreadLocal

ThreadLocal是Java線程封閉的一種方式,將變量局限在線程中维咸,每個(gè)線程都有一份數(shù)據(jù)剂买,避免了變量的共享,也就避免了線程安全的問題癌蓖。
具體的實(shí)現(xiàn)可以看:http://www.reibang.com/p/22ca4db0a616

Spring AOP

  1. AOP的一些概念
    1.1 Join point和pointcut區(qū)別
    Join point 就是菜單上的選項(xiàng)瞬哼,Pointcut就是你選的菜。Join point 你只是你切面中可以切的那些方法租副,一旦你選擇了要切哪些方法坐慰,那就是Pointcut。
    也就是說用僧,所有在你程序中可以被調(diào)用的方法都是Join point. 使用Pointcut 表達(dá)式结胀,那些匹配的方法,才叫Pointcut责循。所以你根本不用關(guān)心Join point糟港。比如你有10個(gè)方法,你只想切2個(gè)方法院仿,那么那10個(gè)方法就是Join point秸抚, 2個(gè)方法就是Pointcut
    1.2 Advice
    advice就是你作用到pointcut上的方式,你可以使用befor, after 或者around
    1.3 advisor
    advisor就是作用在具體對(duì)象上的ponitcut和advice意蛀,把ponitcut和advice合起來就是advisor耸别。切到哪個(gè)具體方法上面健芭,是使用的befor县钥、after 還是around,就這個(gè)就叫advisor.
  2. 說到Spring AOP慈迈,就得談一下Bean的生命周期若贮。我們知道Bean的生命周期有實(shí)例化省有、屬性注入、初始化谴麦、銷毀蠢沿。Spring的AOP是在Bean初始化的過程中,生成代理類并返回的匾效。
    Spring AOP的源碼可以總結(jié)為以下內(nèi)容:
    1)通過AutoProxyRegistrar類將AbstractAutoProxyCreator注冊(cè)到Spring容器中
    2)AbstractAutoProxyCreator類的postProcessBeforeInstantiation()只有在自定義 TargetSource的時(shí)候才會(huì)返回代理后的Bean舷蟀,否則會(huì)繼續(xù)實(shí)例化Bean
    3)AbstractAutoProxyCreator類的postProcessAfterInitializatio會(huì)在Bean實(shí)例化之后調(diào)用,里面有個(gè)wrapIfNecessary方法獲取Advisors面哼,然后創(chuàng)建代理
           Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            } else {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }

4)調(diào)用bean方法時(shí)通過proxy來調(diào)用野宜,proxy依次調(diào)用增強(qiáng)器的相關(guān)方法,來實(shí)現(xiàn)方法切入getBean可以獲取到代理后的Bean

Spring 攔截器

Spring事務(wù)的切面實(shí)現(xiàn)是TransactionInterceptor魔策,里面的Invoke方法是實(shí)現(xiàn)了MethodInterceptor的invoke接口匈子,至于Spring的攔截器底層原理是怎么樣的呢,還需要再研究研究闯袒。


image.png

二虎敦、源碼實(shí)現(xiàn)

整體代碼流程分析

Spring事務(wù)是怎么通過AOP調(diào)用到TransactionInterceptor的invoke方法的,感興趣的可以看下這篇文章:https://my.oschina.net/u/3748038/blog/1602438政敢∑溽悖總體來說就是SpringBoot的有一個(gè)TransactionAutoConfiguration自動(dòng)配置類,自動(dòng)配置了@EnableTransactionManagement注解喷户,這個(gè)注解作用就是開啟事務(wù)擂橘。具體就是有一個(gè)AutoProxyRegistrar注冊(cè)了,初始化AbstractAutoProxyCreator,AbstractAutoProxyCreator實(shí)現(xiàn)了BeanProcessors,所以在Bean實(shí)例化前后時(shí)候分別調(diào)用
postProcessBeforeInitialization(Object bean, String beanName) -- 實(shí)例化前執(zhí)行
Object postProcessAfterInitialization(Object bean, String beanName) --實(shí)例化后執(zhí)行
取具。然后可以發(fā)現(xiàn)在postProcessAfterInitialization方法中調(diào)用了wrapIfNecessary席里,一直往下追蹤,可以看到最終會(huì)調(diào)用ransactionAttributeSource的getTransactionAttribute()方法疲憋,這里就用到了那些parser類去真正解析當(dāng)前bean是否包含那些注解,如果包含注解,那么就把當(dāng)前類包裝成代理類茎用。還有就是事務(wù)代理類在執(zhí)行方法之前調(diào)用的切面是BeanFactoryTransactionAttributeSourceAdvisor的advice,亦即TransactionInterceptor,這樣大概就能串起來了睬罗。

  1. TransactionInterceptor的invoke轨功,
    然后調(diào)用子類TransactionAspectSupport的invokeWithinTransaction方法,這里又是模板方法的意思容达。


    image.png
  2. 關(guān)鍵流程代碼都在invokeWithinTransaction方法里古涧,下面只保留了關(guān)鍵代碼
@Nullable
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
            final InvocationCallback invocation) throws Throwable {

        // If the transaction attribute is null, the method is non-transactional.
        TransactionAttributeSource tas = getTransactionAttributeSource();
        final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//獲取事務(wù)管理器,這里默認(rèn)獲取到的是DataSourceTransactionManager花盐,
//具體原因不明羡滑。菇爪。
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // 通過事務(wù)傳播隔離級(jí)別判斷是否需要開啟新事務(wù)
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

            Object retVal;
            try {
                //調(diào)用目標(biāo)方法
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // 發(fā)生unchecked異常,或者error的話柒昏,需要回滾凳宙。
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                cleanupTransactionInfo(txInfo);
            }
//提交事務(wù)
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }
    }
  1. createTransactionIfNecessary方法,判斷是否 需要開啟新事務(wù)职祷,是根據(jù)事務(wù)傳播條件判斷的氏涩,具體的判斷,在下面講有梆。這個(gè)方法主要關(guān)注getTransaction方法


    image.png

事務(wù)管理器

getTransaction中的doGetTransaction削葱、isExistingTransaction、doBegin等調(diào)用的都是DataSourceTransactionManager中的方法淳梦,事務(wù)管理器保存了dataSource析砸,dataSource中有數(shù)據(jù)庫的連接,在事務(wù)中的每一個(gè)語句的執(zhí)行爆袍,都使用這個(gè)連接執(zhí)行首繁。保證事務(wù)的執(zhí)行,總是復(fù)用同一個(gè)連接陨囊。
doBegin的代碼弦疮,忽略無關(guān)代碼

protected void doBegin(Object transaction, TransactionDefinition definition) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;

        try {
            if (!txObject.hasConnectionHolder() ||
                    txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                Connection newCon = obtainDataSource().getConnection();
                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
            }
        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
            con = txObject.getConnectionHolder().getConnection();
//保存之前的隔離級(jí)別,因?yàn)楫?dāng)前事務(wù)可能是子事務(wù)蜘醋,等當(dāng)前執(zhí)行完了胁塞,還要再執(zhí)行
            Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
            txObject.setPreviousIsolationLevel(previousIsolationLevel);

            // 設(shè)置是否自動(dòng)提交,這里的自動(dòng)提交的含義在上面說過了
            if (con.getAutoCommit()) {
                txObject.setMustRestoreAutoCommit(true);
                con.setAutoCommit(false);
            }

            prepareTransactionalConnection(con, definition);
            txObject.getConnectionHolder().setTransactionActive(true);
                      //設(shè)置超時(shí)時(shí)間
            int timeout = determineTimeout(definition);
            if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
            }

            if (txObject.isNewConnectionHolder()) {
//連接綁定到線程压语,這里也是使用的ThreadLocal
                TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
            }
        }

    }

doCommit和doRollback源碼比較簡單啸罢,這里就不開展說了。

事務(wù)傳播機(jī)制

Spring事務(wù)是在getTransaction里面判斷胎食,是否要開啟新事務(wù)扰才,判斷的主要依據(jù)是@Transactional的propagation屬性。

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)

然后我們可以看下源碼厕怜,是有以下幾種枚舉值:

public enum Propagation {

     //支持當(dāng)前事務(wù)衩匣,如果當(dāng)前沒有事務(wù),就新建一個(gè)粥航,是默認(rèn)的傳播機(jī)制
    REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

      //支持當(dāng)前事務(wù)琅捏,當(dāng)前沒有事務(wù)就非事務(wù)地執(zhí)行
    SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

     //支持當(dāng)前事務(wù),如果沒有的話柄延,就拋出異常S
    MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

     //創(chuàng)建新事務(wù)杠输,并且掛起當(dāng)前事務(wù)    
  REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

     // 非事務(wù)地執(zhí)行鹦牛,如果當(dāng)前有事務(wù)就掛起
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

     //非事務(wù)地執(zhí)行礼殊,如果當(dāng)前存在事務(wù)就拋出異常
    NEVER(TransactionDefinition.PROPAGATION_NEVER),

     // 如果當(dāng)前事務(wù)存在婚陪,就執(zhí)行嵌套事務(wù)
    NESTED(TransactionDefinition.PROPAGATION_NESTED);
}

上面的注釋都是基于源碼注解的理解沽一,接下來我們看下實(shí)際的執(zhí)行情況拙友,看看對(duì)于不同的傳播方式漾根,Spring會(huì)做哪些處理鲫竞。依然省略無關(guān)代碼

    @Override
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
//這里調(diào)用的是事務(wù)管理器的方法辐怕,獲取到當(dāng)前的事務(wù)
        Object transaction = doGetTransaction();
            //省略無關(guān)代碼........

//調(diào)用事務(wù)管理器的isExistingTransaction,判斷是否存在事務(wù)从绘,如果存在事務(wù)的話寄疏,調(diào)用handleExistingTransaction
        if (isExistingTransaction(transaction)) {
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }

    //省略無關(guān)代碼.....
        // 如果是MANDATORY,而且當(dāng)前不存在事務(wù)僵井,那么拋出異常
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }
//如果是REQUIRED陕截,或者是REQUIRES_NEW,或者是NESTED批什,那么開啟新事務(wù)
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
//省略無關(guān)代碼
        }
        else {
            // 創(chuàng)建一個(gè)空事務(wù)农曲,沒有實(shí)際的事務(wù)(創(chuàng)建這個(gè)事務(wù)的意圖不是很理解。驻债。乳规。)
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }

接下來,我們看看對(duì)于已存在事務(wù)的情況却汉,Spring是怎么處理的驯妄。

private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction, boolean debugEnabled)
            throws TransactionException {
//如果已存在事務(wù),NEVER傳播級(jí)別合砂,拋出異常
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
            throw new IllegalTransactionStateException(
                    "Existing transaction found for transaction marked with propagation 'never'");
        }
//NOT_SUPPORTED的話青扔,中止當(dāng)前事務(wù),然后準(zhǔn)備事務(wù)翩伪,這里并沒有開啟事務(wù)
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
            Object suspendedResources = suspend(transaction);
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(
                    definition, null, false, newSynchronization, debugEnabled, suspendedResources);
        }
// REQUIRES_NEW微猖,掛起當(dāng)前事務(wù),新建事務(wù)缘屹,并開啟事務(wù)
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
            SuspendedResourcesHolder suspendedResources = suspend(transaction);
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
            
        }
//NESTED隔離級(jí)別
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            if (!isNestedTransactionAllowed()) {
                throw new NestedTransactionNotSupportedException(
                        "Transaction manager does not allow nested transactions by default - " +
                        "specify 'nestedTransactionAllowed' property with value 'true'");
            }
            //設(shè)置了安全點(diǎn)凛剥,給已存在的事務(wù)創(chuàng)建安全點(diǎn),應(yīng)該是為了子事務(wù)失敗之后轻姿,父事務(wù)不回滾犁珠。
            if (useSavepointForNestedTransaction()) {
                DefaultTransactionStatus status =
                        prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
                status.createAndHoldSavepoint();
                return status;
            }
            else {
                // 否則開啟新事務(wù)
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, null);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
        }

        // SUPPORTS or REQUIRED.
        
        if (isValidateExistingTransaction()) {
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
                Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
                    Constants isoConstants = DefaultTransactionDefinition.constants;
                                    isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
                                    "(unknown)"));
                }
            }
        
        }
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
    }

看了那么多源碼,我們可以總結(jié)出以下規(guī)律


傳播機(jī)制

回滾和提交的條件

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            
            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
                //省略異常代碼
            }
            else {
                
                try {
                    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
                }
                //省略異常代碼
            }
        }
    }
    public boolean rollbackOn(Throwable ex) {
        return (ex instanceof RuntimeException || ex instanceof Error);
    }

判斷rollbackOn方法互亮,成立的時(shí)候才會(huì)回滾犁享,而對(duì)于rollbackOn,判斷的是RuntimeException和error兩種情況豹休,所以說炊昆,其他的異常都會(huì)提交事務(wù)。


異常體系

三、使用最佳實(shí)踐

private修飾方法不生效

Spring事務(wù)基于AOP實(shí)現(xiàn)凤巨,而AOP是通過JDK代理视乐,或者Cglib代理,如果是代理接口敢茁,使用的是JDK代理佑淀,否則使用CGLIB代理。對(duì)于private修飾的方法卷要,會(huì)使用Cglib渣聚,但是Cglib的原理是對(duì)指定的目標(biāo)類動(dòng)態(tài)生成一個(gè)子類独榴,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng)僧叉,但因?yàn)椴捎玫氖抢^承,所以不能對(duì)final修飾的類和final方法和private方法進(jìn)行代理棺榔。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {       
 // 1.config.isOptimize()是否使用優(yōu)化的代理策略瓶堕,目前使用與CGLIB        
// config.isProxyTargetClass() 是否目標(biāo)類本身被代理而不是目標(biāo)類的接口       
 // hasNoUserSuppliedProxyInterfaces()是否存在代理接口      
if (config.isOptimize() || config.isProxyTargetClass() ||hasNoUserSuppliedProxyInterfaces(config)) {        
    Class<?> targetClass = config.getTargetClass();         
        if (targetClass == null) {              
          throw new AopConfigException("TargetSource cannot determine target class: " +                     "Either an interface or a target is required for proxy creation.");         
              }                        
        // 2.如果目標(biāo)類是接口或者是代理類,則直接使用JDKproxy       
    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {              
          return new JdkDynamicAopProxy(config);            
            }                      
          // 3.其他情況則使用CGLIBproxy            
          return new ObjenesisCglibAopProxy(config);        
           }        
   else {           
         return new JdkDynamicAopProxy(config);     
      } 
}

方法嵌套不生效

這里直接引用極客時(shí)間的內(nèi)容~

Spring 通過 AOP 技術(shù)對(duì)方法進(jìn)行增強(qiáng)症歇,要調(diào)用增強(qiáng)過的方法必然是調(diào)用代理后的對(duì)象郎笆。我們嘗試修改下 UserService 的代碼,注入一個(gè) self忘晤,然后再通過 self 實(shí)例調(diào)用標(biāo)記有 @Transactional 注解的 createUserPublic 方法宛蚓。設(shè)置斷點(diǎn)可以看到,self 是由 Spring 通過 CGLIB 方式增強(qiáng)過的類:CGLIB 通過繼承方式實(shí)現(xiàn)代理類设塔,private 方法在子類不可見凄吏,自然也就無法進(jìn)行事務(wù)增強(qiáng);this 指針代表對(duì)象自己闰蛔,Spring 不可能注入 this痕钢,所以通過 this 訪問方法必然不是代理。


public int createUserWrong2(String name) {
    try {
        this.createUserPublic(new UserEntity(name));
    } catch (Exception ex) {
        log.error("create user failed because {}", ex.getMessage());
    }
  return userRepository.findByName(name).size();
}

//標(biāo)記了@Transactional的public方法
@Transactional
public void createUserPublic(UserEntity entity) {
    userRepository.save(entity);
    if (entity.getName().contains("test"))
        throw new RuntimeException("invalid username!");
}

傳播方式使用

異承蛄回滾的設(shè)置

通過上面源碼分析可知任连,Spring事務(wù),默認(rèn)只會(huì)對(duì)RuntimeException和error的異常進(jìn)行回滾例诀,如果想讓未受檢異常也回滾随抠,需要在注解上加上
rollbackFor = Exception.class

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市繁涂,隨后出現(xiàn)的幾起案子拱她,更是在濱河造成了極大的恐慌,老刑警劉巖爆土,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椭懊,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)氧猬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門背犯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盅抚,你說我怎么就攤上這事漠魏。” “怎么了妄均?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵柱锹,是天一觀的道長。 經(jīng)常有香客問我丰包,道長禁熏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任邑彪,我火速辦了婚禮瞧毙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寄症。我一直安慰自己宙彪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布有巧。 她就那樣靜靜地躺著释漆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篮迎。 梳的紋絲不亂的頭發(fā)上男图,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音柑潦,去河邊找鬼享言。 笑死渗鬼,一個(gè)胖子當(dāng)著我的面吹牛览露,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播譬胎,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼差牛,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了堰乔?” 一聲冷哼從身側(cè)響起偏化,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎镐侯,沒想到半個(gè)月后侦讨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年韵卤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了骗污。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沈条,死狀恐怖需忿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蜡歹,我是刑警寧澤屋厘,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站月而,受9級(jí)特大地震影響汗洒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜景鼠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一仲翎、第九天 我趴在偏房一處隱蔽的房頂上張望痹扇。 院中可真熱鬧铛漓,春花似錦、人聲如沸鲫构。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽结笨。三九已至包晰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間炕吸,已是汗流浹背伐憾。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赫模,地道東北人树肃。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像瀑罗,于是被迫代替她去往敵國和親胸嘴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • spring實(shí)現(xiàn)事務(wù)的原理 Spring事務(wù) 的本質(zhì)其實(shí)就是數(shù)據(jù)庫對(duì)事務(wù)的支持斩祭,沒有數(shù)據(jù)庫的事務(wù)支持劣像,spring...
    onlyHalfSoul閱讀 1,634評(píng)論 0 1
  • Spring同時(shí)支持編程式事務(wù)策略和聲明式事務(wù)策略,大部分時(shí)候摧玫,我們都推薦采用聲明式事務(wù)策略耳奕。使用聲明式事務(wù)策略的...
    SevenCoder閱讀 1,794評(píng)論 0 0
  • 久違的晴天,家長會(huì)。 家長大會(huì)開好到教室時(shí)屋群,離放學(xué)已經(jīng)沒多少時(shí)間了时迫。班主任說已經(jīng)安排了三個(gè)家長分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,513評(píng)論 16 22
  • 今天感恩節(jié)哎谓晌,感謝一直在我身邊的親朋好友掠拳。感恩相遇!感恩不離不棄纸肉。 中午開了第一次的黨會(huì)溺欧,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,559評(píng)論 0 11
  • 可愛進(jìn)取,孤獨(dú)成精柏肪。努力飛翔姐刁,天堂翱翔。戰(zhàn)爭美好烦味,孤獨(dú)進(jìn)取聂使。膽大飛翔,成就輝煌谬俄。努力進(jìn)取柏靶,遙望,和諧家園溃论∈候眩可愛游走...
    趙原野閱讀 2,720評(píng)論 1 1