亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Boot 各種回滾操作實戰(zhàn)教程(自動回滾、手動回滾、部分回滾)

 更新時間:2023年07月17日 10:48:54   作者:book多得  
這篇文章主要介紹了Spring Boot 各種回滾操作實戰(zhàn)教程(自動回滾、手動回滾、部分回滾),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

概念

事務定義

事務,就是一組操作數(shù)據(jù)庫的動作集合。事務是現(xiàn)代數(shù)據(jù)庫理論中的核心概念之一。如果一組處理步驟或者全部發(fā)生或者一步也不執(zhí)行,我們稱該組處理步驟為一個事務。當所有的步驟像一個操作一樣被完整地執(zhí)行,我們稱該事務被提交。由于其中的一部分或多步執(zhí)行失敗,導致沒有步驟被提交,則事務必須回滾到最初的系統(tǒng)狀態(tài)。

事務特點

  • 原子性:一個事務中所有對數(shù)據(jù)庫的操作是一個不可分割的操作序列,要么全做要么全不做
  • 一致性:數(shù)據(jù)不會因為事務的執(zhí)行而遭到破壞
  • 隔離性:一個事務的執(zhí)行,不受其他事務的干擾,即并發(fā)執(zhí)行的事務之間互不干擾
  • 持久性:一個事務一旦提交,它對數(shù)據(jù)庫的改變就是永久的。

事務實現(xiàn)機制

Spring 為事務管理提供了豐富的功能支持。Spring 事務管理分為編碼式和聲明式的兩種方式。

  • 編程式事務管理: 編程式事務管理使用 TransactionTemplate 或者直接使用底層的 PlatformTransactionManager。對于編程式事務管理,spring推薦使用TransactionTemplate。
  • 聲明式事務管理: 建立在AOP之上的。其本質(zhì)是對方法前后進行攔截,然后在目標方法開始之前創(chuàng)建或者加入一個事務,在執(zhí)行完目標方法之后根據(jù)執(zhí)行情況提交或者回滾事務。
  • 聲明式事務管理不需要入侵代碼,更快捷而且簡單,推薦使用。

聲明式事務有兩種方式:

  • 一種是在配置文件(xml)中做相關(guān)的事務規(guī)則聲明(因為很少用本文不講解)
  • 另一種是基于 @Transactional 注解的方式。注釋配置是目前流行的使用方式,推薦使用。

在應用系統(tǒng)調(diào)用聲明了 @Transactional 的目標方法時,Spring Framework 默認使用 AOP 代理,在代碼運行時生成一個代理對象,根據(jù) @Transactional 的屬性配置信息,這個代理對象決定該聲明 @Transactional 的目標方法是否由攔截器 TransactionInterceptor 來使用攔截,在 TransactionInterceptor 攔截時,會在目標方法開始執(zhí)行之前創(chuàng)建并加入事務,并執(zhí)行目標方法的邏輯,最后根據(jù)執(zhí)行情況是否出現(xiàn)異常,利用抽象事務管理器 AbstractPlatformTransactionManager 操作數(shù)據(jù)源 DataSource 提交或回滾事務。

Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 兩種,以 CglibAopProxy 為例,對于 CglibAopProxy,需要調(diào)用其內(nèi)部類的 DynamicAdvisedInterceptor 的 intercept 方法。對于 JdkDynamicAopProxy,需要調(diào)用其 invoke 方法。

開啟事務

注解@Transactional的使用

注解@Transactional常用配置

