真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Spring事務(wù)有哪些坑

這篇文章主要講解了“Spring事務(wù)有哪些坑”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Spring事務(wù)有哪些坑”吧!

上蔡網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),上蔡網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為上蔡近1000家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請(qǐng)找那個(gè)售后服務(wù)好的上蔡做網(wǎng)站的公司定做!

一、Spring事務(wù)管理的幾種方式:

Spring事務(wù)在具體使用方式上可分為兩大類:

1.  聲明式

  •  基于 TransactionProxyFactoryBean的聲明式事務(wù)管理

  •  基于 命名空間的事務(wù)管理

  •  基于 @Transactional 的聲明式事務(wù)管理

2.  編程式

  •  基于事務(wù)管理器API 的編程式事務(wù)管理

  •  基于TransactionTemplate 的編程式事務(wù)管理

目前大部分項(xiàng)目使用的是聲明式的后兩種:

  •  基于 命名空間的聲明式事務(wù)管理可以充分利用切點(diǎn)表達(dá)式的強(qiáng)大支持,使得管理事務(wù)更加靈活。

  •  基于 @Transactional 的方式需要實(shí)施事務(wù)管理的方法或者類上使用 @Transactional 指定事務(wù)規(guī)則即可實(shí)現(xiàn)事務(wù)管理,在Spring Boot中通常也建議使用這種注解方式來標(biāo)記事務(wù)。

二、Spring事務(wù)實(shí)現(xiàn)機(jī)制

接下來我們?cè)敿?xì)看下Spring事務(wù)的源代碼,進(jìn)而了解其工作原理。我們從標(biāo)簽的解析類開始:

@Override  public void init() {          registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());          registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());          registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());      }  }
class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {      @Override      protected Class getBeanClass(Element element) {          return TransactionInterceptor.class;      }  }

由此可看到Spring事務(wù)的核心實(shí)現(xiàn)類TransactionInterceptor及其父類TransactionAspectSupport,其實(shí)現(xiàn)了事務(wù)的開啟、數(shù)據(jù)庫操作、事務(wù)提交、回滾等。我們平時(shí)在開發(fā)時(shí)如果想確定是否在事務(wù)中,也可以在該方法進(jìn)行斷點(diǎn)調(diào)試。

TransactionInterceptor:

public Object invoke(final MethodInvocation invocation) throws Throwable {          Class targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);          // Adapt to TransactionAspectSupport's invokeWithinTransaction...          return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {              @Override              public Object proceedWithInvocation() throws Throwable {                  return invocation.proceed();              }          });      }

TransactionAspectSupport

protected Object invokeWithinTransaction(Method method, Class targetClass, final InvocationCallback invocation)              throws Throwable {          // If the transaction attribute is null, the method is non-transactional.          final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);          final PlatformTransactionManager tm = determineTransactionManager(txAttr);          final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);          if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {              // Standard transaction demarcation with getTransaction and commit/rollback calls.              TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);              Object retVal = null;              try {                  // This is an around advice: Invoke the next interceptor in the chain.                  // This will normally result in a target object being invoked.                  retVal = invocation.proceedWithInvocation();              }              catch (Throwable ex) {                  // target invocation exception                  completeTransactionAfterThrowing(txInfo, ex);                  throw ex;              }              finally {                  cleanupTransactionInfo(txInfo);              }              commitTransactionAfterReturning(txInfo);              return retVal;          }  }

至此我們了解事務(wù)的整個(gè)調(diào)用流程,但還有一個(gè)重要的機(jī)制沒分析到,那就是Spring 事務(wù)針對(duì)不同的傳播級(jí)別控制當(dāng)前獲取的數(shù)據(jù)庫連接。接下來我們看下Spring獲取連接的工具類DataSourceUtils,JdbcTemplate、Mybatis-Spring也都是通過該類獲取Connection。

public abstract class DataSourceUtils {  …  public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {          try {              return doGetConnection(dataSource);          }          catch (SQLException ex) {              throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);          }      }  public static Connection doGetConnection(DataSource dataSource) throws SQLException {          Assert.notNull(dataSource, "No DataSource specified");         ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);          if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {              conHolder.requested();              if (!conHolder.hasConnection()) {                  logger.debug("Fetching resumed JDBC Connection from DataSource");                  conHolder.setConnection(dataSource.getConnection());              }              return conHolder.getConnection();          }  …  }

