返回顶部
首页 > 资讯 > 精选 >Spring事务有哪些坑
  • 881
分享到

Spring事务有哪些坑

2023-06-15 22:06:23 881人浏览 薄情痞子
摘要

这篇文章主要讲解了“spring事务有哪些坑”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring事务有哪些坑”吧!一、Spring事务管理的几种方式:Spring事务在具体使用方式上可

这篇文章主要讲解了“spring事务有哪些坑”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring事务有哪些坑”吧!

一、Spring事务管理的几种方式:

Spring事务在具体使用方式上可分为两大类:

1.  声明式

  •  基于 TransactionProxyFactoryBean的声明式事务管理

  •  基于 <tx> 和 <aop> 命名空间的事务管理

  •  基于 @Transactional 的声明式事务管理

2.  编程式

  •  基于事务管理器api编程式事务管理

  •  基于TransactionTemplate 的编程式事务管理

目前大部分项目使用的是声明式的后两种:

  •  基于 <tx> 和 <aop> 命名空间的声明式事务管理可以充分利用切点表达式的强大支持,使得管理事务更加灵活。

  •  基于 @Transactional 的方式需要实施事务管理的方法或者类上使用 @Transactional 指定事务规则即可实现事务管理,在Spring Boot中通常也建议使用这种注解方式来标记事务。

二、Spring事务实现机制

接下来我们详细看下Spring事务的源代码,进而了解其工作原理。我们从<tx>标签的解析类开始:

@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事务的核心实现类TransactionInterceptor及其父类TransactionAspectSupport,其实现了事务的开启、数据库操作、事务提交、回滚等。我们平时在开发时如果想确定是否在事务中,也可以在该方法进行断点调试。

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;          }  }

至此我们了解事务的整个调用流程,但还有一个重要的机制没分析到,那就是Spring 事务针对不同的传播级别控制当前获取的数据库连接。接下来我们看下Spring获取连接的工具类DataSourceUtils,JdbcTemplate、mybatis-Spring也都是通过该类获取Connection。

public abstract class DataSourceUtils {  &hellip;  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();          }  &hellip;  }

TransactionSynchronizationManager也是一个事务同步管理的核心类,它实现了事务同步管理的职能,包括记录当前连接持有connection holder。

TransactionSynchronizationManager

private static final ThreadLocal<Map<Object, Object>> resources =              new NamedThreadLocal<Map<Object, Object>>("Transactional resources");  &hellip;  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;      }            private static Object doGetResource(Object actualKey) {          Map<Object, Object> 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;      }

在事务管理器类AbstractPlatformTransactionManager中,getTransaction获取事务时,会处理不同的事务传播行为,例如当前存在事务,但调用方法事务传播级别为REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED时,对当前事务进行挂起、恢复等操作,以此保证了当前数据库操作获取正确的Connection。

具体是在子事务提交的最后会将挂起的事务恢复,恢复时重新调用TransactionSynchronizationManager. bindResource设置之前的connection holder,这样再获取的连接就是被恢复的数据库连接, TransactionSynchronizationManager当前激活的连接只能是一个。

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的事务是通过AOP代理类中的一个Advice(TransactionInterceptor)进行生效的,而传播级别定义了事务与子事务获取连接、事务提交、回滚的具体方式。

AOP(Aspect Oriented Programming),即面向切面编程。Spring AOP技术实现上其实就是代理类,具体可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;(AspectJ);而动态代理则在运行时借助于 默写类库在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。其中java是使用的动态代理模式 (jdk+CGLIB)。

JDK动态代理 JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

CGLIB动态代理 CGLIB全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLIB封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLIB创建动态代理。

CGLIB 创建代理的速度比较慢,但创建代理后运行的速度却非常快,而 JDK 动态代理正好相反。如果在运行的时候不断地用 CGLIB 去创建代理,系统的性能会大打折扣。因此如果有接口,Spring默认使用JDK 动态代理,源代码如下:

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代理的两种特点后,我们也就知道在做事务切面配置时的一些注意事项,例如JDK代理时方法必须是public,CGLIB代理时必须是public、protected,且类不能是final的;在依赖注入时,如果属性类型定义为实现类,JDK代理时会报如下注入异常:

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代理时则会成功注入,所以如果有接口,建议注入时该类属性都定义为接口。另外事务切点都配置在实现类和接口都可以生效,但建议加在实现类上。

