精华内容
下载资源
问答
  • 事务回滚

    千次阅读 2012-05-25 16:12:27
    框架里面用了Spring的AOP处理事务,事务针对Biz级别来做,而异常统一都用自定义的RuntimeException。...改为向外抛RuntimeException后,事务回滚了。 Google之,发现原因如下: Spring的AOP事务管理默认是针对unchec

    框架里面用了Spring的AOP处理事务,事务针对Biz级别来做,而异常统一都用自定义的RuntimeException。

    那段Biz中的代码没用RuntimeException,而是try catch  再向外抛了Exceition,结果就没有回滚。改为向外抛RuntimeException后,事务回滚了。

    Google之,发现原因如下:

    Spring的AOP事务管理默认是针对unchecked exception回滚。

    Exception作为基类,下面还分checked exception和unchecked exception。如果客户端可以通过其他的方法恢复异常,那么这种异常就是checked exception;如果客户端对出现的这种异常无能为力,那么这种异常就是Unchecked exception;简单来说,继承于RuntimeException的都是unchecked exception。

    Error:
    1.总是不可控制的(unchecked) 
    2.经常用来用于表示系统错误或低层资源的错误 
    3.如何可能的话,应该在系统级被捕捉 

    Exception:
    1.可以是可被控制(checked) 或不可控制的(unchecked) 
    2.表示一个由程序员导致的错误 
    3.应该在应用程序级被处理 

    Java 中定义了两类异常: 
    1) Checked exception: 这类异常都是Exception的子类 。异常的向上抛出机制进行处理,假如子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。
    2) Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是非凡的,它们 不能通过client code来试图解决,所以称为Unchecked exception 。

     

    解决办法:

    1.在针对事务的类中抛出RuntimeException异常,而不是抛出Exception。

    2.在txAdive中增加rollback-for,里面写自己的exception,例如自己写的exception为com.abc.b2e.base.exception.***Exception

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
    <tx:method name="*" rollback-for="com.abc.b2e.base.exception.***Exception"/>
    </tx:attributes>
    </tx:advice>

    这样,凡是这个指定Exception抛出来的,都会回滚。

    ------------------------------------------------------------------------

    spring管理下的hibernate默认是对Exception不回滚的,只对RuntimeException回滚 
    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">  
    <property name="transactionManager" ref="transactionManager" />  
    <property name="transactionAttributes">  
    <props>  
    <prop key="*">PROPAGATION_REQUIRED,-Exception </prop>  
    </props>  
    </property>  
    </bean>



    在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。

    Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked

    如果遇到checked意外就不回滚。

    如何改变默认规则:

    1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)

    2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

    3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

    在整个方法运行前就不会开启事务

           还可以加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成一个只读事务,可以提高效率。

           各种属性的意义:

           REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。

           NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。

           REQUIRESNEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。

           MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。

           SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。

           NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。

           NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

    事务陷阱-1

    清单 1. 使用 JDBC 的简单数据库插入

    view plaincopy to clipboardprint?
    @Stateless 
    public class TradingServiceImpl implements TradingService {   
       @Resource SessionContext ctx;   
       @Resource(mappedName="java:jdbc/tradingDS") DataSource ds;  

       public long insertTrade(TradeData trade) throws Exception {   
          Connection dbConnection = ds.getConnection();   
          try {   
             Statement sql = dbConnection.createStatement();   
             String stmt =   
                "INSERT INTO TRADE (ACCT_ID, SIDE, SYMBOL, SHARES, PRICE, STATE)" 
              + "VALUES (" 
              + trade.getAcct() + "','" 
              + trade.getAction() + "','" 
              + trade.getSymbol() + "'," 
              + trade.getShares() + "," 
              + trade.getPrice() + ",'" 
              + trade.getState() + "')";   
             sql.executeUpdate(stmt, Statement.RETURN_GENERATED_KEYS);   
             ResultSet rs = sql.getGeneratedKeys();   
             if (rs.next()) {   
                return rs.getBigDecimal(1).longValue();   
             } else {   
                throw new Exception("Trade Order Insert Failed");   
             }   
          } finally {   
             if (dbConnection != null) dbConnection.close();   
          }   
       }   

    @Stateless
    public class TradingServiceImpl implements TradingService {
       @Resource SessionContext ctx;
       @Resource(mappedName="java:jdbc/tradingDS") DataSource ds;
    public long insertTrade(TradeData trade) throws Exception {
          Connection dbConnection = ds.getConnection();
          try {
             Statement sql = dbConnection.createStatement();
             String stmt =
                "INSERT INTO TRADE (ACCT_ID, SIDE, SYMBOL, SHARES, PRICE, STATE)"
              + "VALUES ("
              + trade.getAcct() + "','"
              + trade.getAction() + "','"
              + trade.getSymbol() + "',"
              + trade.getShares() + ","
              + trade.getPrice() + ",'"
              + trade.getState() + "')";
             sql.executeUpdate(stmt, Statement.RETURN_GENERATED_KEYS);
             ResultSet rs = sql.getGeneratedKeys();
             if (rs.next()) {
                return rs.getBigDecimal(1).longValue();
             } else {
                throw new Exception("Trade Order Insert Failed");
             }
          } finally {
             if (dbConnection != null) dbConnection.close();
          }
       }
    }

    清单 1 中的 JDBC 代码没有包含任何事务逻辑,它只是在数据库中保存 TRADE 表中的交易订单。在本例中,数据库处理事务逻辑。

    在 LUW 中,这是一个不错的单个数据库维护操作。但是如果需要在向数据库插入交易订单的同时更新帐户余款呢?如清单 2 所示:


    清单 2. 在同一方法中执行多次表更新

    view plaincopy to clipboardprint?
    public TradeData placeTrade(TradeData trade) throws Exception {   
       try {   
          insertTrade(trade);   
          updateAcct(trade);   
          return trade;   
       } catch (Exception up) {   
          //log the error   
          throw up;   
       }   

    public TradeData placeTrade(TradeData trade) throws Exception {
       try {
          insertTrade(trade);
          updateAcct(trade);
          return trade;
       } catch (Exception up) {
          //log the error
          throw up;
       }
    }

    在本例中,insertTrade() 和 updateAcct() 方法使用不带事务的标准 JDBC 代码。insertTrade() 方法结束后,数据库保存(并提交了)交易订单。如果 updateAcct() 方法由于任意原因失败,交易订单仍然会在 placeTrade() 方法结束时保存在 TRADE 表内,这会导致数据库出现不一致的数据。如果 placeTrade() 方法使用了事务,这两个活动都会包含在一个 LUW 中,如果帐户更新失败,交易订单就会回滚。

    事务陷阱-2

    随着 Java 持久性框架的不断普及,如 Hibernate、TopLink 和 Java 持久性 API(Java Persistence API,JPA),我们很少再会去编写简单的 JDBC 代码。更常见的情况是,我们使用更新的对象关系映射(ORM)框架来减轻工作,即用几个简单的方法调用替换所有麻烦的 JDBC 代码。例如,要插入 清单 1 中 JDBC 代码示例的交易订单,使用带有 JPA 的 Spring Framework,就可以将 TradeData 对象映射到 TRADE 表,并用清单 3 中的 JPA 代码替换所有 JDBC 代码:


    清单 3. 使用 JPA 的简单插入

    view plaincopy to clipboardprint?
    public class TradingServiceImpl {   
        @PersistenceContext(unitName="trading") EntityManager em;  

        public long insertTrade(TradeData trade) throws Exception {   
           em.persist(trade);   
           return trade.getTradeId();   
        }   

    public class TradingServiceImpl {
        @PersistenceContext(unitName="trading") EntityManager em;

        public long insertTrade(TradeData trade) throws Exception {
           em.persist(trade);
           return trade.getTradeId();
        }
    }
    注意,清单 3 在 EntityManager 上调用了 persist() 方法来插入交易订单。很简单,是吧?其实不然。这段代码不会像预期那样向 TRADE 表插入交易订单,也不会抛出异常。它只是返回一个值 0 作为交易订单的键,而不会更改数据库。这是事务处理的主要陷阱之一:基于 ORM 的框架需要一个事务来触发对象缓存与数据库之间的同步。这通过一个事务提交完成,其中会生成 SQL 代码,数据库会执行需要的操作(即插入、更新、删除)。没有事务,就不会触发 ORM 去生成 SQL 代码和保存更改,因此只会终止方法 — 没有异常,没有更新。如果使用基于 ORM 的框架,就必须利用事务。您不再依赖数据库来管理连接和提交工作。

    这些简单的示例应该清楚地说明,为了维护数据完整性和一致性,必须使用事务。不过对于在 Java 平台中实现事务的复杂性和陷阱而言,这些示例只是涉及了冰山一角。

    Spring Framework @Transactional 注释陷阱-3

    清单 4. 使用 @Transactional 注释

    view plaincopy to clipboardprint?
    public class TradingServiceImpl {   
       @PersistenceContext(unitName="trading") EntityManager em;  

       @Transactional 
       public long insertTrade(TradeData trade) throws Exception {   
          em.persist(trade);   
          return trade.getTradeId();   
       }   

    public class TradingServiceImpl {
       @PersistenceContext(unitName="trading") EntityManager em;

       @Transactional
       public long insertTrade(TradeData trade) throws Exception {
          em.persist(trade);
          return trade.getTradeId();
       }
    }

    现在重新测试代码,您发现上述方法仍然不能工作。问题在于您必须告诉 Spring Framework,您正在对事务管理应用注释。除非您进行充分的单元测试,否则有时候很难发现这个陷阱。这通常只会导致开发人员在 Spring 配置文件中简单地添加事务逻辑,而不会使用注释。

    要在 Spring 中使用 @Transactional 注释,必须在 Spring 配置文件中添加以下代码行:

    view plaincopy to clipboardprint?
    <tx:annotation-driven transaction-manager="transactionManager"/> 
    <tx:annotation-driven transaction-manager="transactionManager"/>

    transaction-manager 属性保存一个对在 Spring 配置文件中定义的事务管理器 bean 的引用。这段代码告诉 Spring 在应用事务拦截器时使用 @Transaction 注释。如果没有它,就会忽略 @Transactional 注释,导致代码不会使用任何事务。

    让基本的 @Transactional 注释在 清单 4 的代码中工作仅仅是开始。注意,清单 4 使用 @Transactional 注释时没有指定任何额外的注释参数。我发现许多开发人员在使用 @Transactional 注释时并没有花时间理解它的作用。例如,像我一样在清单 4 中单独使用 @Transactional 注释时,事务传播模式被设置成什么呢?只读标志被设置成什么呢?事务隔离级别的设置是怎样的?更重要的是,事务应何时回滚工作?理解如何使用这个注释对于确保在应用程序中获得合适的事务支持级别非常重要。回答我刚才提出的问题:在单独使用不带任何参数的 @Transactional 注释时,传播模式要设置为 REQUIRED,只读标志设置为 false,事务隔离级别设置为 READ_COMMITTED,而且事务不会针对受控异常(checked exception)回滚。

    @Transactional 只读标志陷阱

    我在工作中经常碰到的一个常见陷阱是 Spring @Transactional 注释中的只读标志没有得到恰当使用。这里有一个快速测试方法:在使用标准 JDBC 代码获得 Java 持久性时,如果只读标志设置为 true,传播模式设置为 SUPPORTS,清单 5 中的 @Transactional 注释的作用是什么呢?


    清单 5. 将只读标志与 SUPPORTS 传播模式结合使用 — JDBC

    view plaincopy to clipboardprint?
    @Transactional(readOnly = true, propagation=Propagation.SUPPORTS)   
    public long insertTrade(TradeData trade) throws Exception {   
       //JDBC Code...   

    @Transactional(readOnly = true, propagation=Propagation.SUPPORTS)
    public long insertTrade(TradeData trade) throws Exception {
       //JDBC Code...
    }

    当执行清单 5 中的 insertTrade() 方法时,猜一猜会得到下面哪一种结果:
    抛出一个只读连接异常 
    正确插入交易订单并提交数据 
    什么也不做,因为传播级别被设置为 SUPPORTS 
    是哪一个呢?正确答案是 B。交易订单会被正确地插入到数据库中,即使只读标志被设置为 true,且事务传播模式被设置为 SUPPORTS。但这是如何做到的呢?由于传播模式被设置为 SUPPORTS,所以不会启动任何事物,因此该方法有效地利用了一个本地(数据库)事务。只读标志只在事务启动时应用。在本例中,因为没有启动任何事务,所以只读标志被忽略。

    Spring Framework @Transactional 注释陷阱-4

    清单 6 中的 @Transactional 注释在设置了只读标志且传播模式被设置为 REQUIRED 时,它的作用是什么呢?


    清单 6. 将只读标志与 REQUIRED 传播模式结合使用 — JDBC

    view plaincopy to clipboardprint?
    @Transactional(readOnly = true, propagation=Propagation.REQUIRED)   
    public long insertTrade(TradeData trade) throws Exception {   
       //JDBC code...   

    @Transactional(readOnly = true, propagation=Propagation.REQUIRED)
    public long insertTrade(TradeData trade) throws Exception {
       //JDBC code...
    }

    执行清单 6 中的 insertTrade() 方法会得到下面哪一种结果呢:

    抛出一个只读连接异常 
    正确插入交易订单并提交数据 
    什么也不做,因为只读标志被设置为 true 
    根据前面的解释,这个问题应该很好回答。正确的答案是 A。会抛出一个异常,表示您正在试图对一个只读连接执行更新。因为启动了一个事务(REQUIRED),所以连接被设置为只读。毫无疑问,在试图执行 SQL 语句时,您会得到一个异常,告诉您该连接是一个只读连接。

    关于只读标志很奇怪的一点是:要使用它,必须启动一个事务。如果只是读取数据,需要事务吗?答案是根本不需要。启动一个事务来执行只读操作会增加处理线程的开销,并会导致数据库发生共享读取锁定(具体取决于使用的数据库类型和设置的隔离级别)。总的来说,在获取基于 JDBC 的 Java 持久性时,使用只读标志有点毫无意义,并会启动不必要的事务而增加额外的开销。

    使用基于 ORM 的框架会怎样呢?按照上面的测试,如果在结合使用 JPA 和 Hibernate 时调用 insertTrade() 方法,清单 7 中的 @Transactional 注释会得到什么结果?


    清单 7. 将只读标志与 REQUIRED 传播模式结合使用 — JPA

    view plaincopy to clipboardprint?
    @Transactional(readOnly = true, propagation=Propagation.REQUIRED)   
    public long insertTrade(TradeData trade) throws Exception {   
       em.persist(trade);   
       return trade.getTradeId();   

    @Transactional(readOnly = true, propagation=Propagation.REQUIRED)
    public long insertTrade(TradeData trade) throws Exception {
       em.persist(trade);
       return trade.getTradeId();
    }

    清单 7 中的 insertTrade() 方法会得到下面哪一种结果:

    抛出一个只读连接异常 
    正确插入交易订单并提交数据 
    什么也不做,因为 readOnly 标志被设置为 true 
    正确的答案是 B。交易订单会被准确无误地插入数据库中。请注意,上一示例表明,在使用 REQUIRED 传播模式时,会抛出一个只读连接异常。使用 JDBC 时是这样。使用基于 ORM 的框架时,只读标志只是对数据库的一个提示,并且一条基于 ORM 框架的指令(本例中是 Hibernate)将对象缓存的 flush 模式设置为 NEVER,表示在这个工作单元中,该对象缓存不应与数据库同步。不过,REQUIRED 传播模式会覆盖所有这些内容,允许事务启动并工作,就好像没有设置只读标志一样。

    这令我想到了另一个我经常碰到的主要陷阱。阅读了前面的所有内容后,您认为如果只对 @Transactional 注释设置只读标志,清单 8 中的代码会得到什么结果呢?


    清单 8. 使用只读标志 — JPA

    view plaincopy to clipboardprint?
    @Transactional(readOnly = true)   
    public TradeData getTrade(long tradeId) throws Exception {   
       return em.find(TradeData.class, tradeId);   

    @Transactional(readOnly = true)
    public TradeData getTrade(long tradeId) throws Exception {
       return em.find(TradeData.class, tradeId);
    }

    清单 8 中的 getTrade() 方法会执行以下哪一种操作?

    启动一个事务,获取交易订单,然后提交事务 
    获取交易订单,但不启动事务 
    正确的答案是 A。一个事务会被启动并提交。不要忘了,@Transactional 注释的默认传播模式是 REQUIRED。这意味着事务会在不必要的情况下启动。根据使用的数据库,这会引起不必要的共享锁,可能会使数据库中出现死锁的情况。此外,启动和停止事务将消耗不必要的处理时间和资源。总的来说,在使用基于 ORM 的框架时,只读标志基本上毫无用处,在大多数情况下会被忽略。但如果您坚持使用它,请记得将传播模式设置为 SUPPORTS(如清单 9 所示),这样就不会启动事务:
    清单 9. 使用只读标志和 SUPPORTS 传播模式进行选择操作

    view plaincopy to clipboardprint?
    @Transactional(readOnly = true, propagation=Propagation.SUPPORTS)   
    public TradeData getTrade(long tradeId) throws Exception {   
       return em.find(TradeData.class, tradeId);   

    @Transactional(readOnly = true, propagation=Propagation.SUPPORTS)
    public TradeData getTrade(long tradeId) throws Exception {
       return em.find(TradeData.class, tradeId);
    }

    另外,在执行读取操作时,避免使用 @Transactional 注释,如清单 10 所示:

    清单 10. 删除 @Transactional 注释进行选择操作

    view plaincopy to clipboardprint?
    public TradeData getTrade(long tradeId) throws Exception {   
       return em.find(TradeData.class, tradeId);   

    public TradeData getTrade(long tradeId) throws Exception {
       return em.find(TradeData.class, tradeId);
    }

    REQUIRES_NEW 事务属性陷阱

    不管是使用 Spring Framework,还是使用 EJB,使用 REQUIRES_NEW 事务属性都会得到不好的结果并导致数据损坏和不一致。REQUIRES_NEW 事务属性总是会在启动方法时启动一个新的事务。许多开发人员都错误地使用 REQUIRES_NEW 属性,认为它是确保事务启动的正确方法。

    Spring Framework @Transactional 注释陷阱-5

    清单 11. 使用 REQUIRES_NEW 事务属性

    view plaincopy to clipboardprint?
    @Transactional(propagation=Propagation.REQUIRES_NEW)   
    public long insertTrade(TradeData trade) throws Exception {...}  

    @Transactional(propagation=Propagation.REQUIRES_NEW)   
    public void updateAcct(TradeData trade) throws Exception {...} 
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public long insertTrade(TradeData trade) throws Exception {...}

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void updateAcct(TradeData trade) throws Exception {...}

    注意,清单 11 中的两个方法都是公共方法,这意味着它们可以单独调用。当使用 REQUIRES_NEW 属性的几个方法通过服务间通信或编排在同一逻辑工作单元内调用时,该属性就会出现问题。例如,假设在清单 11 中,您可以独立于一些用例中的任何其他方法来调用 updateAcct() 方法,但也有在 insertTrade() 方法中调用 updateAcct() 方法的情况。现在如果调用 updateAcct() 方法后抛出异常,交易订单就会回滚,但帐户更新将会提交给数据库,如清单 12 所示:


    清单 12. 使用 REQUIRES_NEW 事务属性的多次更新

    view plaincopy to clipboardprint?
    @Transactional(propagation=Propagation.REQUIRES_NEW)   
    public long insertTrade(TradeData trade) throws Exception {   
       em.persist(trade);   
       updateAcct(trade);   
       //exception occurs here! Trade rolled back but account update is not!   
       ...   

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public long insertTrade(TradeData trade) throws Exception {
       em.persist(trade);
       updateAcct(trade);
       //exception occurs here! Trade rolled back but account update is not!
       ...
    }

    之所以会发生这种情况是因为 updateAcct() 方法中启动了一个新事务,所以在 updateAcct() 方法结束后,事务将被提交。使用 REQUIRES_NEW 事务属性时,如果存在现有事务上下文,当前的事务会被挂起并启动一个新事务。方法结束后,新的事务被提交,原来的事务继续执行。

    由于这种行为,只有在被调用方法中的数据库操作需要保存到数据库中,而不管覆盖事务的结果如何时,才应该使用 REQUIRES_NEW 事务属性。比如,假设尝试的所有股票交易都必须被记录在一个审计数据库中。出于验证错误、资金不足或其他原因,不管交易是否失败,这条信息都需要被持久化。如果没有对审计方法使用 REQUIRES_NEW 属性,审计记录就会连同尝试执行的交易一起回滚。使用 REQUIRES_NEW 属性可以确保不管初始事务的结果如何,审计数据都会被保存。这里要注意的一点是,要始终使用 MANDATORY 或 REQUIRED 属性,而不是 REQUIRES_NEW,除非您有足够的理由来使用它,类似审计示例中的那些理由。

    事务回滚陷阱

    我将最常见的事务陷阱留到最后来讲。遗憾的是,我在生产代码中多次遇到这个错误。我首先从 Spring Framework 开始,然后介绍 EJB 3。

    到目前为止,您研究的代码类似清单 13 所示:


    清单 13. 没有回滚支持

    view plaincopy to clipboardprint?
    @Transactional(propagation=Propagation.REQUIRED)   
    public TradeData placeTrade(TradeData trade) throws Exception {   
       try {   
          insertTrade(trade);   
          updateAcct(trade);   
          return trade;   
       } catch (Exception up) {   
          //log the error   
          throw up;   
       }   

    @Transactional(propagation=Propagation.REQUIRED)
    public TradeData placeTrade(TradeData trade) throws Exception {
       try {
          insertTrade(trade);
          updateAcct(trade);
          return trade;
       } catch (Exception up) {
          //log the error
          throw up;
       }
    }
    假设帐户中没有足够的资金来购买需要的股票,或者还没有准备购买或出售股票,并抛出了一个受控异常(例如 FundsNotAvailableException),那么交易订单会保存在数据库中吗?还是整个逻辑工作单元将执行回滚?答案出乎意料:根据受控异常(不管是在 Spring Framework 中还是在 EJB 中),事务会提交它还未提交的所有工作。使用清单 13,这意味着,如果在执行 updateAcct() 方法期间抛出受控异常,就会保存交易订单,但不会更新帐户来反映交易情况。

    这可能是在使用事务时出现的主要数据完整性和一致性问题了。运行时异常(即非受控异常)自动强制执行整个逻辑工作单元的回滚,但受控异常不会。因此,清单 13 中的代码从事务角度来说毫无用处;尽管看上去它使用事务来维护原子性和一致性,但事实上并没有。

    尽管这种行为看起来很奇怪,但这样做自有它的道理。首先,不是所有受控异常都是不好的;它们可用于事件通知或根据某些条件重定向处理。但更重要的是,应用程序代码会对某些类型的受控异常采取纠正操作,从而使事务全部完成。例如,考虑下面一种场景:您正在为在线书籍零售商编写代码。要完成图书的订单,您需要将电子邮件形式的确认函作为订单处理的一部分发送。如果电子邮件服务器关闭,您将发送某种形式的 SMTP 受控异常,表示邮件无法发送。如果受控异常引起自动回滚,整个图书订单就会由于电子邮件服务器的关闭全部回滚。通过禁止自动回滚受控异常,您可以捕获该异常并执行某种纠正操作(如向挂起队列发送消息),然后提交剩余的订单。

    Spring Framework @Transactional 注释陷阱-6

    使用 Declarative 事务模式时,必须指定容器或框架应该如何处理受控异常。在 Spring Framework 中,通过 @Transactional 注释中的 rollbackFor 参数进行指定,如清单 14 所示:


    清单 14. 添加事务回滚支持 — Spring

    view plaincopy to clipboardprint?
    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)   
    public TradeData placeTrade(TradeData trade) throws Exception {   
       try {   
          insertTrade(trade);   
          updateAcct(trade);   
          return trade;   
       } catch (Exception up) {   
          //log the error   
          throw up;   
       }   

    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
    public TradeData placeTrade(TradeData trade) throws Exception {
       try {
          insertTrade(trade);
          updateAcct(trade);
          return trade;
       } catch (Exception up) {
          //log the error
          throw up;
       }
    }

    注意,@Transactional 注释中使用了 rollbackFor 参数。这个参数接受一个单一异常类或一组异常类,您也可以使用 rollbackForClassName 参数将异常的名称指定为 Java String 类型。还可以使用此属性的相反形式(noRollbackFor)指定除某些异常以外的所有异常应该强制回滚。通常大多数开发人员指定 Exception.class 作为值,表示该方法中的所有异常应该强制回滚。

    在回滚事务这一点上,EJB 的工作方式与 Spring Framework 稍微有点不同。EJB 3.0 规范中的 @TransactionAttribute 注释不包含指定回滚行为的指令。必须使用 SessionContext.setRollbackOnly() 方法将事务标记为执行回滚,如清单 15 所示:


    清单 15. 添加事务回滚支持 — EJB

    view plaincopy to clipboardprint?
    @TransactionAttribute(TransactionAttributeType.REQUIRED)   
    public TradeData placeTrade(TradeData trade) throws Exception {   
       try {   
          insertTrade(trade);   
          updateAcct(trade);   
          return trade;   
       } catch (Exception up) {   
          //log the error   
          sessionCtx.setRollbackOnly();   
          throw up;   
       }   

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public TradeData placeTrade(TradeData trade) throws Exception {
       try {
          insertTrade(trade);
          updateAcct(trade);
          return trade;
       } catch (Exception up) {
          //log the error
          sessionCtx.setRollbackOnly();
          throw up;
       }
    }
    调用 setRollbackOnly() 方法后,就不能改变主意了;惟一可能的结果是在启动事务的方法完成后回滚事务。本系列后续文章中描述的事务策略将介绍何时、何处使用回滚指令,以及何时使用 REQUIRED 与 MANDATORY 事务属性。

    Isolation Level(事务隔离等级)

    1、Serializable:最严格的级别,事务串行执行,资源消耗最大;
    2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
    3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
    4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。
    我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行。
    我们首先说并发中可能发生的3中不讨人喜欢的事情
    1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
    2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成 200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
    3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。

     Dirty reads non-repeatable reads phantom reads 
    Serializable 不会 不会 不会 
    REPEATABLE READ  不会 不会 会 
    READ COMMITTED 不会 会 会 
    Read Uncommitted 会 会 会

    readOnly
    事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。


    展开全文
  • @Transactional 事务回滚 分析

    千次阅读 2017-09-07 15:36:35
    @Transactional 事务回滚 分析 博客分类: java Hibernate Spring2.5 +   @Transactional 事务回滚   Spring的AOP事务管理默认是针对unchecked exception回滚(运行期异常,Runtime Exception)。...
    

    @Transactional 事务回滚

     

    Spring的AOP事务管理默认是针对unchecked exception回滚(运行期异常,Runtime Exception)。

    unchecked ,就是不用手工写try catch的exception

     

    Exception作为基类,下面还分checked exception和unchecked exception。如果客户端可以通过其他的方法恢复异常,那么这种异常就是checked exception;如果客户端对出现的这种异常无能为力,那么这种异常就是Unchecked exception;简单来说,继承于RuntimeException的都是unchecked exception。

     

    Error:
    1.总是不可控制的(unchecked)
    2.经常用来用于表示系统错误或低层资源的错误
    3.如何可能的话,应该在系统级被捕捉 

    Exception:
    1.可以是可被控制(checked) 或不可控制的(unchecked)
    2.表示一个由程序员导致的错误
    3.应该在应用程序级被处理

     

    Java 中定义了两类异常:
    1) Checked exception: 这类异常都是Exception的子类 。异常的向上抛出机制进行处理,假如子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。
    2) Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是非凡的,它们 不能通过client code来试图解决,所以称为Unchecked exception 。

     

     

    在一个类的最外面加@Transactional

    表示这个类的方法中,会开启事务

     

    但这个事务只对unchecked( runtime exception)异常回滚

     

    Java代码 复制代码  收藏代码
    1. throw new RuntimeException("异常"); //这个回滚  
    Java代码   收藏代码
    1. throw new RuntimeException("异常"); //这个回滚  

     

    Java代码 复制代码  收藏代码
    1. throw new Exception("异常");//这个不回滚  
    Java代码   收藏代码
    1. throw new Exception("异常");//这个不回滚  

     

     

    对checked, Exception是不回滚的

     

     

    如果要对checked异常进行回滚,则必须在此方法上加:

     

    Java代码 复制代码  收藏代码
    1. @Transactional(rollbackFor=Exception.class)  
    Java代码   收藏代码
    1. @Transactional(rollbackFor=Exception.class)  

     

    在mng(service)中,如果只是get值,那么可以在此方法上加:

    Java代码 复制代码  收藏代码
    1. @Transactional(propagation=Propagation.NOT_SUPPORTED)  
    Java代码   收藏代码
    1. @Transactional(propagation=Propagation.NOT_SUPPORTED)  

     

    事务默认为:

    Java代码 复制代码  收藏代码
    1. @Transactional(propagation=Propagation.REQUIRED)  
    Java代码   收藏代码
    1. @Transactional(propagation=Propagation.REQUIRED)  

     

     

    REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。


    NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。


    REQUIRESNEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。


    MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。


    SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。


    Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。


    NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效

    展开全文
  • spring事务回滚相关

    2018-11-29 14:12:35
    事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退...这是个很常见的小坑,异常并没有被 “捕获” 到,导致事务并没有回滚。我们在业务层代码中,也许已...

    事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态。

     

    在实际项目中,使用事务是很简单的,在 Spring 项目中,一个 @Transactional 注解就可以解决。

     

    1. 异常并没有被 “捕获” 到

     

    这是个很常见的小坑,异常并没有被 “捕获” 到,导致事务并没有回滚。我们在业务层代码中,也许已经考虑到了异常的存在,或者编辑器已经提示我们需要抛出异常,但是这里面有个需要注意的地方:并不是说我们把异常抛出来了,有异常了事务就会回滚。我们来看一个例子:

     

    @Service

    public class UserServiceImpl implements UserService {

     

       @Resource

       private UserMapper userMapper;

       

       @Override

       @Transactional

       public void isertUser(User user) throws Exception {

           // 插入用户信息

           userMapper.insertUser(user);

           // 手动抛出异常

           throw new SQLException("数据库异常");

       }

    }

     

     

    我们看上面这个代码,其实并没有什么问题,手动抛出一个 SQLException 来模拟实际中操作数据库发生的异常,在这个方法中,既然抛出了异常,那么事务应该回滚,实际却不如此,读者可以自己测试一下就会发现,仍然是可以往数据库插入一条用户数据的。

     

    那么问题出在哪呢?因为 Spring默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。比如上面我们的例子中如果抛出的 RuntimeException 就没有问题,但是抛出 SQLException 就无法回滚了。

     

    针对非运行时异常如果要进行事务回滚的话,可以在 @Transactional 注解中使用 rollbackFor 属性来指定异常,比如:

    @Transactional(rollbackFor = Exception.class)

     

    2. 异常被 “吃” 掉了

     

    就如我本文的标题一样,异常怎么会被吃掉呢?还是回归到现实项目中去,我们在处理异常时,有两种方式,要么抛出去,让上一层来捕获处理;要么把异常 try...catch 掉,在异常出现的地方给处理掉。就因为有这个 try...catch,所以导致异常被 “吃” 掉,事务无法回滚。我们还是看上面那个例子,只不过简单修改一下代码:

     

    @Service

    public class UserServiceImpl implements UserService {

     

       @Resource

       private UserMapper userMapper;

     

       @Override

       @Transactional(rollbackFor = Exception.class)

       public void isertUser(User user) {

           try {

               // 插入用户信息

               userMapper.insertUser(user);

               // 手动抛出异常

               throw new SQLException("数据库异常");

           } catch (Exception e) {

               // 异常处理逻辑

           }

       }

    }

     

     

     

    读者也可以自己测试一下,仍然是可以插入一条用户数据,说明事务并没有因为抛出异常而回滚。这就是 try...catch 把异 “吃” 掉了,这个细节往往比上面那个坑更难以发现,因为我们的思维方式很容易导致 try...catch 代码的产生,一旦出现这种问题,往往排查起来比较费劲。这个就是很明显的自己给自己挖坑,而且自己掉进去之后,还出不来。

     

    那这种怎么解决呢?直接往上抛,给上一层来处理即可,千万不要在事务中把异常自己 ”吃“ 掉。

     

    3. 别忘了事务是有范围的

     

    事务范围这个东西比上面两个坑埋的更深!我之所以把这个也写上,是因为这是我之前在实际项目中遇到的,该场景我就不模拟了,我写一个 demo 让大家看一下,把这个坑记住即可,以后在写代码时,遇到并发问题,如果能想到这个坑,那么这篇文章也就有价值了。

     

    @Service

    public class UserServiceImpl implements UserService {

     

       @Resource

       private UserMapper userMapper;

     

       @Override

       @Transactional(rollbackFor = Exception.class)

       public synchronized void isertUser4(User user) {

           // 实际中的具体业务……

           userMapper.insertUser(user);

       }

    }

     

    可以看到,因为要考虑并发问题,我在业务层代码的方法上加了个 synchronized 关键字。我举个实际的场景,比如一个数据库中,针对某个用户,只有一条记录,下一个插入动作过来,会先判断该数据库中有没有相同的用户,如果有就不插入,就更新,没有才插入,所以理论上,数据库中永远就一条同一用户信息,不会出现同一数据库中插入了两条相同用户的信息。 

     

    但是在压测时,就会出现上面的问题,数据库中确实有两条同一用户的信息,那说明 synchronized 并没有起到作用。分析其原因,在于事务的范围和锁的范围问题。

     

    从上面方法中可以看到,方法上是加了事务的,那么也就是说,在执行该方法开始时,事务启动,执行完了后,事务关闭。但是 synchronized 没有起作用,其实根本原因是因为事务的范围比锁的范围大。也就是说,在加锁的那部分代码执行完之后,锁释放掉了,但是事务还没结束,就在此时另一个线程进来了,事务没结束的话,第二个线程进来时,数据库的状态和第一个线程刚进来是一样的。即由于mysql Innodb引擎的默认隔离级别是可重复读(在同一个事务里,SELECT的结果是事务开始时时间点的状态),线程二事务开始的时候,线程一还没提交完成,导致读取的数据还没更新。第二个线程也做了插入动作,导致了脏数据。

     

    这个问题可以避免,第一,把事务去掉即可(不推荐);第二,在调用该 service 的地方加锁,保证锁的范围比事务的范围大即可。

     

    手动回滚

     

    已经加了@Transactional就不用try catch了,try catch已经把异常捕获了所以事物没有回滚,如果要回滚就加上 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()手动回滚 @Transactional public UserModel login(UserModel user){ UserModel userEntity = new UserModel(); userEntity = userDao.login(user); if(userEntity == null) { return null; } userEntity = userDao.login(user); if(userEntity == null) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return null; } else { return userEntity; } }

    展开全文
  • @Transactional 事务回滚   Spring的AOP事务管理默认是针对unchecked exception回滚(运行期异常,Runtime Exception)。 unchecked ,就是不用手工写try catch的exception   Exception作为基类,下面还分...

    @Transactional 事务回滚

     

    Spring的AOP事务管理默认是针对unchecked exception回滚(运行期异常,Runtime Exception)。

    unchecked ,就是不用手工写try catch的exception

     

    Exception作为基类,下面还分checked exception和unchecked exception。如果客户端可以通过其他的方法恢复异常,那么这种异常就是checked exception;如果客户端对出现的这种异常无能为力,那么这种异常就是Unchecked exception;简单来说,继承于RuntimeException的都是unchecked exception。

     

    Error:
    1.总是不可控制的(unchecked)
    2.经常用来用于表示系统错误或低层资源的错误
    3.如何可能的话,应该在系统级被捕捉 

    Exception:
    1.可以是可被控制(checked) 或不可控制的(unchecked)
    2.表示一个由程序员导致的错误
    3.应该在应用程序级被处理

     

    Java 中定义了两类异常:
    1) Checked exception: 这类异常都是Exception的子类 。异常的向上抛出机制进行处理,假如子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。
    2) Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是非凡的,它们 不能通过client code来试图解决,所以称为Unchecked exception 。

     

     

    在一个类的最外面加@Transactional

    表示这个类的方法中,会开启事务

     

    但这个事务只对unchecked( runtime exception)异常回滚

     

    Java代码 复制代码  收藏代码
    1. throw new RuntimeException("异常"); //这个回滚  
    throw new RuntimeException("异常"); //这个回滚

     

    Java代码 复制代码  收藏代码
    1. throw new Exception("异常");//这个不回滚  
    throw new Exception("异常");//这个不回滚

     

     

    对checked, Exception是不回滚的

     

     

    如果要对checked异常进行回滚,则必须在此方法上加:

     

    Java代码 复制代码  收藏代码
    1. @Transactional(rollbackFor=Exception.class)  
    @Transactional(rollbackFor=Exception.class)

     

    在mng(service)中,如果只是get值,那么可以在此方法上加:

    Java代码 复制代码  收藏代码
    1. @Transactional(propagation=Propagation.NOT_SUPPORTED)  
    @Transactional(propagation=Propagation.NOT_SUPPORTED)

     

    事务默认为:

    Java代码 复制代码  收藏代码
    1. @Transactional(propagation=Propagation.REQUIRED)  
    @Transactional(propagation=Propagation.REQUIRED)

     

     

    REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。


    NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。


    REQUIRESNEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。


    MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。


    SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。


    Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。


    NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效

    展开全文
  • spring异常与事务回滚

    2013-07-17 13:04:22
    [color=red]如果你不启用事务,默认是自动提交的,不需要设置autoCommit 如果你需要使用事务,那么在进行数据库操作之前需要 // 设置不自动提交, 事务开始 conn.setAutoCommit(false); // 这里进行...
  • (一)问题的引出、主要解决手段在线程中使用 batchupdate ,中的每一条记录都会自动的commit(但仍使用一个数据库连接会话,有点像hibernate一缓存的概念,多个事务,一个会话),如果有异常,则只有异常的数据...
  • 但是,如果要装配或者集成企业的组件,情况就复杂了。一个组件可以有一个或多个支持它的数据库,因此,当装配两个或更多的组件时,我们希望能够保持在跨组件的多个数据库中进行的操作的原子性。J2EE服务器为这些...
  • 也就是默认对RuntimeException()异常或是其子类进行事务回滚;checked异常,即Exception可try{}捕获的不会回滚,如果使用try-catch捕获抛出的unchecked异常后没有在catch块中采用页面硬编码的方式使用spring api对...
  • 1 Hibernate简介 ...Hibernate本质就是对JDBC进行了轻量的封装。 2 使用Hibernate的好处①引入Hibernate可以使工作人员角色细化,让程序员更关心业务流程。让数据库人员更关注数据库相关的各种操作。 ②分层
  • ●在JavaEE企业开发的应用领域,为了保证数据的完整性和一致性,必须引入数据库事务的概念,所以事务管理是企业应用程序开发中必不可少的技术。 ●事务就是一组由于逻辑上紧密关联而合并成一个整体(工作单元)的...
  •  System.Transactions 基础结构既提供了基于 Transaction 类的显式编程模型,也提供了使用 TransactionScope 类的隐式编程模型,在后一种模型中,事务由该基础结构自动管理。   重要说明: 建议使用 ...
  • 事务回滚

    2016-04-29 14:19:00
    按照一般的事务管理规则,此刻是应该触发事务回滚的。然而并没有触发,前两次操作成功地写入了数据库,最后一次失败告终。 问题追踪 项目大体是使用mysql数据库,管理事务是在spring中完成。其实这里跟开发语言...
  • spring事务异常回滚

    2019-02-18 18:45:13
    也就是默认对RuntimeException()异常或是其子类进行事务回滚,其他异常不回滚; 如果使用try-catch捕获抛出的unchecked异常后没有在catch块中采用页面硬编码的方式使用spring api对事务做显式的回滚,则事务不会...
  • ---- 推荐访问权限问题方法用final修饰未被spring管理多线程调用表不支持事务未开启事务事务不回滚错误的传播特性自己吞了异常手动抛了别的异常自定义了回滚异常嵌套事务回滚多了其他常见问题编程式事务大事务问题 ...
  • MySql事务无法回滚的原因

    万次阅读 2014-07-31 14:29:12
    使用MySQL时,如果发现事务无法回滚,但Hibernate、Spring、JDBC等配置又没有明显问题时,不要苦恼,先看看MySQL创建的表有没有问题,即表的类型。 InnoDB和MyISAM是在使用MySQL最常用的两个表类型,各有优缺点,视...
  • 回滚机制——《亿流量》

    千次阅读 2017-05-12 17:51:14
    最常见的如事务回滚、代码库回滚、部署版本回滚、数据版本回滚、静态资源版本回滚等。通过回滚机制保证系统某些场景下的高可用。  事务回滚  在执行数据库SQL时,如果我们检测到事务提交冲突,那么事务中的所有...
  • 1.常用事务操作 ...1)A:原子性—事务是原子的、不可分割的有机体,这意味着,事务过程修改是不能分割的,即过程修改或者全部执行成功,或者全部执行失败回滚,不能存在部分成功部分失败的场景;...
  • 工作中有一项业务,需要在存储过程里先把A表的数据插入到B表,然后再删除A表的数据。 insert into B select * from A--因为文章重点不是SQL语句,所以就简化成这两句话了 delete A 后来有发生过A,B两张表都没有数据...
  • 近期在项目开发中,碰到了一个诡异的问题,springboot整合mybatis,开启了事务管理,但业务代码发生自定义异常时,事务未能回滚。最终通过debug, 发现mybatis只能对RuntimeException 或者 Error类型进行回滚 。...
  • 分布式系统回滚机制

    千次阅读 2018-07-16 10:00:52
    事务回滚 发布回滚 发布版本化 增量发布 灰度发布 架构升级并行发布 静态资源版本回滚 回滚是指当程序或者数据出错时,恢复到最近的一个正确版本的行为。最常见的如事务回滚、代码库回滚、部署版本回滚、数据...
  • Spring事务管理-回滚

    2015-10-28 18:15:39
    )不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚: 要想所有异常都回滚,要加上 @Transactional(rollbackFor={Exception...
  • PostgreSQL , 10.0 , 事务回滚 , 语句回滚 , 服务端特性 背景 数据库的原子操作单位是事务,那么在一个事务中的SQL,正常情况下,应该是这样的:要么全部提交,要么全部回滚。 为了保证持久性,数据库还有一个概念...
  • 一、知识点及问题后端框架: Spring 、Spring mvc 、mybatis...配置Spring声明式事务,执行中出现异常未回滚.从网上查询得到一开始是自己的配置出了问题,由于配置文件的加载顺序决定了容器的加载顺序导致Spring事务没有

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 43,597
精华内容 17,438
关键字:

业务级事务回滚