2016-11-12 124 views
0

Grails 2.5.5事務問題。grails 2.5:failOnError:false在錯誤時總是失敗。如何不失敗的錯誤?

我們有一個get Balance調用API調用,它是一個調用少量服務的簡單控制器。它做2件事,用客戶賬戶返回餘額並更新客戶會話(就像保持活着)。

問題是,當客戶在同一時間要求爲getBalance兩次,而不是一個總是拋出一個異常,即使在保存(),我們有failOnError:假(和推杆嘗試捕捉周圍沒有幫助,見下文) 。我們需要refreshSession以默默的方式失敗,如果其他人當前正在刷新會話,並返回帳戶餘額,就好像沒有任何錯誤。我們無法弄清楚如何在grails中做到這一點failOnError:false和try catch不工作,丟棄和刷新都沒有任何影響。

SessionService:

boolean refreshSession(Session aSession) { 
    if (aSession != null) { 
     aSession.lastUpdatedAt = new Date() 
     aSession.save(flush:true, failOnError: false) // line 569. This always fails on error even with failOnError set to false. 
     return true 
    } 
    return false 
} 

這是我們得到的錯誤:

org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Object of class [com.xxx.Session] with identifier [23]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx.Session#23] 
at com.xxx.services.SessionService$$EQ2LbJCm.refreshSession(SessionService.groovy:569) 
at com.xxx.getBalance(AccountController.groovy:56) 

怎麼會失敗,如果failOnError =假?

會話服務是與否有關它沒有交易電學性能的研究定義的控制器稱爲:

控制器:

def getBalance() { 
try { 
     : 
     Session session = sessionService.getSession(payload.token) 

     sessionService.refreshSession(session) // this updates the session object 

     def accountResult = accountService.getBalance(session.player) // line 59: this returns a structure of account domain objects 
                     // which have a balance and currency etc. It is ready only! 

     render(status: 200, contentType: 'application/json') { 
      [ 
        'result' : 0, 
        'accounts': accountResult.accounts 
      } 
     return 

    } catch (Exception e) { 
     renderError(ERROR, e.toString()) 
     log.error("getBalance API Error:" + e.toString(), e) 
    } 

我們曾嘗試:

  1. 使refreshSession事務性和非事務性(相同的結果)
  2. 從refreshSession中刪除「flush:true」。 (相同結果)
  3. 在刷新會話的身體周圍加入try catch(Exception e)。這沒有發現異常。 (相同結果)
  4. 在refresh會話體周圍添加try catch(Exception e)並創建refreshSession NotTransactional。

有趣的是,最後一個改變發生在到下一行異常的行(該一個讀取帳戶,其不寫入,僅讀出)

org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Object of class [com.xxx.Session] with identifier [23]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx.Session#23] 
at com.xxx.getBalance(AccountController.groovy:59) 
  • 嘗試丟棄()。沒有幫助。

    @NotTransactional 
    
    boolean refreshSession(Session aSession) { 
        if (aSession != null) { 
         try { 
          aSession.lastUpdatedAt = new Date() 
          aSession.save(failOnError: false) 
         } catch (Exception e) { 
          aSession.discard() 
          aSession.refresh() 
         } finally { 
          return true 
         } 
        } 
    
    return false 
    } 
    
  • 用flush:true試過上面的問題。同樣的結果。

  • 嘗試使refreshSession在自己的事務中獲取自己的會話副本。

    @Transactional 
    boolean refreshSession(long id) { 
        Session session = Session.get(id) 
        if (session != null) { 
         try { 
          session.lastUpdatedAt = new Date() 
          session.save(flush: true, failOnError: false) 
         } catch (Exception e) { 
          println("XXX got exception:" + e.message) 
          session.discard() 
         } finally { 
          return true 
         } 
        } 
        return false 
    } 
    
  • 這失敗,原始異常。在grails中忽略寫入失敗似乎是不可能的。即使異常被捕獲,並且它會打印出「XXX got exception」,異常再次拋出,原因不明。

    我做的一切的理解是非交易它的工作原理就像自動提交:真正的 - 每一個DB更新會立即發生,如果一件事情失敗,它不會回滾其他的東西?

    任何想法?

    回答

    1

    我想你需要.withNewTransaction

    boolean refreshSession(long id) { 
    Session.withNewTransaction { 
    Session session = Session.get(id) 
        if (session != null) { 
         try { 
          session.lastUpdatedAt = new Date() 
          session.save(flush: true, failOnError: false) 
         } catch (Exception e) { 
          println("XXX got exception:" + e.message) 
          session.discard() 
         } finally { 
          return true 
         } 
        } 
        } 
        return false 
    } 
    

    在所提供,它被返回的最新狀態,因爲該元素可能已經在過程中更新,同時也得到了記錄,並試圖檢查它的鏈接。您還可以查看isDirty以及getPersistentValue。個人認爲用NewTransaction打包將解決這個問題。你說你想放棄第二次點擊,但實際上不是有效的和最新的lastUpdatedAt?

    +0

    Session.withNewTransaction解決了它。我之前沒有看到過這個構造。另外,我曾假設,如果控制器不是跨國的,並且服務是交易,那麼服務將在該服務方法的持續時間內創建新的交易。我(和我所有的Grails同事)有多大錯誤。有沒有人找到一個博客或文檔來詳細說明交易如何在Grails中工作? –

    3

    How can it fail if failOnError = false?

    您提到過HibernateOptimisticLockingFailureExceptionfailOnError與此沒有任何關係。 failOnError控制在.save()期間發生驗證錯誤時是否拋出異常。這不會影響HibernateOptimisticLockingFailureException