官网关于Spring AOP的详细介绍:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html%23aop

三、Spring事务的那些坑

通过之前章节,相信您已经掌握了spring事务的使用方式与原理,不过还是要注意,因为一不小心就可能就掉坑。首先看第一个坑:

3.1 事务不生效

测试代码,事务AOP配置:

<tx:advice id="txAdvice" transaction-manager="myTxManager">          <tx:attributes>              <!-- 指定在连接点方法上应用的事务属性 -->              <tx:method name="openAccount" isolation="DEFAULT" propagation="REQUIRED"/>              <tx:method name="openStock" isolation="DEFAULT" propagation="REQUIRED"/>              <tx:method name="openStockInAnotherDb" isolation="DEFAULT" propagation="REQUIRES_NEW"/>              <tx:method name="openTx" isolation="DEFAULT" propagation="REQUIRED"/>              <tx:method name="openWithoutTx" isolation="DEFAULT" propagation="NEVER"/>              <tx:method name="openWithMultiTx" isolation="DEFAULT" propagation="REQUIRED"/>  </tx:advice>
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());      }
//调用同类方法,外围配置事务      public void openTx(String aname, double money) {              openAccount(aname,money);              openStock(aname,11);      }

1.运行输出:

insertAccount connection hashcode=319558327

insertStock connection hashcode=319558327

//调用同类方法,外围未配置事务      public void openWithoutTx(String aname, double money) {          openAccount(aname,money);              openStock(aname,11);      }

2.运行输出:

insertAccount connection hashcode=1333810223

insertStock connection hashcode=1623009085

//通过AopContext.currentProxy()方法获取代理  @Override  public void openWithMultiTx(String aname, double money) {  openAccount(aname,money);    openStockInAnotherDb(aname, 11);//传播级别为REQUIRES_NEW  }

3.运行输出:

insertAccount connection hashcode=303240439

insertStock connection hashcode=303240439

可以看到2、3测试方法跟我们事务预期并一样,结论:调用方法未配置事务、本类方法直接调用,事务都不生效!

究其原因,还是因为Spring的事务本质上是个代理类,而本类方法直接调用时其对象本身并不是织入事务的代理,所以事务切面并未生效。具体可以参见#Spring事务实现机制#章节。

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));      }

那如何修改为代理类调用呢?最直接的想法是注入自身,代码如下:

    @Autowired      private IStockProcessService stockProcessService;  //注入自身类,循环依赖,亲测可以       public void openTx(String aname, double money) {              stockProcessService.openAccount(aname,money);              stockProcessService.openStockInAnotherDb (aname,11);     }

当然Spring提供了获取当前代理的方法:代码如下:

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

另外Spring是通过TransactionSynchronizationManager类中线程变量来获取事务中数据库连接,所以如果是多线程调用或者绕过Spring获取数据库连接,都会导致Spring事务配置失效。

最后Spring事务配置失效的场景:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2.  事务切面未配置正确

  3.  本类方法调用

  4.  多线程调用

  5.  绕开Spring获取数据库连接

接下来我们看下Spring的事务的另外一个坑:

3.2 事务不回滚

测试代码:

<tx:advice id="txAdvice" transaction-manager="myTxManager">          <tx:attributes>              <!-- 指定在连接点方法上应用的事务属性 -->              <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED"/>          </tx:attributes>  </tx:advice>
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);     }

输出结果:

insertAccount connection hashcode=656479172

updateAccount connection hashcode=517355658

account balance is 8000.0

应用抛出异常,但accountDao.updateAccount却进行了提交。究其原因,直接看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;                  }                  &hellip;  }  public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {  @Override      public boolean rollbackOn(Throwable ex) {          return (ex instanceof RuntimeException || ex instanceof Error);      }  &hellip; }

由代码可见,Spring事务默认只对RuntimeException和Error进行回滚,如果应用需要对指定的异常类进行回滚,可配置rollback-for=属性,例如: 

