Подтвердить что ты не робот

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 инициализируется значениями, которые я передаю в файле persistence.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>

EDIT: я развертываю приложение на сервере Red Hat с Tomcat и MySQL Installed. Мне просто интересно, почему 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 Зимуют-ядро-4.3.10 hibernate c3p0 4.3.10.финал hibernate-jpa 2.1 Спасибо заранее

Версия mysql - это Mysql 5.6.17, если это может помочь...

EDIT 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, который имеет объект Singleity Object EntityManagerFactory, поэтому getEntityManager создает entityManager из одноэлементного объекта EntityManagerFactory. Object: = > код лучше 1000 слов: 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;
}

Теперь это плохо, и я просто сохраню его, пока у меня нет ответа на этот вопрос (похоже, это forver: D). Таким образом, с этим кодом в основном все соединения закрываются после того, как спящий режим им не нужен. Заранее благодарю за любые усилия, поставленные вами в этом вопросе:)

4b9b3361

Ответ 1

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

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

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

Ответ 2

Вы вызываете Persistence.createEntityManagerFactory("CAOE") каждый раз. Это неверно. Каждый вызов createEntityManagerFactory создает новый (независимый) пул соединений. Вы должны кэшировать объект EntityManagerFactory где-то.

EDIT:

Также вы должны вручную отключить EntityManagerFactory. Вы можете сделать это в @WebListener:

@WebListener
public class AppInit implements ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {}

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

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

Ответ 3

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

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

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

Ответ 4

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

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

Нижняя строка. При использовании пула соединений вы должны ожидать, что определенное количество соединений DB останется открытым на всю жизнь вашего приложения. Не должно быть, чтобы число увеличивалось с каждым запросом. Если вы настаиваете на закрытии соединений в конце сеанса, не используйте пул и будьте готовы заплатить цену.

Ответ 5

Я столкнулся с той же проблемой и смог исправить ее, создав класс оболочки singleton для 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;
    }
}

а затем используйте экземпляр factory для создания EntityManager для каждого запроса.

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

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

Ответ 6

Похоже, проблема связана с ошибка Hibernate. Пожалуйста, попробуйте указать Fetch-стратегию EAGER в аннотации OneToMany.

@OneToMany(mappedBy = "sondage", cascade = CascadeType.ALL, fetch = FetchType.EAGER)