2016-05-24 26 views
1

spring jparepositoryメソッドがスレッドセーフであるかどうか不思議に思っていましたが、スタックフロー記事(Is a Spring Data (JPA) Repository thread-safe? (aka is SimpleJpaRepository thread safe))を読みました。そこから、私はリポジトリメソッドがスレッドセーフであることを理解してから、スレッドセーフティをテストするために1つのPOCを作成しました。私は1つのリポジトリにFormRepositoryと言って、JpaRepositoryを拡張している 'フォーム'エンティティのCRUD操作を行いました。 DAOから、フォームオブジェクトを作成し、そのidを手動で設定してから、フォームオブジェクトを保存する100個のスレッドを呼び出すだけでした。以下はSpring JpaRepositoryメソッドのスレッドセーフティについて

参照のためのコードである: -

@Repository 
public interface FormRepository extends JpaRepository<Tbldynamicform, Long>  { 

Tbldynamicform save(Tbldynamicform tblform); 

@Query("SELECT max(tblform.formid) FROM Tbldynamicform tblform") 
Optional<Integer> findMaxId(); 

} 
......End of Repository above and start of DAO below... 

@Component 
public class DynamicFormDAO implements DynamicFormDAO { 

@Inject 
private FormRepository formRepository; 

public void testThreadSafety() throws Exception { 
    List<Callable<Integer>> tasks = new ArrayList<>(100); 
    for (int i = 0; i < 100; i++) { 

     tasks.add(() -> { 
      try { 

       Tbldynamicform tbldynamicform = new Tbldynamicform();//Set all the required fields for form 
       if (tbldynamicform.getFormid() == null) 
        tbldynamicform.setFormid(findFormID()); 
       Tbldynamicform form = formRepository.save(tbldynamicform); 
       return form.getFormid(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
      return null; 
     }); 
    } 
    ExecutorService executor = Executors.newFixedThreadPool(100); 
    executor.invokeAll(tasks); 

} 

private int findFormID() throws Exception { 
    Optional<Integer> id = formRepository.findMaxId(); 
    if (id != null && id.isPresent() && id.get() != null) { 
     int generatedId = id.get().intValue(); 
     return ++generatedId; 
    } 
    return 0; 
} 
} 

私はこれを行うと、私は、フォームのリポジトリメソッドはスレッドセーフですが、何とか私は、SQL dataintegrityviolationexceptionがいくつか取得していますので、物事が正常に動作していることを想定しましたいくつかのレコードの挿入を失敗させるログの回数。参照のための以下のエラー: -

org.springframework.dao.DataIntegrityViolationException:ステートメントを実行できませんでした。 SQL [n/a];制約["PRIMARY ON ON PUBLIC.TBLDYNAMICFORM(FORMID)"; SQL文: Tbldynamicformに挿入(ClientIDを、copyfromexisting、のCreationDate、formdesc、formmode、フォーム名、formtemplate、formtitle、procutype、ステータス、フォームID)の値(、、、、...

これが作られたのか???私はこれがスレッドの安全性の問題か他のいくつかの問題かどうか考えていますが、私のDAOで作成したすべてのtbldynamicformオブジェクトはスレッドスタックに残っています。

私がsetIdを実行して同期ブロックに保存すると、すべて正常に動作しますが、それは私の意図ではなく、リポジトリメソッドがスレッドセーフであれば必要ではありません。

エキスパート、何か助けてください?

答えて

3

保存タスクはアトミックではありません.2つのスレッドが、同じエンティティの1つが新しいエンティティを保存する前に同じ最大IDをフェッチすることがあります。

そして、リポジトリのsaveメソッドがスレッドセーフであっても、それは役に立たないでしょう。

maxIdはスレッドセーフです。セーブはスレッドセーフですが、各スレッドの実行可能ファイル内のメソッドはスレッドセーフではありません。

+0

はい、私は同じことを考えました。なぜなら、私がsaveとfindformIdをsyncronizedブロックに置くと、すべて正常に動作するからです。 –

0

問題は、findFormID()で最後のIDを取得する方法に由来し、同時コンテキストでは機能しません。

2つのスレッドがIDを同時に要求するとどうなりますか?彼らは同じIDを取得し、同じIDを持つ2つのオブジェクトを作成します。ここにあなたの問題があります。

生成されたIDの統合ソリューションの中には、すでに存在するものがあり、自分が何をしているのか分からない限り、独自の実装を試みるべきではありません。

+0

"2つのスレッドが同時にIDを要求するとどうなるでしょうか?" findFormIDは同期されているため、1つのスレッドだけがIDを取得できます。しかし、問題はsaveとsetformidがアトミックではないということです。 –

1

単純に言えば、スレッドセーフですが、データベースもステートフルです(明示的に)。また、整合性を維持するには、ロック戦略(ロックを保持して同期をとることや、必要に応じて再試行してください)。誰かが別の答えで指摘しているように、単にIDを生成する別の方法(SUIDをチェックアウトする)を使用すると、コードは正常に動作します。

関連する問題