欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring事務(wù)相關(guān)問題解決方案

 更新時間:2020年02月27日 11:49:52   作者:min.jiang  
這篇文章主要介紹了Spring事務(wù)相關(guān)問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

有些spring相關(guān)的知識點之前一直沒有仔細研究:比如spring的事務(wù),并不是沒有使用,也曾經(jīng)簡單的在某些需要事務(wù)處理的方法上通過增加事務(wù)注解來實現(xiàn)事務(wù)功能,僅僅是跟隨使用(甚至并未測試過事務(wù)的正確性),至于如何在項目中配置事務(wù),如何才能將事務(wù)寫正確,事務(wù)的其它的一些原理性的東西從未花時間研究。最近同事正好拋出了一個問題,借此機會學習了一遍。

問題一:增加了readOnly=true的事務(wù)中包含寫操作,為什么線上運行這段代碼是正常的呢?

@Transactional(readOnly = true)
  public Integer getUID(String key, String type) {
    keyGeneratorDao.insert(key, type);
    keyGeneratorDao.update(key, type);
    return keyGeneratorDao.select(key, type);
  }

我為什么對這個問題感興趣?

不懂這個readOnly參數(shù)的含義,之前寫@Transactional的注解,那都是使用的默認值,不帶顯示參數(shù)。提出配置了readOnly參數(shù)后,理論上應(yīng)該程序報錯而實際上沒有報錯,想搞清楚為什么。

開始寫單元測試:

在單元測試類中寫事務(wù)函數(shù)以及測試方法

@Autowired
  private IkeyGeneratorDao keyGeneratorDao;

  @Transactional(readOnly=true)
  public Integer getId(String key, String type){
    return keyGeneratorDao.select(key, type);
  }
  @Transactional
  public Integer getUID(String key, String type) {
    keyGeneratorDao.insert(key, type);
    keyGeneratorDao.update(key, type);
    return this.getId(key,type);
  }
  @Test
  public void testCreateGuid(){
    int guid=this.getUID("12345", "jim");
    System.out.println(guid);
  }

測試結(jié)果顯示正常,與上面提到的不允許進行寫操作的觀點相反,于是想起典型的事務(wù)生效問題。

挖的第一個坑:如果事務(wù)采用的是cglib動態(tài)代理,調(diào)用的方法與事務(wù)方法處在同一個類中事務(wù)不生效。

將兩個事務(wù)事務(wù)轉(zhuǎn)移到單獨的類中,然后測試,類代碼省略,只是將上面兩個標記了@Transactional的方法封裝在一個單獨的類中。

@Autowired
  private KeyGeneratorService keyService;

  @Test
  public void testCreateGuid2(){
    int guid=this.keyService.getUID("12345", "jim");
    System.out.println(guid);
  }

測試結(jié)果顯示也是正常,于是想確認下事務(wù)到底是否生效,加入異常以測試數(shù)據(jù)是否回滾,修改代碼如下:

@Transactional
  public Integer getUID3(String key, String type) {
    keyGeneratorDao.insert(key, type);
    Integer.parseInt("aaa");//throw exception
    keyGeneratorDao.update(key, type);
}

測試結(jié)果顯示事務(wù)回滾正常,可以排除事務(wù)環(huán)境配置問題。

挖的第二個坑:做測試一定要與原問題代碼盡量保持一致,否則會產(chǎn)生其它的不明原因影響判斷。通過對比原問題的代碼發(fā)現(xiàn)我寫的測試代碼與問題代碼有區(qū)別,readOnly是加在包含有寫操作的方法上,而我的是兩個方法,只有在讀的方法上增加了readOnly,于時再次修改代碼:

@Transactional(readOnly = true)
  public Integer getUID4(String key, String type) {
    keyGeneratorDao.insert(key, type);
    keyGeneratorDao.update(key, type);
    return keyGeneratorDao.select(key, type);
  }

