web-dev-qa-db-ja.com

Hibernateが接続プールから接続を解放しない

Hibernate JPAを使用してアプリケーションを作成しており、MySQLとの接続プーリングにc3p0を使用しています。 MySQLデータベースへの接続の数に問題があり、開いている152の接続にヒットします。c3p0configファイルで最大プールサイズを20に定義し、もちろん、取得するすべてのエンティティマネージャーを閉じるため、これは望ましくありませんすべてのトランザクションをコミットした後、EntityManagerFactoryから。

コントローラーが実行されるたびに、7つを超える接続が開かれていることに気付きます。更新すると、過去のアイドル接続が閉じられることなく、7つの接続が再び開かれます。そして、私が呼び出すすべてのDAO関数で、em.close()が実行されます。私はここで問題が私のコードにあることを認めますが、私はここで何が間違っているのか分かりません。

これはSondage.Javaエンティティです。

_@Entity
@NamedQuery(name="Sondage.findAll", query="SELECT s FROM Sondage s")
public class Sondage implements Serializable {

    private static final long serialVersionUID = 1L;

    public Sondage() {}

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    private byte needLocation;

    //bi-directional many-to-one association to ResultatSondage
    @OneToMany(mappedBy = "sondage", cascade = CascadeType.ALL)
    @OrderBy("sondage ASC")
    private List<ResultatSondage> resultatSondages;

    //bi-directional many-to-one association to SondageSection
    @OneToMany(mappedBy = "sondage", cascade = CascadeType.ALL)
    private List<SondageSection> sondageSections;
}
_

そして、ここに私のDAOクラスがあります:

_@SuppressWarnings("unchecked")
public static List<Sondage> GetAllSondage() {
    EntityManager em = PersistenceManager.getEntityManager();
    List<Sondage> allSondages = new ArrayList<>();
    try {
        em.getTransaction().begin();
        Query query = em.createQuery("SELECT s FROM Sondage s");
        allSondages = query.getResultList();
        em.getTransaction().commit();
    } catch (Exception ex) {
        if (em.getTransaction().isActive()) {
            em.getTransaction().rollback();
        }
        allSondages = null;
    } finally {
        em.close();
    }
    return allSondages;
}
_

ご覧のとおり、emは閉じられています。私のJSPでは、私はこれを行います:これはビュー側で物事を行うための良い方法ではないことを知っています。

_<body>
    <div class="header">
        <%@include file="../../../Includes/header.jsp" %>
    </div>
    <h2 style="color: green; text-align: center;">الاستمارات</h2>
    <div id="allsurveys" class="pure-menu custom-restricted-width">
        <%
            List<Sondage> allSondages = (List<Sondage>) request.getAttribute("sondages");

            for (int i = 0; i < allSondages.size(); i++) {
        %>
        <a  href="${pageContext.request.contextPath }/auth/dosurvey?id=<%= allSondages.get(i).getId()%>"><%= allSondages.get(i).getName()%></a> &nbsp;
        <%
            if (request.getSession().getAttribute("user") != null) {
                Utilisateur user = (Utilisateur) request.getSession().getAttribute("user");
                if (user.getType().equals("admin")) {
        %>
        <a href="${pageContext.request.contextPath }/aauth/editsurvey?id=<%= allSondages.get(i).getId()%>">تعديل</a>
        <%
                }
            }
        %>
        <br />
        <%
            }
        %>
    </div>
</body>
_

私はuser.getType()を呼び出すたびにリクエストが確立されると推測していますか?もしそうなら、どうすればこれを防ぐことができますか?

C4p0構成ファイルについては、persistence.xmlに含めました。c3p0-config.xmlにc3p0構成ファイルを配置する必要があるという記事をいくつか見ましたが、セットアップではc3p0は永続化で渡した値で初期化されます。 .xmlファイル、mysql接続も152接続に到達していますが、maxpoolsizeは20にあります。これがpersistence.xmlファイルです

