クエリ“レイヤー”の良いデザインとはJava JPA用
質問
JPAでは、エンティティは注釈付きのプレーンオールドJavaオブジェクトです。しかし、私はそれらとデータベースと対話する良い方法を見つけていません。
現在のアプリでは、基本設計では常にシーケンスベースのIDを主キーとして使用するため、通常はPK以外のプロパティでエンティティを検索する必要があります。
各エンティティには、ステートレスEJBがあります
@Stateless
public class MyEntApiBean implements MyEntApi {
@PersistenceContext(unitName = "xxx") @Inject EntityManager entityManager;
すべてが何らかのバリエーションであるクエリメソッドを使用する
/**
* @return A List of all MyEnts that have some property
* @param someProp some property
*/
public List<MyEnt> getAllMyEntsFromProp(final String someProp) {
try {
final Query query = entityManager.createQuery("select me from MyEnt me where me.someProp = :someProp");
query.setParameter("someProp", someProp);
return query.getResultList();
} catch(final NoResultException nre) {
log.warn("No MyEnts found");
}
return new ArrayList<MyEnt>();
}
だから:
-
これらのメソッドがEJBにあるのは本当に嫌いです。なぜなら、それらはエンティティ自体に属しているように見え、EJBのローカルインターフェイスは私をがっかりさせるからです。
-
各メソッドに&quot; try、createQuery、getResultList、catch、log、return&quot;を含む重複が嫌いです。 (ほとんどクロージャーがないか、「withステートメント」またはJavaでの結果)。
誰かが私の問題の一方または両方に対処するエンティティおよびデータベースと対話するためのより良い方法についての提案を持っていますか?
現在、ジェネリックとリフレクションを使用していくつかの基本メソッドを実行し、いくつかのジェネリッククエリメソッドを取得して重複を減らすことを考えています(問題2)(後でレビューするためにプロトタイプを作成します)。
ありがとう、 アンダース
解決
Seamを試してください。 クエリオブジェクトが最も多くを行いますあなたのための仕事の、そして彼らは簡単に拡張可能です。または、常に同様のパターンを実装できます。
一般に、SeamはJPAとユーザー層およびビジネス層との間のギャップを埋めるために多くの便利な機能を実行します。 Seam for JSFを使用する必要はありません。
他のヒント
不必要に冗長になっています。 1つには、getResultList()は行が返されないときに例外をスローしません(少なくともEclipseまたはToplinkではそうではありません。他のプロバイダーが異なるとは想像できません)。 getSingleResult()は行いますが、getResultList()は行いません。また、ビルダーパターンを使用することもできます。
@SuppressWarnings("unchecked")
public List<MyEnt> getAllMyEntsFromProp(final String someProp) {
return entityManager.createQuery("select me from MyEnt me where me.someProp = :someProp")
.setParameter("someProp", someProp);
.getResultList();
}
結果のリストが存在する場合は結果のリスト、または存在しない場合は空のリストを返すのに十分なはずです。注意すべき2つのこと:
-
@SuppressWarnings(&quot; unchecked&quot;)は不要ですが、非汎用リストの結果をgetResultList()から汎用リストにキャストする際に回避できない警告を取り除きます。および
-
(通常)MyEntでcreateQuery()呼び出しを@NamedQueryに置き換える価値があるでしょう。一つには、これによりデプロイ時の検証やその他の便利なことが可能になります。
合理的かつ簡潔かつ完全。
多くのテキスト検索を行う場合は、コンパス。
アプリケーションに適しているかどうかはわかりませんが、そうであれば、コードの設計とパフォーマンスの両方を向上させることができます。
実際にSeamを使用しています。 Queryオブジェクトの提案は、Hibernates 基準クエリ(例によるクエリ)機能。それは私が探していたものに非常に近いようです。
基本クラスで、ダッシュジェネリック ....?
Moin!
単一の結果を得るための私のバージョンです(デスクトップJPAアプリケーションでTopLink Essentialsを使用しています):
public class JPA {
@SuppressWarnings ("unchecked")
public static <T> T querySingle(
EntityManager em,
Class<T> clazz,
String namedQuery,
Pair... params)
{
Query q = em.createNamedQuery(namedQuery);
for (Pair pair : params) {
q.setParameter(pair.key, pair.value);
}
List<T> result = q.getResultList();
if ( result.size() == 0 ) {
return null;
}
if ( result.size() == 1 ) {
return result.get(0);
}
throw new
IllegalStateException(
"To many result rows for query: "
+ namedQuery
+ " where "
+ Arrays.toString(params));
}
public static class Pair {
String key;
Object value;
public static Pair param (String key, Object value) {
return new Pair (key, value);
}
public Pair (String key, Object value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return key + "=" + value;
}
}
}
そして使用法:
import static org.sepix.JPA.*;
...
String id = ...
Customer customer = querySingle (em, Customer.class,
"Customer.findByID", Pair.param ("id", id));
または:
String inquiryID = ...
Boolean current = Boolean.TRUE;
Inquiry inq = querySingle (em, Inquiry.class,
"Inquiry.findCurrent",
Pair.param ("inquiry", inquiryID),
Pair.param ("current", current));
よろしく、 ジョシュ。
SpringのJpaDaoSupportを使用することをお勧めします。これはJPAの処理に役立ちます。良い例はこちら http:// github .com / rafalrusin / jpaqb / blob / master / src / test / java / jpaqb / CarDao.java 。
ロジックを適切に分離するには、DAOクラス(データアクセスオブジェクト)とDTO(データ転送オブジェクト)を使用します。通常、DAOにはすべての必要なクエリが含まれ、DTOはフィールドを持つエンティティです。