ロールバックのみとしてマークされているため、トランザクションはロールバックされました



Transaction Rolled Back Because It Has Been Marked



ロールバックのみとしてマークされているため、トランザクションはロールバックされました

問題

プログラムは例外org.springframework.transaction.UnexpectedRollbackExceptionを実行します:トランザクションはロールバックのみとしてマークされているためロールバックされ、エラーログスタックは明らかなトレースを見つけることができません。

シーンの復元

入り口:

@Transactional public void saveUserList(List userList) { User user = new User('100', 'Hangzhou', 'Zhang San') serviceWarp.saveUser(user) try { serviceWarp.saveUserList(userList) } catch (Exception e) { log.error('I don't want to do it') } }

内部通話:

public void saveUser(User user) { userMapper.saveUser(user) } @Transactional public void saveUserList(List userList) { for (User user : userList) { userMapper.saveUser(user) / / Make good data in the database, let it throw a primary key conflict exception } }

理由

Springトランザクション管理。別のトランザクションメソッドが呼び出されたときにトランザクションを伝播するトランザクション対応のメソッド。 @Transactionalのデフォルトの伝播メカニズムはPROPAGATION_REQUIREDです。



コミュニケーション行動 意味
PROPAGATION_REQUIRED 現在のメソッドがトランザクションで実行されている必要があることを示します。現在のトランザクションが存在する場合、メソッドはそのトランザクションで実行されます。それ以外の場合は、新しいトランザクションが開始されます。

したがって、fillメソッドが実行されると、同期されたトランザクションがfilledトランザクションにマージされます。同期請求書が異常な場合、try catchによってキャッチされ、スローされません。ただし、トランザクションは引き続きロールバックされます。 DataSourceTransactionManagerクラスのdoSetRollbackOnlyメソッドへのロールバックが実行されると、rollbackOnly = trueが設定されます。

public void setRollbackOnly() { this.getConnectionHolder().setRollbackOnly() } public void setRollbackOnly() { this.rollbackOnly = true }

例外がキャッチされるため、トランザクション全体がブロックされることはありません。トランザクション全体が実行された後、コミットコミットが実行されます。 DefaultTransactionStatusクラスのisGlobalRollbackOnlyメソッドを実行すると、rollbackOnlyがtrueと判断され、ロールバックが実行され、エラーのログが出力されます。 「トランザクションはロールバックのみとしてマークされているため、ロールバックされました」。



if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug('Global transaction is marked as rollback-only but transactional code requested commit') } processRollback(defStatus) // Throw UnexpectedRollbackException only at outermost transaction boundary // or if explicitly asked to. if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { throw new UnexpectedRollbackException( 'Transaction rolled back because it has been marked as rollback-only') } @Override public boolean isGlobalRollbackOnly() { return ((this.transaction instanceof SmartTransactionObject) && ((SmartTransactionObject) this.transaction).isRollbackOnly()) }

いくつかの考え

  1. トランザクションはトランザクションを完了することができます
  2. トランザクションがネストされて伝播されると、try catchの使用量が減り、ロールバックがロールバックされます。
  3. トランザクションがネストされている場合、伝播モードを指定できます。必要に応じて、内部トランザクションを@Transactional(propagation = Propagation.NESTED)または@Transactional(propagation = Propagation.PROPAGATION_REQUIRED_NEW)として指定できます。
コミュニケーション行動 意味
PROPAGATION_REQUIRED_NEW 現在のメソッドが独自のトランザクションで実行されている必要があることを示します。新しいトランザクションが開始されます。現在のトランザクションがある場合、現在のトランザクションはメソッドの実行中に一時停止されます。
PROPAGATION_NESTED トランザクションがすでに存在する場合、メソッドはネストされたトランザクションで実行されることを示します。ネストされたトランザクションは、現在のトランザクションとは関係なくコミットまたはロールバックできます。現在のトランザクションが存在しない場合、その動作はPROPAGATION_REQUIREDと同じです。この種の通信動作に対するベンダーのサポートは異なることに注意してください。リソースマネージャーのドキュメントを参照して、ネストされたトランザクションをサポートしているかどうかを確認できます。