_<persistence version="2.1"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="CAOE" transaction-type="RESOURCE_LOCAL">
        <class>com.caoe.Models.ChoixQuestion</class>
        <class>com.caoe.Models.Question</class>
        <class>com.caoe.Models.Reponse</class>
        <class>com.caoe.Models.ResultatSondage</class>
        <class>com.caoe.Models.Section</class>
        <class>com.caoe.Models.Sondage</class>
        <class>com.caoe.Models.SondageSection</class>
        <class>com.caoe.Models.SousQuestion</class>
        <class>com.caoe.Models.Utilisateur</class>
        <properties>
            <property name="hibernate.connection.provider_class"
                      value=" org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider" />

            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.password" value=""/>

            <property name="hibernate.connection.url"
                      value="jdbc:mysql://localhost:3306/caoe?useUnicode=yes&amp;characterEncoding=UTF-8"/>

            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.show_sql" value="true" />

            <property name="hibernate.c3p0.max_size" value="50" />
            <property name="hibernate.c3p0.min_size" value="3" />
            <property name="hibernate.c3p0.max_statements" value="20" />
            <property name="hibernate.c3p0.acquire_increment" value="1" />
            <property name="hibernate.c3p0.idle_test_period" value="30" />
            <property name="hibernate.c3p0.timeout" value="35" />
            <property name="hibernate.c3p0.checkoutTimeout" value="60000" />
            <property name="hibernate.connection.release_mode" value="after_statement" />

            <property name="debugUnreturnedConnectionStackTraces"
                      value="true" />
        </properties>
    </persistence-unit>
</persistence>
_

編集:TomcatとMySQLがインストールされたRed Hatサーバーにアプリケーションをデプロイしています。 HibernateがMySQLへの接続をあまりにも多く開いており、すべてのエンティティマネージャーが閉じられているため、接続は開いたままにならないのではないかと思っています。私はこのようなことをするときに接続が開かれていると確信している場合、私は推測して修正しています:

_List<Sondage> allSondages = SondageDao.getAllSondages();

for (Sondage sondage : allSondages) {
    List<Question> questions = sondage.getQuestions();
    //code to display questions for example
}
_

ここでsondage.getQuestions()を使用すると、Hibernateはデータベースへの接続を開き、その後も閉じません。設定ファイルで、完了時に接続を閉じたりプールに戻すものが見つかりません。助けてくれてありがとう。

EDIT2:バージョンを求めているので、ここにあります:Java jre 1.8.0_25 Apache Tomcat v7.0 hibernate-core-4.3.10 hibernate c3p0 4.3.10.final hibernate-jpa 2.1前もって感謝します

MysqlバージョンはMysql 5.6.17です。

編集4:私が投稿したコードの魔女バージョンについて人々が混乱しているので、これが何であるか正確に分かるように編集させてください。

まず、バグのあるコードが何であるかを示すことから始めます。皆さんは何が機能しているか気にしないからです。

_@SuppressWarnings("unchecked")
public static List<Sondage> GetAllSondage() {
    EntityManager em = PersistenceManager.getEntityManager();
    List<Sondage> allSondages = new ArrayList<>();
    try {
       em.getTransaction().begin();
       Query query = em.createQuery("SELECT s FROM Sondage s");
       allSondages = query.getResultList();
       em.getTransaction().commit();
    } catch (Exception ex) {
    if (em.getTransaction().isActive()) {
        em.getTransaction().rollback();
    }
    allSondages = null;
    } finally {
        em.close();
    }
    return allSondages;
  }
_

だから、これは基本的にすべてのdao関数に対して行ったことです。トランザクションはクローズする接続に重要であるという質問を見たので、ここではトランザクションは必要ありません。この横に、EntityManagerFactoryシングルトンオブジェクトを持つPersistenceManagerクラスからgetEntityManagerを取得するため、getEntityManagerはEntityManagerFactoryシングルトンObject:=>コードからentityManagerを作成します。1000Wordよりも優れています:PesistenceManager.Java:

_import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;

    public class PersistenceManager 
    {
    private static EntityManagerFactory emf = null;

    public static EntityManager getEntityManager()
    {
        return getEntityManagerFactory().createEntityManager();     
    }

    public static EntityManagerFactory getEntityManagerFactory()
    {
            if(emf == null) {
                    emf = Persistence.createEntityManagerFactory("CAOE");
                    return emf;
        }
            else
                    return emf;
        }
}
_

はい、これはクールですべて良いですが、問題はどこにありますか?

ここでの問題は、このバージョンでは接続が開かれ、閉じられないことです。em.close()は効果がなく、データベースへの接続を開いたままにします。

Noobの修正:

この問題を修正するために行ったのは、すべてのリクエストに対してEntityManagerFactoryを作成することです。つまり、daoは次のようになります。

_    @SuppressWarnings("unchecked")
public static List<Sondage> GetAllSondage() {
    //this is the method that return the EntityManagerFactory Singleton Object
    EntityManagerFactory emf = PersistenceManager.getEntitManagerFactory();
    EntityManager em = emf.createEntityManager();
        List<Sondage> allSondages = new ArrayList<>();
        try {
            em.getTransaction().begin();
            Query query = em.createQuery("SELECT s FROM Sondage s");
            allSondages = query.getResultList();
            em.getTransaction().commit();
    } catch (Exception ex) {
        if (em.getTransaction().isActive()) {
            em.getTransaction().rollback();
        }
        allSondages = null;
        } finally {
        em.close();
        emf.close();
    }
    return allSondages;
}
_

さて、これは悪いことであり、この質問に対する答えが得られない間はそのままにしておきます(以前のように思われます:D)。したがって、このコードでは、基本的に、Hibernateがそれらを必要としない後にすべての接続が閉じられます。この質問にご協力いただきありがとうございます:)

22
Reda

ここでは、HibernateとC3P0が正しく動作していると思います。実際、C3P0の構成に従って、常に少なくとも3つのデータベースへの接続が開いていることがわかります。

クエリを実行すると、Hibernateはプールからの接続を使用し、完了時にそれを返します。接続は閉じられません。 C3P0は、最小サイズを超え、接続の一部がタイムアウトした場合、プールを縮小する場合があります。

最後の例では、エンティティマネージャファクトリと接続プールもシャットダウンしたため、接続が閉じていることがわかります。

11
Alex Barnes

毎回Persistence.createEntityManagerFactory("CAOE")を呼び出します。違います。各呼び出しcreateEntityManagerFactoryは、新しい(独立した)接続プールを作成します。 EntityManagerFactoryオブジェクトをどこかにキャッシュする必要があります。

編集:

また、EntityManagerFactoryを手動でシャットダウンする必要があります。 @WebListenerで実行できます。

@WebListener
public class AppInit implements ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {}

    public void contextDestroyed(ServletContextEvent sce) {
         PersistenceManager.closeEntityMangerFactory();
    }
}

それ以外の場合、再デプロイの各ケースは、リークされた接続のソースです。

9
sibnick

あなたは次を試すことができます:

<property name="hibernate.connection.release_mode" value="after_transaction" />
<property name="hibernate.current_session_context_class" value="jta" />

現在のリリースモードの代わりに?

3