參 數(shù) 名 稱功 能 描 述
readOnly用于設(shè)置當前事務是否為只讀事務,設(shè)置為true表示只讀,false則表示可讀寫,默認值為false。例如:@Transactional(readOnly=true)
rollbackFor用于設(shè)置需要進行回滾的異常類數(shù)組,當方法中拋出指定異常數(shù)組中的異常時,則進行事務回滾。例如:指定單一異常類:@Transactional(rollbackFor=RuntimeException.class);指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
transactionManager / value多個事務管理器托管在 Spring 容器中時,指定事務管理器的 bean 名稱
rollbackForClassName用于設(shè)置需要進行回滾的異常類名稱數(shù)組,當方法中拋出指定異常名稱數(shù)組中的異常時,則進行事務回滾。例如:指定單一異常類名稱@Transactional(rollbackForClassName=”RuntimeException”)指定多個異常類名稱:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
noRollbackFor用于設(shè)置不需要進行回滾的異常類數(shù)組,當方法中拋出指定異常數(shù)組中的異常時,不進行事務回滾。例如:指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName用于設(shè)置不需要進行回滾的異常類名稱數(shù)組,當方法中拋出指定異常名稱數(shù)組中的異常時,不進行事務回滾。例如:指定單一異常類名稱:@Transactional(noRollbackForClassName=”RuntimeException”)指定多個異常類名稱:@Transactional(noRollbackForClassName={“RuntimeException”, ”Exception”})
propagation用于設(shè)置事務的傳播行為。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly=true)
isolation用于設(shè)置底層數(shù)據(jù)庫的事務隔離級別,事務隔離級別用于處理多事務并發(fā)的情況,通常使用數(shù)據(jù)庫的默認隔離級別即可,基本不需要進行設(shè)置
timeout該屬性用于設(shè)置事務的超時秒數(shù),默認值為-1表示永不超時事物超時設(shè)置:@Transactional(timeout=30) ,設(shè)置為30秒

Propagation的屬性(事務的傳播行為)

例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

Propagation屬性含義
REQUIRED默認值 在有transaction狀態(tài)下執(zhí)行;如當前沒有transaction,則創(chuàng)建新的transaction;
SUPPORTS如當前有transaction,則在transaction狀態(tài)下執(zhí)行;如果當前沒有transaction,在無transaction狀態(tài)下執(zhí)行;
MANDATORY必須在有transaction狀態(tài)下執(zhí)行,如果當前沒有transaction,則拋出異常IllegalTransactionStateException;
REQUIRES_NEW創(chuàng)建新的transaction并執(zhí)行;如果當前已有transaction,則將當前transaction掛起;
NOT_SUPPORTED在無transaction狀態(tài)下執(zhí)行;如果當前已有transaction,則將當前transaction掛起;
NEVER在無transaction狀態(tài)下執(zhí)行;如果當前已有transaction,則拋出異常IllegalTransactionStateException。

事務5種隔離級別

例如:@Transactional(isolation = Isolation.READ_COMMITTED)

隔離級別含義
DEFAULT這是一個PlatfromTransactionManager默認的隔離級別,使用數(shù)據(jù)庫默認的事務隔離級別另外四個與JDBC的隔離級別相對應;
READ_UNCOMMITTED最低的隔離級別。事實上我們不應該稱其為隔離級別,因為在事務完成前,其他事務可以看到該事務所修改的數(shù)據(jù)。而在其他事務提交前,該事務也可以看到其他事務所做的修改??赡軐е屡K,幻,不可重復讀
READ_COMMITTED大多數(shù)數(shù)據(jù)庫的默認級別。在事務完成前,其他事務無法看到該事務所修改的數(shù)據(jù)。遺憾的是,在該事務提交后,你就可以查看其他事務插入或更新的數(shù)據(jù)。這意味著在事務的不同點上,如果其他事務修改了數(shù)據(jù),你就會看到不同的數(shù)據(jù)。可防止臟讀,但幻讀和不可重復讀仍可以發(fā)生。
REPEATABLE_READ比ISOLATION_READ_COMMITTED更嚴格,該隔離級別確保如果在事務中查詢了某個數(shù)據(jù)集,你至少還能再次查詢到相同的數(shù)據(jù)集,即使其他事務修改了所查詢的數(shù)據(jù)。然而如果其他事務插入了新數(shù)據(jù),你就可以查詢到該新插入的數(shù)據(jù)。可防止臟讀,不可重復讀,但幻讀仍可能發(fā)生。
SERIALIZABLE完全服從ACID的隔離級別,確保不發(fā)生臟讀、不可重復讀和幻影讀。這在所有隔離級別中也是最慢的,因為它通常是通過完全鎖定當前事務所涉及的數(shù)據(jù)表來完成的。代價最大、可靠性最高的隔離級別,所有的事務都是按順序一個接一個地執(zhí)行。避免所有不安全讀取。