測試結(jié)果顯示運行不正常,提示如下錯誤:Cause: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed,到這的確說明在在加了readOnly=true的事務(wù)內(nèi)是不允許寫入操作的。為什么這段代碼在線上運行是成功的呢,于時查看前端的調(diào)用,發(fā)現(xiàn)前端調(diào)用的并不是直接標識了Transactional的方法,而是根據(jù)不同的具體業(yè)務(wù)重新包裝的方法,比如我們需要生成訂單的編號,前端只調(diào)用genOrderCode而不調(diào)用getUID。

非事務(wù)方法在內(nèi)部調(diào)用了本類事務(wù)方法,然后非事務(wù)方法被外部調(diào)用ServicegenOrderCode,是一個非事務(wù)方法,內(nèi)部調(diào)用了getUIDgetUID,是一個事務(wù)方法

@Autowired
  private IkeyGeneratorDao keyGeneratorDao;

  @Transactional(readOnly = true)
  public Integer getUID(String key, String type) {
    keyGeneratorDao.insert(key, type);
    keyGeneratorDao.update(key, type);
    return keyGeneratorDao.select(key, type);
  }

  public String genOrderCode(Date orderDate)
  {
    SimpleDateFormat df = new SimpleDateFormat("yyMMddHH");
    String ticketDate = df.format(orderDate);
    Integer uid = getUID(ticketDate, ORDER_CODE);    

    return ticketDate + genRandom2(uid.toString(), 3, 3) ;
  }

Test,外部類調(diào)用非事務(wù)方法

  @Test
  public void testCreateGuid3(){
    String guid=this.keyService.genOrderCode(new Date());
    System.out.println(guid);
  }

測試結(jié)果居然是正常的,這與線上運行的結(jié)果相同,后面經(jīng)同事提醒,這又是一個不正確使用事務(wù)的案例。

挖的第三個坑:當調(diào)用一個類的非事務(wù)方法且這個非事務(wù)方法內(nèi)部調(diào)用了本類自身的事務(wù)方法,那么事務(wù)也不會生效。

問題二:下面的代碼可以實現(xiàn)事務(wù)回滾嗎?

Service

  • genOrderCode方法調(diào)用
  • getUID2兩個方法都是具備相同的事務(wù)參數(shù)
  • getUID2拋出異常
  • genOrderCode捕獲這個異常
@Transactional
  public Integer getUID2(String key, String type) {
    keyGeneratorDao.insert(key, type);
    Integer.parseInt("aaa");//throw exception
    keyGeneratorDao.update(key, type);
    return keyGeneratorDao.select(key, type);
  }

  @Transactional
  public String genOrderCode(Date orderDate)
  {
    try{
      SimpleDateFormat df = new SimpleDateFormat("yyMMddHH");
      String ticketDate = df.format(orderDate);
      Integer uid = getUID2(ticketDate, ORDER_CODE);    

      return ticketDate + genRandom2(uid.toString(), 3, 3) ;
    }catch(Exception ex){
      System.out.println(ex);
    }
    return null;
  }

Test

  @Test
  public void testCreateGuid3(){
    String guid=this.keyService.genOrderCode(new Date());
    System.out.println(guid);
  }

執(zhí)行測試代碼,發(fā)現(xiàn)可以成功提交,但數(shù)據(jù)是不完整的,因為更新操作沒有完成。為什么會是這樣的呢?因為默認的Propagation.REQUIRED指明多個操作處于一個事務(wù)中,由于genOrderCode有異常處理,所以即使getUID2中發(fā)生異常,系統(tǒng)也會認定提交是合法的,因此會出現(xiàn)插入操作正常更新不正常但事務(wù)正常提交并不回滾的情況。
如果顯示指定Propagation.REQUIRES_NEW呢?

@Transactional(propagation=Propagation.REQUIRES_NEW)
  public Integer getUID2(String key, String type) {
    keyGeneratorDao.insert(key, type);
    Integer.parseInt("aaa");//throw exception
    keyGeneratorDao.update(key, type);
    return keyGeneratorDao.select(key, type);
  }