<!-- 注册事务通知 -->     <tx:advice id="txAdvice" transaction-manager="myTxManager">         <tx:attributes>             <!-- 指定在连接点方法上应用的事务属性 -->             <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback-for="StockException"/>         </tx:attributes>     </tx:advice>

事务不回滚的原因:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2.  事务配置切面未生效

  3.  应用方法中将异常捕获

  4.  抛出的异常不属于运行时异常(例如IOException),

  5.  rollback-for属性配置不正确

接下来我们看下Spring事务的第三个坑:

3.3 事务超时不生效

测试代码:

<!-- 注册事务通知 -->      <tx:advice id="txAdvice" transaction-manager="myTxManager">          <tx:attributes>               <tx:method name="openAccountForLongTime" isolation="DEFAULT" propagation="REQUIRED" timeout="3"/>          </tx:attributes>      </tx:advice>
@Override      public void openAccountForLongTime(String aname, double money) {          accountDao.insertAccount(aname, money);          try {              Thread.sleep(5000L);//在数据库操作之后超时          } catch (InterruptedException e) {              e.printStackTrace();          }      }
@Test  public void testTimeout() {      service.openAccountForLongTime("dcbs", 10000);  }

正常运行,事务超时未生效

public void openAccountForLongTime(String aname, double money) {          try {              Thread.sleep(5000L); //在数据库操作之前超时          } catch (InterruptedException e) {              e.printStackTrace();          }          accountDao.insertAccount(aname, money);      }

抛出事务超时异常,超时生效

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)

&hellip;

通过源码看看Spring事务超时的判断机制:

ResourceHolderSupport

      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;      }            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所调用, 继续看applyTimeout的Call Hierarchy,可以看到有两处调用,一个是JdbcTemplate,一个是TransactionAwareInvocationHandler类,后者是只有TransactionAwareDataSourceProxy类调用,该类为DataSource的事务代理类,我们一般并不会用到。难道超时只能在这调用JdbcTemplate中生效?写代码亲测:

<!-- 注册事务通知 -->   <tx:advice id="txAdvice" transaction-manager="myTxManager">       <tx:attributes>           <tx:method name="openAccountForLongTimeWithoutJdbcTemplate" isolation="DEFAULT" propagation="REQUIRED" timeout="3"/>       </tx:attributes>   </tx:advice>
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;     }

运行正常,事务超时失效

由上可见:Spring事务超时判断在通过JdbcTemplate的数据库操作时,所以如果超时后未有JdbcTemplate方法调用,则无法准确判断超时。另外也可以得知,如果通过Mybatis等操作数据库,Spring的事务超时是无效的。鉴于此,Spring的事务超时谨慎使用。

四、 总结

JDBC规范中Connection 的setAutoCommit是原生控制手动事务的方法,但传播行为、异常回滚、连接管理等很多技术问题都需要开发者自己处理,而Spring事务通过AOP方式非常优雅的屏蔽了这些技术复杂度,使得事务管理变的异常简单。

但凡事有利弊,如果对实现机制理解不透彻,很容易掉坑里。最后总结下Spring事务的可能踩的坑:

1.  Spring事务未生效

  •  调用方法本身未正确配置事务

  •  本类方法直接调用

  •  数据库操作未通过Spring的DataSourceUtils获取Connection

  •  多线程调用

2.  Spring事务回滚失效

  •  未准确配置rollback-for属性

  •  异常类不属于RuntimeException与Error

  •  应用捕获了异常未抛出

3.  Spring事务超时不准确或失效

  •  超时发生在最后一次JdbcTemplate操作之后

  •  通过非JdbcTemplate操作数据库,例如Mybatis 

感谢各位的阅读,以上就是“Spring事务有哪些坑”的内容了,经过本文的学习后,相信大家对Spring事务有哪些坑这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: Spring事务有哪些坑

本文链接: https://lsjlt.com/news/281786.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Spring事务有哪些坑
    这篇文章主要讲解了“Spring事务有哪些坑”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring事务有哪些坑”吧!一、Spring事务管理的几种方式:Spring事务在具体使用方式上可...
    99+
    2023-06-15
  • spring事务的注意事项有哪些
    spring事务的注意事项有哪些,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。  事务一般是指数据库事务,是指作为一个程序执行单元执行的一...
    99+
    2024-04-02
  • Spring事务的知识点有哪些
    这篇文章主要介绍“Spring事务的知识点有哪些”,在日常操作中,相信很多人在Spring事务的知识点有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring事务的知识点有哪些”的疑惑有所帮助!接下来...
    99+
    2023-06-02
  • Spring事务的传播特性有哪些
    本文小编为大家详细介绍“Spring事务的传播特性有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“Spring事务的传播特性有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识...
    99+
    2024-04-02
  • spring事务实现的方式有哪些
    Spring事务的实现方式有以下几种:1. 基于注解的方式:使用@Transactional注解来标识需要被事务管理的方法或类。可以...
    99+
    2023-09-28
    spring
  • spring事务管理的优点有哪些
    1. 原子性:Spring事务管理确保在事务范围内的所有操作要么全部成功要么全部失败,保证数据的一致性。2. 一致性:Spring事...
    99+
    2023-09-29
    spring
  • Spring事务的失效场景有哪些
    本篇内容主要讲解“Spring事务的失效场景有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring事务的失效场景有哪些”吧!1、Spring事务最终依赖的数据库的事务,如果用的是mys...
    99+
    2023-06-29
  • spring的事务传播属性有哪些
    在Spring中,事务传播属性定义了一个方法的事务边界如何与其他方法的事务边界相互关联。Spring提供了以下的事务传播属性:1. ...
    99+
    2023-09-13
    spring
  • Spring事务失效的场景有哪些
    本篇内容主要讲解“Spring事务失效的场景有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring事务失效的场景有哪些”吧!概述Spring针对Java Transaction API...
    99+
    2023-07-05
  • spring事务传播的级别有哪些
    在Spring中,事务传播行为定义了方法如何参与现有的事务或创建新事务。Spring框架提供了多种事务传播级别,常见的有以下几种: ...
    99+
    2024-04-02
  • spring中事务失效的场景有哪些
    在Spring中,事务可能失效的场景包括:1. 方法未标记为事务:如果一个方法没有被@Transactional注解标记,那么Spr...
    99+
    2023-09-28
    spring
  • Spring中事务管理的方式有哪些
    Spring中事务管理的方式有以下几种:1. 编程式事务管理:通过编写代码来管理事务,需要手动开启、提交、回滚事务。可以使用`Pla...
    99+
    2023-09-29
    Spring
  • spring事务注解失效的原因有哪些
    以下是一些可能导致Spring事务注解失效的原因:1. 配置问题:可能是由于配置文件的错误或不完整,导致Spring无法正确解析和处...
    99+
    2023-08-23
    spring
  • Spring事务回滚失败的原因有哪些
    Spring事务回滚失败的原因有以下几种情况: 事务配置错误:可能是因为事务管理器配置不正确,或者在事务方法上没有正确标注@Tr...
    99+
    2024-03-01
    Spring
  • 基于Spring中的事务@Transactional知识点有哪些
    本篇内容介绍了“基于Spring中的事务@Transactional知识点有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!ACID,事务...
    99+
    2023-06-25
  • 有哪些C++模板坑
    这篇文章主要介绍“有哪些C++模板坑”,在日常操作中,相信很多人在有哪些C++模板坑问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”有哪些C++模板坑”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!问题复现头...
    99+
    2023-06-16
  • Java CPP的坑有哪些
    这篇文章主要讲解了“Java CPP的坑有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java CPP的坑有哪些”吧!1.分清楚System.load与System.loadLibra...
    99+
    2023-06-04
  • 使用Python坑有哪些
    本篇内容介绍了“使用Python坑有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!缩进,符号和空格不正确写代码时大家会使用缩进、对齐、空...
    99+
    2023-06-02
  • Spring的事件机制知识点有哪些
    这篇文章主要讲解了“Spring的事件机制知识点有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring的事件机制知识点有哪些”吧!同步事件和异步事件同步事件: 在一个线程里,按顺序...
    99+
    2023-06-22
  • Spring事务回滚使用要注意哪些事项
    在Spring中,事务回滚是非常重要的,可以确保数据的一致性和完整性。以下是使用Spring事务回滚时需要注意的事项: 使用@T...
    99+
    2024-03-01
    Spring
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作