使用注意事項(防止事務失效) 

  • 在具體的類(或類的方法)上使用 @Transactional 注解,而不要使用在類所要實現(xiàn)的任何接口上。
  • @Transactional 注解應該只被應用在 public 修飾的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 該注解,它也不會報錯(IDEA會有提示), 但事務并沒有生效。
  • 被外部調(diào)用的公共方法A有兩個進行了數(shù)據(jù)操作的子方法B和子方法C的事務注解說明:
    • 被外部調(diào)用的公共方法A未聲明事務@Transactional,子方法B和C若是其他類的方法且各自聲明事務,則事務由子方法B和C各自控制
    • 被外部調(diào)用的公共方法A未聲明事務@Transactional,子方法B和C若是本類的方法,則無論子方法B和C是否聲明事務,事務均不會生效
    • 被外部調(diào)用的公共方法A聲明事務@Transactional,無論子方法B和C是不是本類的方法,無論子方法B和C是否聲明事務,事務均由公共方法A控制
    • 被外部調(diào)用的公共方法A聲明事務@Transactional,子方法運行異常,但運行異常被子方法自己 try-catch 處理了,則事務回滾是不會生效的!

如果想要事務回滾生效,需要將子方法的事務控制交給調(diào)用的方法來處理:

  • 方案1:子方法中不用 try-catch 處理運行異常
  • 方案2:子方法的catch里面將運行異常拋出【throw new RuntimeException();】

默認情況下,Spring會對unchecked異常進行事務回滾,也就是默認對 RuntimeException() 異?;蚴瞧渥宇愡M行事務回滾。
如果是checked異常則不回滾,例如空指針異常、算數(shù)異常等會被回滾;文件讀寫、網(wǎng)絡(luò)問題Spring就沒法回滾。

若想對所有異常(包括自定義異常)都起作用,注解上面需配置異常類型:@Transactional(rollbackFor = Exception.class

  • 數(shù)據(jù)庫要支持事務,如果是mysql,要使用innodb引擎,myisam不支持事務
  • 事務@Transactional由spring控制時,它會在拋出異常的時候進行回滾。如果自己使用try-catch捕獲處理了,是不生效的。如果想事務生效可以進行手動回滾或者在catch里面將異常拋出【throw new RuntimeException();】
    • 方案一:手動拋出運行時異常(缺陷是不能在catch代碼塊自定義返回值)
  try{
      ....  
  }catch(Exception e){
      logger.error("",e);
      throw new RuntimeException(e);
  }
  • 方案二:手動進行回滾【 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 】
  try{
      ...
  }catch(Exception e){
      log.error("fail",e);
      TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      return false;
  }
  • @Transactional可以放在Controller下面直接起作用,看到網(wǎng)上好多同學說要放到@Component下面或者@Service下面,經(jīng)過試驗,可以不用放在這兩個下面也起作用。
  • @Transactional引入包問題,它有兩個包:
import javax.transaction.Transactional; 
// 和
import org.springframework.transaction.annotation.Transactional;         // 推薦

這兩個都可以用,對比了一下他們兩個的方法和屬性,發(fā)現(xiàn)后面的比前面的強大。建議使用后面的。

使用場景

自動回滾

直接拋出,不try/catch

@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder() throws Exception {  
     success();  
     //假如exception這個操作數(shù)據(jù)庫的方法會拋出異常,方法success()對數(shù)據(jù)庫的操作會回滾。 
     exception(); 
     return ApiReturnUtil.success();
}

手動回滾

進行try/catch,回滾并拋出

@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder (){  
    success();  
    try {  
        exception(); 
     } catch (Exception e) {  
        e.printStackTrace();     
        // 手動回滾事務
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        return ApiReturnUtil.error();
     }  
    return ApiReturnUtil.success();
}

回滾部分異常

使用【Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint(); 】設(shè)置回滾點。

使用【TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);】回滾到savePoint。

@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder (){  
    success();  
    //只回滾以下異常,
    Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
    try {  
        exception(); 
     } catch (Exception e) {  
        e.printStackTrace();     
        // 手工回滾事務
        TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
        return ApiReturnUtil.error();
     }  
    return ApiReturnUtil.success();
}

手動創(chuàng)建、提交、回滾事務

PlatformTransactionManager 這個接口中定義了三個方法 getTransaction創(chuàng)建事務,commit 提交事務,rollback 回滾事務。它的實現(xiàn)類是 AbstractPlatformTransactionManager。

