2017-03-13 20 views
0

Springブート、spring-data-jpaに基づく大規模なREST Webアプリケーション。私は、ParentEntityとManyToOneの関係を持つPersonEntityを持っています。関係の所有者はPersonEntityで、彼のテーブルにはparentIdがあります。エンティティを保存するときのConcurrentModificationException

多くのSoapUI同時テストを実行すると、ランダムに1つの場所にあるConcurrentModificationExceptionがスローされていることがわかりました(ランダムに、実際には1年以上続いていますが、別のスレッドから同時にアクセスしています

@Override 
@Transactional 
public void disconnectPersonFromParent(final String parentId, final String personId) { 
    //throw NotFound when null from repo 
    final PersonEntity personEntity = personService.getPerson(personId); 
    //throw NotFound when null from repo   
    final ParentEntity parentEntity = parentService.getParent(parentId); 
    if(parentEntity.equals(personEntity.getParent())) { 
     personEntity.setParent(null); 
     //Addresses comes from parent, when we disconnecting we have disconnect addresses also 
     personEntity.setAddresses(new HashSet<>()); 
     personRepository.save(personEntity); 
    } 
} 

スタックトレース:

||ERROR|http-nio-auto-1-exec-487|error|[TransactionSystemExceptionMapper:353]|Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction 
Caused by: javax.persistence.RollbackException: Error while committing the transaction 
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:94) 
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517) 
    ... 130 more 
Caused by: java.util.ConcurrentModificationException 
    at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719) 
    at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:752) 
    at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:750) 
    at org.hibernate.internal.util.collections.IdentityMap.entryArray(IdentityMap.java:163) 
    at org.hibernate.internal.util.collections.IdentityMap.concurrentEntries(IdentityMap.java:75) 
    at org.hibernate.event.internal.AbstractFlushingEventListener.postFlush(AbstractFlushingEventListener.java:379) 
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:57) 
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258) 
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425) 
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101) 
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177) 
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77) 
    ... 131 more 

はそれがプロであるという問題が発生する)

コードコードの邪魔?どうすればそれを避けることができますか?

+0

あなたは正しいです、PersonEntityインスタンスが別のスレッドによって更新され、DBに保存されました。そのため、このエラーが発生しています。さまざまな理由で行がロックされている、テーブルがロックされている – vhula

+0

[OK]を、どうすれば解決できるかを質問します。 – ByeBye

答えて

0

データベースのすべてのテーブルにVERSIONカラムを追加し、エンティティで以下のように@Versionで注釈を付けます。

@Version 
private Long version 

バージョン列を配置すると、JPA/Hibernateはエンティティを保存する前にバージョンを自動的にチェックします。エンティティのロードと保存の間にバージョンの不一致があると、javax.persistence.OptimisticLockExceptionが発生します。あなたは例外をキャッチし、ログ、ユーザへの通知などのような必要なアクションをとることができます。@Versionの実装に関してネット上でさまざまな記事があります。

希望します。

Repoコールの周りに例外処理サービスメソッドを持つことができます。または、例外をキャプチャして共通に扱うために @COntrollerAdviceを追加します。次の例は、すべてのREST呼び出しが応答として "カスタムメッセージ"を含む500エラーを送信するためのものです。

@ControllerAdvice 
public class ExceptionHandlerAdvice extends ResponseEntityExceptionHandler { 

@ExceptionHandler(value = { Exception.class }) 
protected ResponseEntity<Object> handleAllExcecption(Exception ex, WebRequest request) { 
    // Do what ever you want. Log the error message, send notification etc 
    return handleExceptionInternal(ex, logId.longValue(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, 
      request); 
} 

}

+0

すべてのリポジトリ操作はtry-catchにする必要がありますか? – ByeBye

+0

サービスメソッドには、repo呼び出しの周りにtry catchが必要です。または、Serviceクラスから例外をスローするだけで、以下のような共通の例外処理を行うことができます。 Springには@ControllerAdvice機能があります。これは、コントローラメソッドから来るすべての例外を処理します。 – VimalKumar

+0

ええと、私は答えを出すのではなく、リソースを待っているときに解決策を模索していますが、あなたの答え – ByeBye

関連する問題