Sibnickはすでに技術的な質問に回答しているので、あなたが混乱していると思われるいくつかの点を取り上げようとします。それでは、休止状態のアプリケーションと接続プールがどのように機能するかをいくつか考えてみましょう。

  1. データベース接続を開くことは「高価な」操作です。リクエストごとにそのコストを支払う必要を回避するために、接続プールを使用します。プールはデータベースへの特定の数の接続を事前に開き、必要な場合は既存の接続の1つを借りることができます。トランザクションの終了時に、これらの接続は閉じられませんが、プールに返されるため、次の要求で借りることができます。負荷が高い場合、すべての要求を処理するには接続が少なすぎる可能性があるため、プールは追加の接続を開く可能性があります。
  2. EntityManagerFactoryの作成はさらに高価です(キャッシュの作成、新しい接続プールのオープンなど)ので、すべてのリクエストに対してそれを行うことは絶対に避けてください。あなたの応答時間は信じられないほど遅くなります。また、作成するEntityManagerFactoriesが多すぎると、PermGenスペースが使い果たされる可能性があります。したがって、アプリケーション/永続コンテキストごとにEntityManagerFactoryを1つだけ作成し、アプリケーションの起動時に作成します(そうしないと、最初の要求に時間がかかりすぎます)。アプリケーションのシャットダウン時に閉じます。

結論:接続プールを使用する場合、アプリケーションの存続期間中、一定数のDB接続が開いたままになることを期待する必要があります。発生してはならないことは、リクエストごとに数が増えることです。セッションの終了時に接続を閉じることを主張する場合は、プールを使用せず、料金を支払う準備をしてください。

1
piet.t

私のアプリケーションプロパティには、データソース関連のパラメーターがあります。それらは以下のとおりです。

# DataSource Parameter
minPoolSize:5
maxPoolSize:100
maxIdleTime:5
maxStatements:1000
maxStatementsPerConnection:100
maxIdleTimeExcessConnections:10000

ここに、 **maxIdleTime**値が主な原因です。 I tは秒単位で値をとります。ここで、maxIdleTime = 5は、5秒後に接続が使用されていない場合、接続を解放し、minPoolSize:5接続を取ることを意味します。一度に接続します。

DataSource構成クラスには、Beanがあります。コードの例を次に示します。

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.core.env.Environment;
import org.springframework.beans.factory.annotation.Autowired;

@Autowired
    private Environment env;

 @Bean
    public ComboPooledDataSource dataSource(){
        ComboPooledDataSource dataSource = new ComboPooledDataSource();

        try {
            dataSource.setDriverClass(env.getProperty("db.driver"));
            dataSource.setJdbcUrl(env.getProperty("db.url"));
            dataSource.setUser(env.getProperty("db.username"));
            dataSource.setPassword(env.getProperty("db.password"));
            dataSource.setMinPoolSize(Integer.parseInt(env.getProperty("minPoolSize")));
            dataSource.setMaxPoolSize(Integer.parseInt(env.getProperty("maxPoolSize")));
            dataSource.setMaxIdleTime(Integer.parseInt(env.getProperty("maxIdleTime")));
            dataSource.setMaxStatements(Integer.parseInt(env.getProperty("maxStatements")));
            dataSource.setMaxStatementsPerConnection(Integer.parseInt(env.getProperty("maxStatementsPerConnection")));
            dataSource.setMaxIdleTimeExcessConnections(10000);

        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return dataSource;
    }

これがあなたの問題を解決することを願っています:)

0

同じ問題にぶつかり、EntityManagerFactoryのシングルトンラッパークラスを作成し、必要な場所にEntityManagerを作成することで修正できました。シングルトンクラスでEntityManagerの作成をラップしているため、接続のオーバーロードの問題が発生していますが、これは間違っています。 EntityManagerはトランザクションスコープを提供し(再利用しないでください)、EntityManagerFactoryは接続を提供します(再利用する必要があります)。

from: https://cloud.google.com/appengine/docs/Java/datastore/jpa/overview

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class EMF {
    private static final EntityManagerFactory emfInstance =
        Persistence.createEntityManagerFactory("CAOE");

private EMF() {}

public static EntityManagerFactory get() {
    return emfInstance;
    }
}

次に、ファクトリインスタンスを使用して、リクエストごとにEntityManagerを作成します。

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import EMF;

// ...
EntityManager em = EMF.get().createEntityManager();
0