JPA Eclipselinkを使用して、マルチレイヤー環境(REST + EJB + Core)でDBに接続する方法は?
-
29-09-2019 - |
質問
Glassfish V3サーバーを使用しています。
通常、EJB3 + JPA(eclipselink)とのDB接続は、 @persistenceunitまたは@persistenceContextを使用して、インジェクションを通じて行われます。
ただし、アプリには3つのレイヤーがあります。
コア(ビジネスロジック、エンティティ、例外処理などが含まれます)
その上にEJBがあり、正しいコアオブジェクトとメソッドを呼び出してジョブを行います。このEJBは、ERPの他の内部モジュールによって呼び出されます。
FrontEnd Webサイトによる使用のために、その上のRESTレイヤー。
EJBのEntityManagerもEMF(EM Factory)も取得したくありません。なぜなら、その下にDBが使用されていることに中央の層が気づかないことを望んでいるからです。結局のところ、後で、非DB使用のコア実装のコア実装を変更することを決定できました。
私は2つの悪い解決策だけを見ます:
1)DB接続が必要なコアレイヤーのメソッドを呼び出すたびにEMパラメーターを追加します。とても醜く、私が上で言ったことを再び行きます。
2)DB接続を必要とするコアのすべての方法で、工場、EMを作成し、それらを使用してから両方を閉じます。
コアレベルのクラスごとに1つの工場を備えた中央で物事をカットしようとしましたが、すべての方法でEMSが作成され、閉じられています。しかし、私はまだこのようなメモリリークがあります:
javax.servlet.ServletException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.0.v20091127-r5931): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Error in allocating a connection. Cause: In-use connections equal max-pool-size and expired max-wait-time. Cannot allocate more connections.
私のEJBメソッドの1つが10の異なるオブジェクトを使用している場合、10のEM工場を作成し、どれも閉じられていないためだと思います。
コアオブジェクトでの典型的な使用の例:
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// do some stuff with em; for example persist, etc
em.flush();
em.close();
ソリューション2に行くべきですか?このコアレベルで単一のEM工場を使用する方法はありますか? JPA Specは、EJBレベルのみでエンティティを使用すると想定しているようです。これは、マルチレイヤーアプリでは悪いです。
編集:@injectを試した後の現在のステータスは次のとおりです。
私のコアジャーに /meta-infディレクトリに空のbeans.xmlファイルを追加しました。
親DAOクラスは今こうなりました:
パブリッククラスExampleBZL {
public EntityManagerFactory emf; @Inject public Emf emfobject; public ExampleBZL() { this.emf = emfobject.emf; }
EMFクラスは非常にシンプルで、無国籍です。
@Stateless Public Class EMFはEMFABSTRACTを実装しています{
@PersistenceUnit(unitName = Setup.persistenceUnitName) public EntityManagerFactory emf; public Emf() { }
}
私は何か間違ったことをしているに違いありませんが、注入は機能しません。グラスフィッシュのaltoughは、エンジンリストに「[ejb、weld、web]」を見るので、CDIがロードされます。
Servlet.service() for servlet Jersey Web Application threw exception
java.lang.NullPointerException
at com.blablabla.core.bizlogic.ExampleBZL.<init>(ExampleBZL.java:40)
他の注釈が足りないのですか?これらの2つの小さな注釈(片側に無国籍、もう片方に注入)を使用して、瓶でイネ症を行うために本当に機能していますか?
解決
2つのセッション豆を持っている場合はどうなりますか? 1つはJTAを活用できる注入されたEntityManagerを使用し、もう1つは現在のセッションBeanです。
私は現在、シリーズをまとめています 私のブログ Eclipselink&Glass Fish v3を使用して、セッションBeanをRESTサービスとして使用する:
以下は、セッションビーンにエンティティマネージャーを挿入する方法です。
package org.example.customer;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.ws.rs.Path;
import org.eclipse.persistence.rest.JPASingleKeyResource;
@Stateless
@LocalBean
@Path("/customers")
public class CustomerService {
@PersistenceContext(unitName="CustomerService", type=PersistenceContextType.TRANSACTION)
EntityManager entityManager;
}
@EJBアノテーションを使用して、セッション豆をリンクできます。
package org.example;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.naming.Context;
import javax.naming.InitialContext;
@Stateless
@LocalBean
@EJB(name = "someName", beanInterface = CustomerService.class)
public class OtherSessionBean {
public Customer read(long id) {
try {
Context ctx = new InitialContext();
CustomerService customerService = (CustomerService) ctx.lookup("java:comp/env/someName");
return customerService.read(id);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
他のヒント
Javaee 6を使用すると、コアレベルのクラスを豆として定義し、そこにリソースを注入できます。 Javaee 6でコンテキストと依存関係注射(CDI)を確認してください。
それが良い答えかどうかはわかりませんが、私はこれを見つけました:フォーラムのリンク 言う:
JPAとCDI間の相互作用は考慮されたが、何らかの未知の理由で仕様の一部にはなされなかったようです。理由を知るようになったら、このスレッドを更新します。その間、それは適切な人々へのフィードバックとして送られてきました。したがって、これは間違いなくグラスフィッシュのバグではありません。
それで、それは、Javaクラスの(エンティティマネージャーを含むクラスの)私の@InjectがJavaクラスで機能していない理由を説明していますか?
Blaiseのおかげで、これが最終作業コードです:
接続を「受信」する父親のクラス
com.wiztivi.apps.wsp.billing.interfaces.bin.db.newinterfaceをインポートします。 javax.ejb.localbeanをインポートします。 javax.ejb.Statelessをインポートします。 javax.naming.contextをインポートします。 javax.naming.initialContextをインポートします。 javax.persistence.entitymanagerをインポートします。
@Stateless @LocalBean public class FatherService { public EntityManager em; public FatherService() { } public EntityManager getGoodEm() { try { Context ctx = new InitialContext(); NewInterface dp = (NewInterface) ctx.lookup("java:global/billing-ear/billing-connection/DataProvider"); em = dp.getEm(); } catch(Exception e) { throw new RuntimeException(e); } return em; } }
接続を「提供」するクラス(エンティティとの別の接続jarで)
javax.ejb.localbeanをインポートします。 javax.ejb.Statelessをインポートします。 javax.persistence.entitymanagerをインポートします。 javax.persistence.persistenceContextをインポートします。 javax.persistence.persistencecontexttypeをインポートします。
@Stateless @LocalBean Public Class DataProviderはnewInterfaceを実装しています{
@PersistenceContext(unitName=Setup.persistenceUnitName, type=PersistenceContextType.TRANSACTION) public EntityManager entityManager; public DataProvider() { } @Override public EntityManager getEm() { return entityManager; }
}
重要なこと:FatherService EJB(私の場合、残りのクラス)を呼び出す「高レベル」レイヤーのクラスに@Statelessを入れなければなりません。コアレイヤーはEJBとしてパッケージ化する必要があります。両方とも耳に