3

Hibernate を使用して postgre データベースに接続するアプリケーションがあります。C3P0 を接続プールとして使用します。

persistence.xml:

<persistence-unit name="tv-europe-core" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider>

- -クラス - -

<プロパティ> <プロパティ名="hibernate.dialect" 値="org.hibernate.dialect.PostgreSQLDialect" />
<プロパティ名="hibernate.connection.password" 値="---パスワード---" />
< property name="hibernate.connection.url" value="---database---" />
<property name="hibernate.connection.username" value="---username---" />

<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.connection.release_mode" value="after_statement" />
<property name="hibernate.connection.autocommit " 値="false" />

<property name="hibernate.c3p0.minPoolSize" value="5"/>
<property name="hibernate.c3p0.maxPoolSize" value="60"/>
<property name="hibernate.c3p0.maxIdleTime" value=" 10"/>
<property name="hibernate.c3p0.idleConnectionTestPeriod" value="5"/>
<property name="hibernate.c3p0.testConnectionOnCheckin" value="true"/>
</properties>
</persistence-unit>

オブジェクトの保存:

public Entity saveOrUpdate(Entity entity, User u) {  
EntityTransaction tx = EntityManagerHelper.getEntityManager().getTransaction();  
try {  
  if(!tx.isActive())  
      tx.begin();  
          Entity result = null;  
      if (getID(entity) == null) {  
      EntityManagerHelper.getEntityManager().persist(entity);  
  } else {  
      result = EntityManagerHelper.getEntityManager().merge(entity);  
  }  
  tx.commit();    
  return result;  
  } catch (RuntimeException re) {  
      re.printStackTrace();  
      tx.rollback();  
      throw re;  
  }  
}  

オブジェクトを読み込んでいます:

@SuppressWarnings("unchecked")
    public List<Entity> findByProperty(String propertyName, final Object value,
            final int... rowStartIdxAndCount) {

        try {
            final String queryString = "select model from " + clazz.getName()
                    + " model where model." + propertyName + "=     :propertyValue";
            Query query = EntityManagerHelper.getEntityManager().createQuery(
                    queryString);
            query.setParameter("propertyValue", value);
            if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0)     {
                int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
                if (rowStartIdx > 0) {
                    query.setFirstResult(rowStartIdx);
                }

                if (rowStartIdxAndCount.length > 1) {
                    int rowCount = Math.max(0, rowStartIdxAndCount[1]);
                    if (rowCount > 0) {
                        query.setMaxResults(rowCount);
                    }
                }
            }
            final List<Entity> result = query.getResultList();
            return result;
        } catch (RuntimeException re) {
            re.printStackTrace();
            throw re;
        }
    }  

EntityManagerFactory の作成と EntityManager の取得:

private static EntityManagerFactory emf;
private static final ThreadLocal<EntityManager> threadLocal = new ThreadLocal<EntityManager>();  

public static EntityManager getEntityManager() throws HibernateException {
    EntityManager session = (EntityManager) threadLocal.get();

    if (session == null || !session.isOpen()) {
        session = (emf != null) ? emf.createEntityManager()
                : null;
        threadLocal.set(session);
    }

    return session;
}

問題は、データベース接続が何度も「トランザクションでアイドル」状態のままになり、その後、この接続が返されないことです。数日後、接続数がプールの最大サイズを超えたため、アプリケーションが応答を停止します。
hibernate hibernate.connection.autocommit が有効になっている場合、これらの接続は「トランザクションでアイドル状態」にはなりませんが、それでも何らかの理由でブロックされ、結果として生じる問題は同じです。

何か間違ったことをしていませんか (構成が不足しているなど)?

熱心な読み込みのみを使用する場合、問題がないことに気付きました。しかし、パフォーマンスのために遅延読み込みを使用する必要があります。EntityManagerFactory を明示的に閉じる必要がありますか? アプリが非常に長い間機能する必要があり、誰かが永続オブジェクトを操作しているときにアプリをリセットすることはできないと思うので、そうでないことを願っています.

ログには次のように表示されますが、それが何らかの形で問題に関連しているかどうかはわかりません。

java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE
    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:491)
    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:191)
    at     com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.dest    royResource(C3P0PooledConnectionPool.java:470)
    at     com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.ja    va:964)
    at     com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunn    er.java:547)  

助けてくれてありがとう!:)

4

3 に答える 3

4

あなたの設定は私には正しく見えません:

  1. transaction-type="RESOURCE_LOCAL" は、JTA 環境にないことを意味します。
  2. property name="hibernate.connection.release_mode" value="after_statement" を auto-commit = false と組み合わせることは非常にまれです。

"after_statement" は、接続プロバイダーがアグレッシブ リリースをサポートしている場合 (および、同じトランザクション内のステートメントごとに同じ接続を返すことができる場合) にのみ使用できます。