@Autowired
priDataSourceTransactionManager dataSourceTransactionManager;
@Autowired
TransactionDefinition transactionDefinition;
// 手動創(chuàng)建事務
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
// 手動提交事務
dataSourceTransactionManager.commit(transactionStatus);
// 手動回滾事務。(最好是放在catch 里面,防止程序異常而事務一直卡在哪里未提交)
dataSourceTransactionManager.rollback(transactionStatus);

事務失效不回滾的原因及解決方案

異常被捕獲導致事務失效

在spring boot 中,使用事務非常簡單,直接在方法上面加入@Transactional 就可以實現(xiàn)。

@GetMapping("delete")
@ResponseBody
@Transactional    
public void delete(@RequestParam("id") int id) {       
    try {          
        //delete country
        this.repository.delete(id);         
        if(id == 1){              
            throw Exception("測試事務");
        }           
         //delete city
        this.repository.deleteByCountryId(id);
    }catch (Exception e){
        logger.error("delete false:" + e.getMessage());        
    }
}

發(fā)現(xiàn)事務不回滾,即 this.repository.delete(id);成功把數(shù)據(jù)刪除了。

原因:

默認spring事務只在發(fā)生未被捕獲的 RuntimeException 時才回滾。

spring aop 異常捕獲原理:被攔截的方法需顯式拋出異常,并不能經(jīng)任何處理,這樣aop代理才能捕獲到方法的異常,才能進行回滾,默認情況下aop只捕獲 RuntimeException 的異常,但可以通過配置來捕獲特定的異常并回滾。

換句話說在service的方法中不使用 try catch 或者在 catch 中最后加上throw new RuntimeExcetpion()拋出運行異常,這樣程序異常時才能被aop捕獲進而回滾。

解決方案:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.2.RELEASE</version>
    </dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.9</version>
</dependency>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<configuration>
    <showWeaveInfo>true</showWeaveInfo>
    <aspectLibraries>
    <aspectLibrary>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        </aspectLibrary>
    </aspectLibraries>
</configuration>
<executions>
<execution>
    <goals>
        <goal>compile</goal>
        <goal>test-compile</goal>
    </goals>
</execution>
</executions>
</plugin>

解決方案

方案1、在類上(或者最外層的公共方法)加事務

@Service
@Slf4j
public class MyTransactional {
    // 最外層公共方法。自動回滾事務方式,insertOrder()方法報錯后事務回滾,且線程中止,后續(xù)邏輯無法執(zhí)行
    @Transactional
    public void test1() {
        this.insertOrder();
        System.out.println("11111111111111111");
    }
    // 最外層公共方法。手動回滾事務方式,insertOrder()方法報錯后事務回滾,可以繼續(xù)執(zhí)行后續(xù)邏輯
    @Transactional
    public void test2() {
        try {  
            insertOrder();
        } catch (Exception e) {  
            log.error("faild to ...", e);
            // 手動回滾事務
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            // 其他操作
        }
        // 其他操作
    }
    // 進行數(shù)據(jù)庫操作的方法(private 或 public 均可)
    private void insertOrder() {
        //insert log info
        //insertOrder
        //updateAccount
    }
}

方案 2、使用AspectJ 取代 Spring AOP 代理

上面的兩個問題@Transactional 注解只應用到 public 方法和自調(diào)用問題,是由于使用 Spring AOP 代理造成的。為解決這兩個問題,可以使用 AspectJ 取代 Spring AOP 代理。

需要將下面的 AspectJ 信息添加到 xml 配置信息中。

AspectJ 的 xml 配置信息

<tx:annotation-driven mode="aspectj" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
</bean class="org.springframework.transaction.aspectj.AnnotationTransactionAspect" factory-method="aspectOf">
    <property name="transactionManager" ref="transactionManager" />
</bean> 

同時在 Maven 的 pom 文件中加入 spring-aspects 和 aspectjrt 的 dependency 以及 aspectj-maven-plugin。

AspectJ 的 pom 配置信息

  • 方案1:例如service層處理事務,那么service中的方法中不做異常捕獲,或者在catch語句中最后增加

throw new RuntimeException(); 語句,以便讓aop捕獲異常再去回滾,并且在service的上層要繼續(xù)捕獲這個異常。

  • 方案2:在service層方法的catch語句中進行手動回滾,這樣上層就無需去處理異常。
