web-dev-qa-db-ja.com

CDI環境でEntityManagerライフサイクルを管理する方法(Tomcatを使用)

私は1つのアプリケーションを開発していて、CDIJSFおよびJPAとともに使用し始めました。 WebコンテナはTomcatです。

私はEntityManager BeanのCDIライフサイクルについて非常に混乱しており、私の心の中のいくつかのものを取り除くために良いアドバイスが必要になります。一般的に私が読んだのは、EntityManagerは主にJava EEコンテナで使用し、PersistenceContextアノテーションを使用して注入する必要があるということです。したがって、コンテナはその寿命を管理します。ただし、Java EEコンテナーをTomcatとして使用しない場合は、EntityManagerの寿命を管理する必要があります。

Tomcat, CDI, JSF and JPAを使用して、今、私の最良のオプションはどれですか?私が現在行っていることは次のとおりです。

public class EntityManagerFactoryProducer {

    public static final String TEST = "test";

    @Produces
    @ApplicationScoped
    public EntityManagerFactory create() {
        return Persistence.createEntityManagerFactory(TEST);
    }

    public void destroy(@Disposes
    EntityManagerFactory factory) {
        factory.close();
    }
}

public class EntityManagerProducer {

    @Inject
    private transient Logger logger;

    @Inject
    private EntityManagerFactory emf;

    @Produces
    public EntityManager create() {
        return emf.createEntityManager();
    }

    public void destroy(@Disposes
    EntityManager em) {
        em.close();
        logger.debug(String.format("%s Entity manager was closed", em));
    }
}

@Named
@ViewScoped
@Interceptors(LoggingInterceptor.class)
public class ProductBacking implements Serializable {

    @Inject
    private ProductDAO productDAO;

@ViewScoped
public class ProductDAOImpl implements ProductDAO, Serializable {
    private static final long serialVersionUID = 4806788420578024259L;

    private static final int MAX_RANDOMIZED_ELEMENTS = 3000;

    @Inject
    private transient Logger logger;

    @Inject
    private EntityManager entityManager;

    @Override
    public List<Product> getSuggestedProducts() {
        logger.debug(String.format("%s Entity manager get products", entityManager));

        return entityManager.createQuery("SELECT p FROM Product p ORDER BY random()", Product.class).setMaxResults(
                MAX_RANDOMIZED_ELEMENTS).getResultList();
    }

    @Override
    public void saveProduct(Product product) {
        logger.debug(String.format("%s Entity manager save product", entityManager));

        entityManager.getTransaction().begin();
        entityManager.merge(product);
        entityManager.getTransaction().commit();
    }

    @PreDestroy
    void destroy() {
        entityManager.close();
    }
}

したがって、基本的には、これを達成するために単純なCDIを使用しています。ただし、これが標準的な方法であるかどうかはわかりません。さらに重要なのは、Beanの寿命が終わった後にEntityManagerを閉じる方法がわかりません。まとめとして:閉じられていない接続(EntityManagers)が多数発生するため、メモリリークが発生します。

誰かが私がどのように進むべきかを理解するのを手伝ってくれる?

20
Ioan

CDIについてではありません。 EntityManagerのライフサイクルは、そのタイプによって異なります。

