ひとつ前のエントリでも説明したのですが、JPAのデフォルトではDMLの実行はコミット時に行われます。
それはJPA仕様ということで理解できるのですが、
タチの悪いことに、DML文の実行順序はEntityManagerの更新系メソッドの呼び出し順序と一致しないことがあります。
どうやらJPAの仕様によると、DMLの実行順序を最適化することが許されているようです。
しかしこの最適化がダメダメなようで、そもそも「特定の順番でSQLを実行しないとエラーになる!(ex. UPDATE→DELETE)」というケースでも問答無用で順序を変えやがります。
で、まんまと一意性制約違反に引っ掛かったりして落ちる、という具合です。
調べた結果、JPAの標準APIではDMLの実行順序を制御する手段はないようです。
唯一可能なのが、処理の区切りごとにEntityManager#flushを呼び出して強制的にSQLを実行させる方法。
しかし開発者がいちいちflushするタイミングを意識してコーディングするということは、
ある意味トランザクション制御のコードを開発者に書かせることに近いものがあると思います。
これだとバグの温床になりかねません。
一応、EclipseLink固有APIを使用することで、DELETE文だけですが順序を優先させることが可能です。
次のようにすればよいです。
final JpaEntityManager jpaEntityManager = (JpaEntityManager) entityManager.getDelegate(); final UnitOfWork unitOfWork = jpaEntityManager.getUnitOfWork(); // DELETE文を最初に実行させる設定 unitOfWork.setShouldPerformDeletesFirst(true);