TransactionSynchronizationManager也是一個(gè)事務(wù)同步管理的核心類,它實(shí)現(xiàn)了事務(wù)同步管理的職能,包括記錄當(dāng)前連接持有connection holder。

TransactionSynchronizationManager

private static final ThreadLocal> resources =              new NamedThreadLocal>("Transactional resources");  …  public static Object getResource(Object key) {          Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);          Object value = doGetResource(actualKey);          if (value != null && logger.isTraceEnabled()) {              logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +                      Thread.currentThread().getName() + "]");          }          return value;      }      /**       * Actually check the value of the resource that is bound for the given key.       */      private static Object doGetResource(Object actualKey) {          Map map = resources.get();          if (map == null) {              return null;          }          Object value = map.get(actualKey);          // Transparently remove ResourceHolder that was marked as void...          if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {              map.remove(actualKey);              // Remove entire ThreadLocal if empty...              if (map.isEmpty()) {                  resources.remove();              }              value = null;          }          return value;      }

在事務(wù)管理器類AbstractPlatformTransactionManager中,getTransaction獲取事務(wù)時(shí),會(huì)處理不同的事務(wù)傳播行為,例如當(dāng)前存在事務(wù),但調(diào)用方法事務(wù)傳播級(jí)別為REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED時(shí),對(duì)當(dāng)前事務(wù)進(jìn)行掛起、恢復(fù)等操作,以此保證了當(dāng)前數(shù)據(jù)庫操作獲取正確的Connection。

具體是在子事務(wù)提交的最后會(huì)將掛起的事務(wù)恢復(fù),恢復(fù)時(shí)重新調(diào)用TransactionSynchronizationManager. bindResource設(shè)置之前的connection holder,這樣再獲取的連接就是被恢復(fù)的數(shù)據(jù)庫連接, TransactionSynchronizationManager當(dāng)前激活的連接只能是一個(gè)。

AbstractPlatformTransactionManager

private TransactionStatus handleExistingTransaction(             TransactionDefinition definition, Object transaction, boolean debugEnabled)             throws TransactionException {         if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {             if (debugEnabled) {                 logger.debug("Suspending current transaction, creating new transaction with name [" +                         definition.getName() + "]");             }             SuspendedResourcesHolder suspendsuspendedResources = 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;             }             catch (RuntimeException beginEx) {                 resumeAfterBeginException(transaction, suspendedResources, beginEx);                 throw beginEx;             }             catch (Error beginErr) {                 resumeAfterBeginException(transaction, suspendedResources, beginErr);                 throw beginErr;             }         }  **      * Clean up after completion, clearing synchronization if necessary,      * and invoking doCleanupAfterCompletion.      * @param status object representing the transaction      * @see #doCleanupAfterCompletion      */     private void cleanupAfterCompletion(DefaultTransactionStatus status) {         status.setCompleted();         if (status.isNewSynchronization()) {             TransactionSynchronizationManager.clear();         }         if (status.isNewTransaction()) {             doCleanupAfterCompletion(status.getTransaction());         }         if (status.getSuspendedResources() != null) {             if (status.isDebug()) {                 logger.debug("Resuming suspended transaction after completion of inner transaction");             }             resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());         }     }

Spring的事務(wù)是通過AOP代理類中的一個(gè)Advice(TransactionInterceptor)進(jìn)行生效的,而傳播級(jí)別定義了事務(wù)與子事務(wù)獲取連接、事務(wù)提交、回滾的具體方式。

AOP(Aspect Oriented Programming),即面向切面編程。Spring AOP技術(shù)實(shí)現(xiàn)上其實(shí)就是代理類,具體可分為靜態(tài)代理和動(dòng)態(tài)代理兩大類,其中靜態(tài)代理是指使用 AOP 框架提供的命令進(jìn)行編譯,從而在編譯階段就可生成 AOP 代理類,因此也稱為編譯時(shí)增強(qiáng);(AspectJ);而動(dòng)態(tài)代理則在運(yùn)行時(shí)借助于 默寫類庫在內(nèi)存中“臨時(shí)”生成 AOP 動(dòng)態(tài)代理類,因此也被稱為運(yùn)行時(shí)增強(qiáng)。其中java是使用的動(dòng)態(tài)代理模式 (JDK+CGLIB)。

JDK動(dòng)態(tài)代理 JDK動(dòng)態(tài)代理主要涉及到j(luò)ava.lang.reflect包中的兩個(gè)類:Proxy和InvocationHandler。InvocationHandler是一個(gè)接口,通過實(shí)現(xiàn)該接口定義橫切邏輯,并通過反射機(jī)制調(diào)用目標(biāo)類的代碼,動(dòng)態(tài)將橫切邏輯和業(yè)務(wù)邏輯編制在一起。Proxy利用InvocationHandler動(dòng)態(tài)創(chuàng)建一個(gè)符合某一接口的實(shí)例,生成目標(biāo)類的代理對(duì)象。

CGLIB動(dòng)態(tài)代理 CGLIB全稱為Code Generation Library,是一個(gè)強(qiáng)大的高性能,高質(zhì)量的代碼生成類庫,可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口,CGLIB封裝了asm,可以再運(yùn)行期動(dòng)態(tài)生成新的class。和JDK動(dòng)態(tài)代理相比較:JDK創(chuàng)建代理有一個(gè)限制,就是只能為接口創(chuàng)建代理實(shí)例,而對(duì)于沒有通過接口定義業(yè)務(wù)方法的類,則可以通過CGLIB創(chuàng)建動(dòng)態(tài)代理。

CGLIB 創(chuàng)建代理的速度比較慢,但創(chuàng)建代理后運(yùn)行的速度卻非???,而 JDK 動(dòng)態(tài)代理正好相反。如果在運(yùn)行的時(shí)候不斷地用 CGLIB 去創(chuàng)建代理,系統(tǒng)的性能會(huì)大打折扣。因此如果有接口,Spring默認(rèn)使用JDK 動(dòng)態(tài)代理,源代碼如下:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {      @Override      public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {          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.");              }              if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {                  return new JdkDynamicAopProxy(config);              }              return new ObjenesisCGLIBAopProxy(config);          }             else {              return new JdkDynamicAopProxy(config);          }      }  }

在了解Spring代理的兩種特點(diǎn)后,我們也就知道在做事務(wù)切面配置時(shí)的一些注意事項(xiàng),例如JDK代理時(shí)方法必須是public,CGLIB代理時(shí)必須是public、protected,且類不能是final的;在依賴注入時(shí),如果屬性類型定義為實(shí)現(xiàn)類,JDK代理時(shí)會(huì)報(bào)如下注入異常:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.wwb.test.TxTestAop': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'stockService' is expected to be of type 'com.wwb.service.StockProcessServiceImpl' but was actually of type 'com.sun.proxy.$Proxy14'

但如果修改為CGLIB代理時(shí)則會(huì)成功注入,所以如果有接口,建議注入時(shí)該類屬性都定義為接口。另外事務(wù)切點(diǎn)都配置在實(shí)現(xiàn)類和接口都可以生效,但建議加在實(shí)現(xiàn)類上。

官網(wǎng)關(guān)于Spring AOP的詳細(xì)介紹:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html%23aop

三、Spring事務(wù)的那些坑

通過之前章節(jié),相信您已經(jīng)掌握了spring事務(wù)的使用方式與原理,不過還是要注意,因?yàn)橐徊恍⌒木涂赡芫偷艨?。首先看第一個(gè)坑:

3.1 事務(wù)不生效

測試代碼,事務(wù)AOP配置:

                                                                                                              
public class StockProcessServiceImpl implements IStockProcessService{  @Autowired       private IAccountDao accountDao;      @Autowired       private IStockDao stockDao;          @Override      public void openAccount(String aname, double money) {          accountDao.insertAccount(aname, money);      }      @Override      public void openStock(String sname, int amount) {          stockDao.insertStock(sname, amount);      }         @Override      public void openStockInAnotherDb(String sname, int amount) {          stockDao.insertStock(sname, amount);  }  }  public void insertAccount(String aname, double money) {          String sql = "insert into account(aname, balance) values(?,?)";          this.getJdbcTemplate().update(sql, aname, money);          DbUtils.printDBConnectionInfo("insertAccount",getDataSource());  }       public void insertStock(String sname, int amount) {          String sql = "insert into stock(sname, count) values (?,?)";          this.getJdbcTemplate().update(sql , sname, amount);          DbUtils.printDBConnectionInfo("insertStock",getDataSource());  }      public static void printDBConnectionInfo(String methodName,DataSource ds) {          Connection connection = DataSourceUtils.getConnection(ds);          System.out.println(methodName+" connection hashcode="+connection.hashCode());      }
//調(diào)用同類方法,外圍配置事務(wù)      public void openTx(String aname, double money) {              openAccount(aname,money);              openStock(aname,11);      }

1.運(yùn)行輸出:

insertAccount connection hashcode=319558327

insertStock connection hashcode=319558327

//調(diào)用同類方法,外圍未配置事務(wù)      public void openWithoutTx(String aname, double money) {          openAccount(aname,money);              openStock(aname,11);      }

2.運(yùn)行輸出:

insertAccount connection hashcode=1333810223

insertStock connection hashcode=1623009085

//通過AopContext.currentProxy()方法獲取代理  @Override  public void openWithMultiTx(String aname, double money) {  openAccount(aname,money);    openStockInAnotherDb(aname, 11);//傳播級(jí)別為REQUIRES_NEW  }

3.運(yùn)行輸出:

insertAccount connection hashcode=303240439

insertStock connection hashcode=303240439

可以看到2、3測試方法跟我們事務(wù)預(yù)期并一樣,結(jié)論:調(diào)用方法未配置事務(wù)、本類方法直接調(diào)用,事務(wù)都不生效!

究其原因,還是因?yàn)镾pring的事務(wù)本質(zhì)上是個(gè)代理類,而本類方法直接調(diào)用時(shí)其對(duì)象本身并不是織入事務(wù)的代理,所以事務(wù)切面并未生效。具體可以參見#Spring事務(wù)實(shí)現(xiàn)機(jī)制#章節(jié)。

Spring也提供了判斷是否為代理的方法:

public static void printProxyInfo(Object bean) {          System.out.println("isAopProxy"+AopUtils.isAopProxy(bean));          System.out.println("isCGLIBProxy="+AopUtils.isCGLIBProxy(bean));          System.out.println("isJdkProxy="+AopUtils.isJdkDynamicProxy(bean));      }

那如何修改為代理類調(diào)用呢?最直接的想法是注入自身,代碼如下:

    @Autowired      private IStockProcessService stockProcessService;  //注入自身類,循環(huán)依賴,親測可以       public void openTx(String aname, double money) {              stockProcessService.openAccount(aname,money);              stockProcessService.openStockInAnotherDb (aname,11);     }

當(dāng)然Spring提供了獲取當(dāng)前代理的方法:代碼如下:

//通過AopContext.currentProxy()方法獲取代理      @Override      public void openWithMultiTx(String aname, double money) {  ((IStockProcessService)AopContext.currentProxy()).openAccount(aname,money);  ((IStockProcessService)AopContext.currentProxy()).openStockInAnotherDb(aname, 11);      }

另外Spring是通過TransactionSynchronizationManager類中線程變量來獲取事務(wù)中數(shù)據(jù)庫連接,所以如果是多線程調(diào)用或者繞過Spring獲取數(shù)據(jù)庫連接,都會(huì)導(dǎo)致Spring事務(wù)配置失效。

最后Spring事務(wù)配置失效的場景:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  事務(wù)切面未配置正確

  3.  本類方法調(diào)用

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

  5.  繞開Spring獲取數(shù)據(jù)庫連接

接下來我們看下Spring的事務(wù)的另外一個(gè)坑:

3.2 事務(wù)不回滾

測試代碼:

                                                  
public void buyStock(String aname, double money, String sname, int amount) throws StockException {          boolean isBuy = true;          accountDao.updateAccount(aname, money, isBuy);          // 故意拋出異常          if (true) {              throw new StockException("購買股票異常");          }          stockDao.updateStock(sname, amount, isBuy);      }
@Test     public void testBuyStock() {         try {             service.openAccount("dcbs", 10000);             service.buyStock("dcbs", 2000, "dap", 5);         } catch (StockException e) {             e.printStackTrace();         }         double accountBalance = service.queryAccountBalance("dcbs");         System.out.println("account balance is " + accountBalance);     }

輸出結(jié)果:

insertAccount connection hashcode=656479172

updateAccount connection hashcode=517355658

account balance is 8000.0

應(yīng)用拋出異常,但accountDao.updateAccount卻進(jìn)行了提交。究其原因,直接看Spring源代碼:

TransactionAspectSupport

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {          if (txInfo != null && txInfo.hasTransaction()) {              if (logger.isTraceEnabled()) {                  logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +                          "] after exception: " + ex);              }              if (txInfo.transactionAttribute.rollbackOn(ex)) {                  try {                      txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());                  }                  catch (TransactionSystemException ex2) {                      logger.error("Application exception overridden by rollback exception", ex);                      ex2.initApplicationException(ex);                      throw ex2;                  }                  …  }  public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {  @Override      public boolean rollbackOn(Throwable ex) {          return (ex instanceof RuntimeException || ex instanceof Error);      }  … }

由代碼可見,Spring事務(wù)默認(rèn)只對(duì)RuntimeException和Error進(jìn)行回滾,如果應(yīng)用需要對(duì)指定的異常類進(jìn)行回滾,可配置rollback-for=屬性,例如: 

                                                      

事務(wù)不回滾的原因:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  事務(wù)配置切面未生效

  3.  應(yīng)用方法中將異常捕獲

  4.  拋出的異常不屬于運(yùn)行時(shí)異常(例如IOException),

  5.  rollback-for屬性配置不正確

接下來我們看下Spring事務(wù)的第三個(gè)坑:

3.3 事務(wù)超時(shí)不生效

測試代碼:

                                               
@Override      public void openAccountForLongTime(String aname, double money) {          accountDao.insertAccount(aname, money);          try {              Thread.sleep(5000L);//在數(shù)據(jù)庫操作之后超時(shí)          } catch (InterruptedException e) {              e.printStackTrace();          }      }
@Test  public void testTimeout() {      service.openAccountForLongTime("dcbs", 10000);  }

正常運(yùn)行,事務(wù)超時(shí)未生效

public void openAccountForLongTime(String aname, double money) {          try {              Thread.sleep(5000L); //在數(shù)據(jù)庫操作之前超時(shí)          } catch (InterruptedException e) {              e.printStackTrace();          }          accountDao.insertAccount(aname, money);      }

拋出事務(wù)超時(shí)異常,超時(shí)生效

org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Fri Nov 23 17:03:02 CST 2018

at org.springframework.transaction.support.ResourceHolderSupport.checkTransactionTimeout(ResourceHolderSupport.java:141)

通過源碼看看Spring事務(wù)超時(shí)的判斷機(jī)制:

ResourceHolderSupport

/**       * Return the time to live for this object in milliseconds.       * @return number of millseconds until expiration       * @throws TransactionTimedOutException if the deadline has already been reached       */      public long getTimeToLiveInMillis() throws TransactionTimedOutException{          if (this.deadline == null) {              throw new IllegalStateException("No timeout specified for this resource holder");          }          long timeToLive = this.deadline.getTime() - System.currentTimeMillis();          checkTransactionTimeout(timeToLive <= 0);          return timeToLive;      }      /**       * Set the transaction rollback-only if the deadline has been reached,       * and throw a TransactionTimedOutException.       */      private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {          if (deadlineReached) {              setRollbackOnly();              throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);          }      }

通過查看getTimeToLiveInMillis方法的Call Hierarchy,可以看到被DataSourceUtils的applyTimeout所調(diào)用, 繼續(xù)看applyTimeout的Call Hierarchy,可以看到有兩處調(diào)用,一個(gè)是JdbcTemplate,一個(gè)是TransactionAwareInvocationHandler類,后者是只有TransactionAwareDataSourceProxy類調(diào)用,該類為DataSource的事務(wù)代理類,我們一般并不會(huì)用到。難道超時(shí)只能在這調(diào)用JdbcTemplate中生效?寫代碼親測:

                               
public void openAccountForLongTimeWithoutJdbcTemplate(String aname, double money) {         try {             Thread.sleep(5000L);         } catch (InterruptedException e) {             e.printStackTrace();         }         accountDao.queryAccountBalanceWithoutJdbcTemplate(aname);     }     public double queryAccountBalanceWithoutJdbcTemplate(String aname) {            String sql = "select balance from account where aname = ?";            PreparedStatement prepareStatement;         try {             prepareStatement = this.getConnection().prepareStatement(sql);                prepareStatement.setString(1, aname);                ResultSet executeQuery = prepareStatement.executeQuery();                while(executeQuery.next()) {                    return executeQuery.getDouble(1);                }         } catch (CannotGetJdbcConnectionException | SQLException e) {             // TODO Auto-generated catch block             e.printStackTrace();         }         return 0;     }

運(yùn)行正常,事務(wù)超時(shí)失效

由上可見:Spring事務(wù)超時(shí)判斷在通過JdbcTemplate的數(shù)據(jù)庫操作時(shí),所以如果超時(shí)后未有JdbcTemplate方法調(diào)用,則無法準(zhǔn)確判斷超時(shí)。另外也可以得知,如果通過Mybatis等操作數(shù)據(jù)庫,Spring的事務(wù)超時(shí)是無效的。鑒于此,Spring的事務(wù)超時(shí)謹(jǐn)慎使用。

四、 總結(jié)

JDBC規(guī)范中Connection 的setAutoCommit是原生控制手動(dòng)事務(wù)的方法,但傳播行為、異常回滾、連接管理等很多技術(shù)問題都需要開發(fā)者自己處理,而Spring事務(wù)通過AOP方式非常優(yōu)雅的屏蔽了這些技術(shù)復(fù)雜度,使得事務(wù)管理變的異常簡單。

但凡事有利弊,如果對(duì)實(shí)現(xiàn)機(jī)制理解不透徹,很容易掉坑里。最后總結(jié)下Spring事務(wù)的可能踩的坑:

1.  Spring事務(wù)未生效

  •  調(diào)用方法本身未正確配置事務(wù)

  •  本類方法直接調(diào)用

  •  數(shù)據(jù)庫操作未通過Spring的DataSourceUtils獲取Connection

  •  多線程調(diào)用

2.  Spring事務(wù)回滾失效

  •  未準(zhǔn)確配置rollback-for屬性

  •  異常類不屬于RuntimeException與Error

  •  應(yīng)用捕獲了異常未拋出

3.  Spring事務(wù)超時(shí)不準(zhǔn)確或失效

  •  超時(shí)發(fā)生在最后一次JdbcTemplate操作之后

  •  通過非JdbcTemplate操作數(shù)據(jù)庫,例如Mybatis 

感謝各位的閱讀,以上就是“Spring事務(wù)有哪些坑”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Spring事務(wù)有哪些坑這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!


文章標(biāo)題:Spring事務(wù)有哪些坑
網(wǎng)頁路徑:http://weahome.cn/article/jsddjs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部