@GetMapping("delete") 
@ResponseBody 
@Transactional 
public Object delete(@RequestParam("id") int id){ 
    if (id < 1){
         return new MessageBean(101,"parameter wrong: id = " + id) ; 
     } 
    try { 
         //delete country
         this.countryRepository.delete(id);
         //delete city
         this.cityRepository.deleteByCountryId(id);
         return new MessageBean(200,"delete success");
     }catch (Exception e){
         logger.error("delete false:" + e.getMessage());
         // 手動回滾
         TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
         return new MessageBean(101,"delete false");
     }
}

自調(diào)用導致事務失效

問題描述及原因

在 Spring 的 AOP 代理下,只有目標方法由外部調(diào)用,目標方法才由 Spring 生成的代理對象來管理,否則會造成自調(diào)用問題。

若同一類中的 沒有@Transactional 注解的方法 內(nèi)部調(diào)用 有@Transactional 注解的方法,有@Transactional 注解的方法的事務被忽略,不會發(fā)生回滾。見 示例代碼展示。

自調(diào)用問題示例:

@Service
public class OrderService {
    private void insert() {
        insertOrder();
    }
    @Transactional
    public void insertOrder() {
        //insert log info
        //insertOrder
        //updateAccount
    }
}
// insertOrder() 盡管有@Transactional 注解,但它被內(nèi)部方法 insert()調(diào)用,事務被忽略,出現(xiàn)異常事務不會發(fā)生回滾,并且會報錯類似于:org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope(翻譯:沒有Transaction無法回滾事務。自調(diào)用導致@Transactional 失效。)

自調(diào)用失效原因:

  • spring里事務是用注解配置的,當一個方法沒有接口,單單只是一個內(nèi)部方法時,事務的注解是不起作用的,需要回滾時就會報錯。出現(xiàn)這個問題的根本原因是:
  • @Transactional 的實現(xiàn)原理是AOP,AOP的實現(xiàn)原理是動態(tài)代理,而自調(diào)用時并不存在代理對象的調(diào)用,也就不會產(chǎn)生基于AOP 的事務回滾操作
  • 雖然可以直接從容器中獲取代理對象,但這樣有侵入之嫌,不推薦。
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.2.RELEASE</version>
    </dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.9</version>
</dependency>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<configuration>
    <showWeaveInfo>true</showWeaveInfo>
    <aspectLibraries>
    <aspectLibrary>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        </aspectLibrary>
    </aspectLibraries>
</configuration>
<executions>
<execution>
    <goals>
        <goal>compile</goal>
        <goal>test-compile</goal>
    </goals>
</execution>
</executions>
</plugin>

其他

事務提交方式

默認情況下,數(shù)據(jù)庫處于自動提交模式。每一條語句處于一個單獨的事務中,在這條語句執(zhí)行完畢時,如果執(zhí)行成功則隱式的提交事務,如果執(zhí)行失敗則隱式的回滾事務。

對于正常的事務管理,是一組相關(guān)的操作處于一個事務之中,因此必須關(guān)閉數(shù)據(jù)庫的自動提交模式。不過,這個我們不用擔心,spring 會將底層連接的【自動提交特性】設(shè)置為 false 。也就是在使用 spring 進行事務管理的時候,spring 會將【是否自動提交】設(shè)置為false,等價于JDBC中的 connection.setAutoCommit(false); ,在執(zhí)行完之后在進行提交 connection.commit(); 。

事務回滾規(guī)則

指示spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內(nèi)拋出異常。spring事務管理器會捕捉任何未處理的異常,然后依據(jù)規(guī)則決定是否回滾拋出異常的事務。

默認配置下,spring只有在拋出的異常為運行時unchecked異常時才回滾該事務,也就是拋出的異常為RuntimeException的子類(Errors也會導致事務回滾),而拋出checked異常則不會導致事務回滾。

可以明確的配置在拋出那些異常時回滾事務,包括checked異常。也可以明確定義那些異常拋出時不回滾事務。

事務并發(fā)會產(chǎn)生的問題

術(shù)語含義
臟讀A事務讀取到了B事務還未提交的數(shù)據(jù),如果B未提交的事務回滾了,那么A事務讀取的數(shù)據(jù)就是無效的,這就是數(shù)據(jù)臟讀
不可重復讀在同一個事務中,多次讀取同一數(shù)據(jù)返回的結(jié)果不一致,這是由于讀取事務在進行操作的過程中,如果出現(xiàn)更新事務,它必須等待更新事務執(zhí)行成功提交完成后才能繼續(xù)讀取數(shù)據(jù),這就導致讀取事務在前后讀取的數(shù)據(jù)不一致的狀況出現(xiàn)
幻讀A事務讀取了幾行記錄后,B事務插入了新數(shù)據(jù),并且提交了插入操作,在后續(xù)操作中A事務就會多出幾行原本不存在的數(shù)據(jù),就像A事務出現(xiàn)幻覺,這就是幻讀

