9

私は次のコードを持っています:

class ServiceA {

   def save(Object object) {
      if (somethingBadComesBack) {
         throw new CustomRuntimeException(data)
      }
   }
}

class ServiceB {

   def serviceA

   def save(Object object) {
      try {
         serviceA.save(object)
         // do more stuff if good to go
      } catch(CustomRuntimeException e) {
        // populate some objects with errors based on exception
      }
   }
}

class ServiceC {

    def serviceB

    def process(Object object) {
       serviceB.save(object)
       if (object.hasErrors() {
          // do some stuff
       }else{
         // do some stuff
       }

       def info = someMethod(object)
       return info
    }
}

class SomeController {

   def serviceC

   def process() {

     def object = .....
     serviceC.save(object) // UnexpectedRollbackException is thrown here

   }
}

ServiceA.save()が呼び出されて例外が発生した場合、は戻りを試みServiceC.save()たときにをスローします。UnexpectedRollbackException

私は次のことをしました:

try {
   serviceC.process(object)
}catch(UnexpectedRollbackException e) {
   println e.getMostSpecificCause()
}

そして私は得ています:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

これを修正する方法をどこから探し始めるかわかりません。

4

2 に答える 2

11

ランタイム例外を使用してトランザクションをロールバックしていますが、それは不正行為です。副作用を利用しています。ランタイム例外は、トランザクションをキャッチする必要がないため、トランザクションを自動的にロールバックします。したがって、トランザクションがスローされた場合、それは予期されていなかったと見なされ、デフォルトの動作はロールバックです。特定の予想される実行時例外に対してロールバックしないようにメソッドを構成できますが、これはややまれです。チェックされた例外は例外をロールバックしません。Javaでは例外をキャッチまたは宣言する必要があるthrowsため、明示的にスローするか、ダックする必要があります。いずれにせよ、再試行する機会がありました。

トランザクションを意図的にロールバックする正しい方法はsetRollbackOnly()、現在のトランザクションを呼び出すTransactionStatusことですが、これはサービスメソッドで直接アクセスできません(withTransactionクロージャへの引数であるため、ブロック内にあります)。org.springframework.transaction.interceptor.TransactionAspectSupportただし、インポートして呼び出すのは簡単TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()です。キャッチする例外がないため、コードを作り直す必要があります。そのため、コードがでロールバックされたことを確認する必要がありますTransactionAspectSupport.currentTransactionStatus().isRollbackOnly()

TransactionStatusこれがGrailsの問題なのか標準的な動作なのかはわかりませんが、これをデバッグしているときに、3つの異なるインスタンスで3つのcommit呼び出しがありました。最初のフラグだけがロールバックフラグを設定しましたが、2番目のフラグは最初のフラグを認識していて問題ありませんでした。3番目のトランザクションは新しいトランザクションと見なされ、表示されていたのと同じ例外をトリガーしたものでした。したがって、これを回避するために、これを2番目と3番目のサービスメソッドに追加しました。

def status = TransactionAspectSupport.currentTransactionStatus()
if (!status.isRollbackOnly()) status.setRollbackOnly()

ロールバックフラグをチェーンします。それはうまくいきました、そして私は得ませんでしたUnexpectedRollbackException

これをチェック済みの例外と組み合わせる方が簡単な場合があります。スタックトレースが不必要に埋められるため、依然として高額ですがsetRollbackOnly()、チェックされた例外を呼び出してスローすると、現在と同じ一般的なワークフローを使用できるようになります。

于 2012-11-20T22:02:08.083 に答える
0

サービスのデフォルトのトランザクション性があなたを悩ませているようで、サービスAでスローされたチェックされていない例外は、一度キャッチされたとしても、トランザクションをロールバックのみに運命づけています。

上記のドキュメントによると、txn伝播レベルはですPROPAGATION_REQUIRED。これは、私のメモリが私に役立つ場合、同じトランザクションがサービスCからAまで共有されることを意味するはずです。save後者からの自動ロールバックを回避するために、サービスAのメソッドにRuntimeExceptionの代わりにチェック例外をスローさせることはできますか?または、それがオプションである場合は、サービスのトランザクションを無効にしますか?

于 2012-11-20T22:01:28.783 に答える