JPAでちょっと嵌った話。

最近、JPAを使うことがあったのですが、慣れてなくて結構ハマってしまいました。

高機能だし、面倒なことを簡単に書ける素敵な感じなのですが、普通のことを普通にかけない残念ちゃんでもあります。

 

メモがてら嵌ったこと書いてきます。表題は当時の気持ちです。

 

参照しかしてないのに、DELETE文が発行されているだと!?

JPAの真骨頂その1「理不尽な最適化」。

JPQLはSQLライクな文法なので書いたのと等価のSQLが発行されることを期待しがちですが、割と違います。

単なるSELECTでも場合によっては1次テーブルを作ったり、INSERTもUPDATEやらDELETEを発行するケースもあるので、要注意です。結果が同じになるだけで何が生成されるかはわからない、と思っておくほうが吉でしょう。

基本的にはより高パフォーマンスなコードを吐いてるはずですが、DB設計や業務要件と合わない場合も考えられるので、必ずログレベルをトレースにしてから実際に生成されるSQLを確認しましょう。

合わない場合は、さくっとNativeQueryを使うのが良いです。

 

馬鹿な!? PKアクセスのレスポンスが.... ありえん!

JPAというよりEJBなのですが、CMTでのトランザクション管理はとても便利です。

とても便利ですが、パフォーマンスに重大な問題が生じるケースがあります。

今ひとつ、正確なメカニズムを解明していないのですが、同じトランザクション内でリソース領域のような物が決まっているようです。なので、同一トランザクション内で大量のクエリを発行すると関係ないクエリのレスポンスが何故か遅延します。

直接コマンドやJDBC経由なら1msかからないPKに対する単純なSELECTに500msとか平気でかかります。

解決方法の一つに下記のようにConnectionをひっぱってJDBCを使う方法があります。

Connection con = entityManager.unwrap(java.sql.Connection.class);
PreparedStatement pst = con.prepareStatement("SELECT FROM EMPLOYEES WHERE ID = ?");

これはこれで便利ですが、低レベルで書きづらいのと、複数のPersistManagerから引っ張ってこようとすると

Cause: java.lang.IllegalStateException: Local transaction already has 1 non-XA Resource:
cannot add more resources.

というエラーが出ますので、分散トランザクション経由で書く面倒な感じに。。。

 

他の方法としては、下記のようにトランザクションで。NON_SUPPORTEDを指定する方法。

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import static javax.ejb.TransactionAttributeType.*;

@Stateless
@TransactionAttribute(NOT_SUPPORTED)
public class FoobarBean {

どうやら、こっちが正道っぽい。チュートリアルにもトランザクションはオーバヘッドがあるから、パフォーマンス稼ぐならコレだね! って書いてあるし。

言うまでもないけど、トランザクションが不要なところのみでしか使えないのでご注意を。

 

まあ、総じてちゃんとドキュメント嫁! ってことですね。

JDBCを薄くラッパーしたほうが幸せだと思うけどきっと気のせいだろう。