第一類丟失更新

沒有事務隔離的情況下,兩個事務都同時更新一行數(shù)據(jù),但是第二個事務卻中途失敗退出, 導致對數(shù)據(jù)的兩個修改都失效了。

例如:

張三的工資為5000,事務A中獲取工資為5000,事務B獲取工資為5000,匯入100,并提交數(shù)據(jù)庫,工資變?yōu)?100;

隨后,事務A發(fā)生異常,回滾了,恢復張三的工資為5000,這樣就導致事務B的更新丟失了。

臟讀

臟讀就是指當一個事務正在訪問數(shù)據(jù),并且對數(shù)據(jù)進行了修改,而這種修改還沒有提交到數(shù)據(jù)庫中,這時,另外一個事務也訪問這個數(shù)據(jù),然后使用了這個數(shù)據(jù)。

例如:

張三的工資為5000,事務A中把他的工資改為8000,但事務A尚未提交。

    與此同時,事務B正在讀取張三的工資,讀取到張三的工資為8000。
    隨后,事務A發(fā)生異常,回滾了事務,張三的工資又回滾為5000。
    最后,事務B讀取到的張三工資為8000的數(shù)據(jù)即為臟數(shù)據(jù),事務B做了一次臟讀。

不可重復讀

是指在一個事務內(nèi),多次讀同一數(shù)據(jù)。在這個事務還沒有結(jié)束時,另外一個事務也訪問該同一數(shù)據(jù)。那么,在第一個事務中的兩次讀數(shù)據(jù)之間,由于第二個事務的修改,那么第一個事務兩次讀到的的數(shù)據(jù)可能是不一樣的。這樣就發(fā)生了在一個事務內(nèi)兩次讀到的數(shù)據(jù)是不一樣的,因此稱為是不可重復讀。

例如:

在事務A中,讀取到張三的工資為5000,操作沒有完成,事務還沒提交?! ∨c此同時,事務B把張三的工資改為8000,并提交了事務?! ‰S后,在事務A中,再次讀取張三的工資,此時工資變?yōu)?000。在一個事務中前后兩次讀取的結(jié)果并不致,導致了不可重復讀。

第二類丟失更新

不可重復讀的特例。

有兩個并發(fā)事務同時讀取同一行數(shù)據(jù),然后其中一個對它進行修改提交,而另一個也進行了修改提交。這就會造成第一次寫操作失效。

例如:

在事務A中,讀取到張三的存款為5000,操作沒有完成,事務還沒提交?! ∨c此同時,事務B存入1000,把張三的存款改為6000,并提交了事務?! ‰S后,在事務A中,存儲500,把張三的存款改為5500,并提交了事務,這樣事務A的更新覆蓋了事務B的更新。

幻讀

是指當事務不是獨立執(zhí)行時發(fā)生的一種現(xiàn)象,例如第一個事務對一個表中的數(shù)據(jù)進行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。同時,第二個事務也修改這個表中的數(shù)據(jù),這種修改是向表中插入一行新數(shù)據(jù)。那么,以后就會發(fā)生操作第一個事務的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣。

例如:

目前工資為5000的員工有10人,事務A讀取到所有的工資為5000的人數(shù)為10人?! 〈藭r,事務B插入一條工資也為5000的記錄。  這時,事務A再次讀取工資為5000的員工,記錄為11人。此時產(chǎn)生了幻讀。

不可重復讀和幻讀的區(qū)別

不可重復讀的重點是修改,同樣的條件,你讀取過的數(shù)據(jù),再次讀取出來發(fā)現(xiàn)值不一樣了

幻讀的重點在于新增或者刪除,同樣的條件,第 1 次和第 2 次讀出來的記錄數(shù)不一樣

到此這篇關(guān)于Spring Boot 各種回滾騷操作實戰(zhàn)(自動回滾、手動回滾、部分回滾)的文章就介紹到這了,更多相關(guān)Spring Boot回滾內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論