  1. コンテナ管理のトランザクション、
  2. コンテナ管理の拡張、
  3. アプリケーション管理。

最初の2つは、本格的なアプリケーションサーバーでのみ使用できます。したがって、サーブレットコンテナを使用する場合は、3番目のオプションに絞り込みます。

アプリケーションでEMを明示的に開いたり閉じたりする必要があります。簡単です。EntityManagerFactoryのアプリケーション全体のインスタンスを作成し、それをすべてのBeanに注入します。 EMが必要な場合は、作成して使用し、Beanのコンテキストが終了するのを待たずにすぐに閉じます。この構成では、開いているEntityManagerは接続を保持するため、存続期間の長いBeanを使用すると接続が不足します。 ObjectDBマニュアルの JPAデータベース接続の取得 セクションに、簡単で包括的な説明があります。

19
Yuri

CDI Scopes を使用して、CDI Beanの状態を維持できます。実際にはEntityManagerProducer#create()にはスコープがありません。 WeldまたはOpenWebBeanのいずれかを設定するために構成/インストールするCDIのRIが何であれ、cdi Beanの状態をbelwoとして定義できます。

_@Produces @RequestScoped
public EntityManager create() {
    return emf.createEntityManager();
}
_

あなたの問題は

_1. CDI, JSF and JPA2.  
2. Managing EntityManager lifecycle when using JPA in a non enterprise environment (e.g. Tomcat)
_

1。 CDI、JSF、JPA2

Tomcatコンテナーは、JSFであっても、そのままではCDIをサポートしていません。開発者は、JSF jarを自分で提供する必要があったことを知っています。JSF2.2には、新しいCDI互換スコープ@ViewScopedがあり、同等のCDIのみ@FlowScopedがあります。 @ManagedBeanの場合。

(1)本当にCDIまたはCDI + JSF + JPAを使用したい場合は、TomcatをTomEEにアップグレードするか、TomEEを使用してください。 Tomcat + Java EE = TomEE.The Java Enterprise Edition of Tomcat、TomEEを使用すると、JPAでTomcatを取得できます。

(2)Tomcatサーバーのアップグレードを制御できない場合は、iを実行する必要がありました。 CDIと他のいくつかのjarファイルおよび構成ファイルをweapp itとともに提供します。 ii。 TomcatへのCDIのインストール(Weld、またはOpenWebBeansこれらは両方とも主要なCDI実装です)

(3)Tomcat8。Tomcat8はJava EE 7。

2)EntityManagerライフサイクルの管理

非エンタープライズ環境(Tomcatなど)でJPAを使用する場合のEntityManagerライフサイクルの管理またはJava SEはカスタムタスクです。この状況では、使用するEntityManagerの適切なスコープを検討し、作業中にリソースでは、不要になったときにリソースを確実に閉じることが常に重要です。

_There are three main types of EntityManagers defined in JPA.

    Container Managed and Transaction Scoped Entity Managers
    Container Managed and Extended Scope Entity Managers
    Application Managed Entity Managers
_

JPAを使用すると、EntityManagerとトランザクションの2種類のリソースを処理できます。この場合は、使用するEntityManagerの適切なスコープを検討する必要があります。

_1. An EntityManager is not a heavyload object.
   There is no need to use the same EntityManger longer than needed,
   You can't use an EntityManager instance for the whole application lifecycle (application scope) for the EntityManager is not Thread-safe)
2. It's not safe to traverse lazy-loaded relationships once the EntityManager is closed (This situation will change as of JPA 2.0).
i.)Method scope (i.e. instantiate/destroy one EntityManager in each business method).
   The method scope is not enough for every situation. There could be some scenarios where you'll need a wide scope, such as the following situations:
   i.  When transactions spread multiple business methods.
   ii. Need to traverse lazy-loaded relationships outside a method (e.g. in a JSF page).
   In method scope be careful to ensure the EntityManger is always closed
  ii.)Request scope (on-demand creation of the EntityManager instance to use within the request service)
   EntityManager per HTTP request strategy with the following features:
    i.  Creation on demand of the EntityManager.
    ii. Lazy closing of the EntityManager. 

The main benefit of this scope is derived from the delayed closing of the EntityManager (it will last as long as a HTTP request is in process).
Every queried entity will be managed till the end of the request and therefore during the presentation phase (the render phase in JSF for instance).
_

あなたの場合、あなたはあなたがアプリケーションエンティティマネージャーとアプリケーションマネージドトランザクションを使っています、それはトランザクションを処理することになっているあなたのコードを意味します。一言で言えば、それは意味します:

あなたが呼ぶ:

_entityManager.getTransaction().begin(); //to start a transaction
_

その後、成功した場合は、電話することを確認します

_entityManager.getTranasaction().commit(); //to commit changes to database
_

または失敗した場合は、必ず電話してください:

_entityManager.getTransaction().rollBack();
_

ここで、begin(), commit() or rollback()を呼び出すタイミングを認識しているコンテナがあるとします。これは、コンテナ管理のトランザクションです。

12
Asif Bhutto

主な問題は、エンティティマネージャープロデューサーにスコープがないことです。その結果、クリーンアップされないのは依存しています。エンティティマネージャーのスコープを提供する必要があります。

もう1つは、Apache DeltaSpikeがすでにこれを解決していることです。 DeltaSpikeを使用しないのはなぜですか? https://deltaspike.Apache.org/documentation/jpa.html

5
John Ament