再執(zhí)行相同的測試,數(shù)據(jù)正?;貪L,這里提供兩張圖,可以看的清楚些(因為常用的就這兩種,其它的有興趣可以多多研究)

REQUIRED

REQUIRES_NEW

通過事務(wù)的兩個小問題,總結(jié)出解決問題的一些小技巧或者叫經(jīng)驗:發(fā)現(xiàn)問題之后,不要局限于某個點,最好根據(jù)上下文來結(jié)合分析,比如問題一的readonly可寫入,單看那段代碼很難找出合理的解釋,只有結(jié)合前后端調(diào)用才能找出根本原因。寫單元測試盡量寫相同的代碼,否則有可能會出現(xiàn)一些干擾項影響判斷。學習呢,有時間盡量學的全點,比如@Transactional這個注解,除了readOnly還有Propagation等等。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • EJB輕松進階之一

    EJB輕松進階之一

    EJB輕松進階之一...
    2006-12-12
  • springboot controller參數(shù)注入方式

    springboot controller參數(shù)注入方式

    這篇文章主要介紹了springboot controller參數(shù)注入方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • @Autowired注解以及失效的幾個原因圖文詳解

    @Autowired注解以及失效的幾個原因圖文詳解

    在微服務(wù)項目中,會遇到@Autowired注解失效的情況,下面這篇文章主要給大家介紹了關(guān)于@Autowired注解以及失效的幾個原因的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-03-03
  • MyBatis 添加元數(shù)據(jù)自定義元素標簽的實現(xiàn)代碼

    MyBatis 添加元數(shù)據(jù)自定義元素標簽的實現(xiàn)代碼

    這篇文章主要介紹了MyBatis 添加元數(shù)據(jù)自定義元素標簽的實現(xiàn)代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • idea?compile項目正常啟動項目的時候build失敗報“找不到符號”等問題及解決方案

    idea?compile項目正常啟動項目的時候build失敗報“找不到符號”等問題及解決方案

    這篇文章主要介紹了idea?compile項目正常,啟動項目的時候build失敗,報“找不到符號”等問題,這種問題屬于lombok編譯失敗導(dǎo)致,可能原因是依賴jar包沒有更新到最新版本,需要的朋友可以參考下
    2023-10-10
  • Spring Boot跨域問題詳解

    Spring Boot跨域問題詳解

    在Spring Boot中處理跨域問題非常簡單,你可以通過全局配置、注解或自定義過濾器的方式來控制跨域請求的行為,本文給大家介紹Spring Boot跨域問題簡介,感興趣的朋友跟隨小編一起看看吧
    2023-09-09
  • java實現(xiàn)jdbc批量插入數(shù)據(jù)

    java實現(xiàn)jdbc批量插入數(shù)據(jù)

    這篇文章主要為大家詳細介紹了java實現(xiàn)jdbc批量插入數(shù)據(jù),三種JDBC批量插入編程方法進行比較,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • RocketMQ整合SpringBoot實現(xiàn)生產(chǎn)級二次封裝

    RocketMQ整合SpringBoot實現(xiàn)生產(chǎn)級二次封裝

    本文主要介紹了RocketMQ整合SpringBoot實現(xiàn)生產(chǎn)級二次封裝,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-06-06
  • JVM之方法返回地址詳解

    JVM之方法返回地址詳解

    這篇文章主要介紹了JVM之方法返回地址詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Spring Boot 將yyyy-MM-dd格式的文本字符串直接轉(zhuǎn)換為LocalDateTime出現(xiàn)的問題

    Spring Boot 將yyyy-MM-dd格式的文本字符串直接轉(zhuǎn)換為LocalDateTime出現(xiàn)的問題

    這篇文章主要介紹了Spring Boot 將yyyy-MM-dd格式的文本字符串直接轉(zhuǎn)換為LocalDateTime出現(xiàn)的問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09

最新評論