AFTER_STATEMENT が休止状態によって無視される可能性があります (休止状態がこのリリースモードがセットアップと互換性がないことを検出するため)、代わりに AFTER_TRANSACTION が使用されます...しかし、間違って使用しないようにするために、

 <property name="hibernate.connection.release_mode" value="auto" />

これにより、非 JTA 環境で AFTER_TRANSACTION リリース モードが設定されます。

これで問題が解決するかどうかはわかりません (既に after_transaction モードで実行している可能性があるため)。(修正されない場合は、コメントしてください。より深い調査が必要になります)。

編集

ところで、sessionFactory の再構築は非常に奇妙に思えます。通常、SessionFactory はシングルトンであり、sessionFactory の主な目的は新しいセッションを非常に効率的に構築することです。一般に、再作成する理由はありません (sessionFactory は静的データのみに依存するため、時間がかかり、役に立ちません)。

sessionFactory を再作成する唯一の理由は、アプリケーションが実行時にデータ モデルを変更する場合 (つまり、新しいテーブルまたは列を作成する場合) です (これらの変更は、同時にアプリは、マッピング ファイルまたはバイト コードを変更します - 新しい注釈、フィールド、およびクラスを追加します)。私はあなたがそれをしていないと思います。

編集2

前の編集で述べたように、sessionFactory の再構築は避けてください。このメソッドをプライベートにして、複数回呼び出されないようにします。コードが sessionFactory を再構築している場合、関連する C3PO セットアップのために新しい SessionFactory がおそらくいくつかの接続を消費するため、それが問題の原因である可能性があります。

その他のポイント:遅延読み込みを無効にすると、もう問題ないと言いました。そのため、遅延読み込み用に作成されたセッションが適切に閉じられていないことが問題の原因である可能性もあります。遅延読み込み操作をデバッグして、セッションがどこから来たのか、セッションが閉じられているかどうかを確認してください。


EDIT 3(最後のコメントへの返信として)

あなたは非常に一般的なアーキテクチャ設計の問題に直面しており、それを解決するには 2 つのアプローチがあるとしましょう。良いものと(非常に)悪いもの。

(非常に) 悪い点: open session in view パターンの使用。

アイデアは、ビューを生成するときにエンティティマネージャーを再度開き、エンティティを再アタッチして、遅延初期化例外が発生しないようにすることです。短期的には、このアプローチは、アプリがうまく動作しているという誤った感覚を与えることになります。多くの同時ユーザーおよび/またはデータベース内のますます多くのレコードを使用して本番環境になると、実際に大きなパフォーマンスの問題が発生する大きなリスクがあります (メモリ使用量および/または応答時間に関して)。

(これらの潜在的な問題の根本的な原因は、小さな DB での開発中に、このビューまたはそのビューが 10 個のオブジェクトを含む遅延初期化されたコレクションをフェッチしていることに気付かないことです...しかし、本番環境では、小さなコレクションが巨大になります10000 オブジェクトで!!!)

これらの問題は、次の理由から修正が困難です。 - それらは複数のビューに存在します。 - ユニット/ロード テストが困難になります (ビュー レイヤーにあるため)。

私の意見では、このアプローチは、膨大な負荷や大量のデータを持つことのない、重要度の低いアプリにのみ使用できます。

良いもの:階層化されたアーキテクチャを使用します。

ビューレイヤーはエンティティマネージャーに触れません。このレイヤーは、コントローラーレイヤーから表示するデータを受け取ります。すべてのデータがそこにあります。ここで遅延コレクションをフェッチする必要はありません。

コントローラー層には 2 つの役割があります。

  • ビジネスロジックを実装する
  • entityManager のライフサイクル (およびトランザクション境界) を管理し、DAO レイヤーで使用できる entityManager を提供します。

さらに、コントローラ レイヤは、ビュー レイヤに完全なオブジェクト グラフを提供する必要があります。完全なオブジェクト グラフとは、ビューがそのコレクションからのデータを表示する必要がある場合、ビュー レイヤーが初期化されていない遅延コレクションを受信しないことを意味します。

DAO 層:

クエリを実行してデータをフェッチするだけです (ここに JPA/HQL/SQL クエリを記述します)。このレイヤーは、コントローラー レイヤーによって提供される entityManager を使用することを除いて、entityManager に対して特別なことは何もしません。

DAO 層は、コントローラー層のすべてのニーズが満たされるように、遅延コレクションの有無にかかわらず、このエンティティを取得するための幅広いクエリを提供する必要があります。

レイヤード アーキテクチャ アプローチの主な利点は、開発プロセスですぐにビューの要件を確認し、必要に応じてクエリを調整して最適化できることです。(つまり、すべての lazy-init 例外を 1 つずつ修正する必要がありますが、ビューの要件をよく理解できます)

于 2013-10-08T14:14:59.333 に答える
0

hibernate.connection.release_mode プロパティの値を after_transaction に変更します。私はそれがあなたの問題を解決するはずだと思います

于 2015-03-17T13:04:22.297 に答える