it-roy-ru.com

Hibernate не освобождает соединения из пула соединений

Я создаю приложение с Hibernate JPA и использую c3p0 для пула соединений с MySQL. У меня есть проблема с количеством соединений с базой данных MySQL, поскольку она достигает 152 открытых соединений, это не нужно, так как я определяю в своем конфигурационном файле c3p0 максимальный размер пула до 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 в файл c3p0-config.xml, но при моей настройке 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>

Правка: я развертываю приложение на сервере Red Hat с установленным Tomcat и MySQL. Мне просто интересно, почему 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, я знаю, что транзакция здесь не нужна, так как я видел вопросы, указывающие, что транзакции важны для закрытия соединения. Помимо этого, я получаюEntityManager из класса PersistenceManager, который имеет одноэлементный объект EntityManagerFactory, поэтому getEntityManager создает объект entityManager из одноэлементного кода EntityManagerFactory Object: => лучше, чем 1000 Word: 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 () не имеет никакого эффекта, она сохраняет соединение открытым для базы данных.

Исправление нуба:

Что я сделал, чтобы решить эту проблему, так это создать EntityManagerFactory для каждого запроса, это означает, что дао выглядит примерно так: 

    @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. Заранее спасибо за любые усилия, которые вы вложили в этот вопрос :) 

21
Reda

Я думаю, что Hibernate и C3P0 ведут себя здесь правильно. Фактически вы должны видеть, что всегда есть по крайней мере три соединения с базой данных, открытых согласно вашей конфигурации C3P0.

При выполнении запроса Hibernate будет использовать соединение из пула, а затем вернет его, когда это будет сделано. Это не закроет соединение. C3P0 может уменьшить пул, если минимальный размер превышен, а время ожидания некоторых соединений истекло.

В последнем примере вы видите, что соединения закрыты, потому что вы закрыли свою фабрику менеджера сущностей и, следовательно, свой пул соединений.

10
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();
    }
}

В противном случае каждый случай повторного развертывания является источником утечек соединений.

7
sibnick

Можете ли вы попробовать следующее:

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

вместо вашего текущего режима выпуска?

3
Norbert van Nobelen

Поскольку sibnick уже ответил на технические вопросы, я постараюсь изложить некоторые моменты, которые, кажется, вас смущают. Итак, позвольте мне дать вам несколько идей о том, как работают hibernate-приложение и пул соединений:

  1. Открытие соединения с базой данных является «дорогой» операцией. Чтобы избежать необходимости оплачивать эту стоимость за каждый запрос, вы используете пул соединений. Пул заранее открывает определенное количество соединений с базой данных, и когда вам нужно, вы можете заимствовать одно из этих существующих соединений. В конце транзакции эти соединения не будут закрыты, а будут возвращены в пул, поэтому они могут быть заимствованы при следующем запросе. При большой нагрузке может быть слишком мало соединений для обслуживания всех запросов, поэтому пул может открывать дополнительные соединения, которые могут быть закрыты позже, но не сразу.
  2. Создание EntityManagerFactory еще более дорого (это создаст кеши, откроет новый пул соединений и т.д.), Поэтому избегайте делать это для каждого запроса. Ваше время отклика станет невероятно медленным. Также создание слишком большого количества EntityManagerFactories может исчерпать ваше пространство PermGen. Поэтому создайте только один EntityManagerFactory для каждого приложения/персистентного контекста, создайте его при запуске приложения (в противном случае первый запрос займет слишком много времени) и закройте его при завершении работы приложения.

Итог: при использовании пула соединений вы должны ожидать, что определенное количество соединений с БД останется открытым в течение всего времени жизни вашего приложения. Чего не должно случиться, так это того, что число увеличивается с каждым запросом. Если вы настаиваете на закрытии соединений в конце сеанса, не используйте пул и будьте готовы заплатить цену.

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. Здесь maxPoolSize: 100 означает, что потребуется максимум 100 соединений одновременно.

В моем классе конфигурации DataSource у меня есть бин. Вот пример кода:

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
Md. Sajedul Karim

Я столкнулся с той же проблемой и смог ее исправить, создав одноэлементный класс-оболочку для EntityManagerFactory и создав EntityManager там, где это необходимо. У вас проблема с перегрузкой соединения, потому что вы помещаете объект EntityManager в класс singleton, что неправильно. EntityManager предоставляет область транзакции (не должна использоваться повторно), EntityManagerFactory обеспечивает соединения (должна использоваться повторно).

от: 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
Willem van Rooyen