2015-09-08 2 views
22

Я создаю приложение с Hibernate JPA, и я использую c3p0 для объединения пулов с MySQL. У меня проблема с количеством подключений к базе данных MySQL, поскольку она попадает в 152 открытых соединения, это не требуется, поскольку я определяю в моем конфигурационном файле c3p0 максимальный размер пула до 20 и, конечно же, я закрываю каждый менеджер объектов, который я получаю от EntityManagerFactory после совершения каждой транзакции.Hibernate не освобождает соединения из пула соединений

За каждый раз, когда выполняется контроллер, я вижу, что открыто более 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 инициализируется значениями I передать в файле 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: Я развертывание приложения на красной шляпе с сервера Установлены 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 гибернации-ядро-4.3.10 зимуют c3p0 4.3.10.final зимуют-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; 
    } 

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

нуб фикс:

Что я сделал, чтобы исправить этот вопрос создать 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; 
} 

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

+0

Заметил, что вы указываете режим выпуска в файле persistence.xml. Согласно этому документу: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/transactions-connection-release.html это обычно плохой идеей. Возможно, это приведет к удалению этой строки конфигурации из вашего файла. Это больше в виде наблюдения и SWAG, чем ответа, поэтому я предлагаю это комментарий, а не ответ. –

+0

Hi @MattCampbell, THANkS так много для ответа, я не делал этого в файле конфигурации, однако, когда я увидел свойство, которое у меня было, это может освободить некоторые подключения к пулу, но этого не произошло, поэтому ответ на ваши предположения , это не проблема. – Reda

+0

Где и как вы получаете «Список », вызывая 'GetAllSondage()' в целевом DAO? Он доступен как атрибут HTTP-запроса на целевом JSP. Он уже выбран во включенном файле 'header.jsp' или где-то еще? – Tiny

ответ

-3

Похоже, что вопрос связан с Hibernate bug. Попробуйте указать стратегию EACHER в ваших аннотациях OneToMany.

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

Привет, emamedov, мне нужен FetchType, чтобы быть ЛАЗЫМ, потому что все мои сущности полагаются на него, когда я добавляю опрос, в котором есть много разделов, в которых много вопросов, я только делаю SurveyDAO.addSurvey, и все вопросы и разделы которые не попадают в базу данных, автоматически добавляются только с одним вызовом DAO, поэтому изменение FetchType для меня - это переделка приложения – Reda

+1

Hi Reda. FetchType не должен влиять на вставку в базу данных. Вы все еще можете использовать один вызов метода, а спящий режим будет добавлять весь объектный граф сам по себе. Но я согласен с тем, что тип LAZY может быть предпочтительным. Я предложил решение просто проверить, что это ошибка Hibernate. В случае «да» мы будем думать об использовании типа EAGER или обходном пути для LAZY типа – emamedov

3

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

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

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

+0

. Привет, снова Норберт, я попробовал это, даже если в документах они указывают на него как на плохой habbit, но ничего. – Reda

6

Вы звоните 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(); 
    } 
} 

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

+0

Привет, Сибник, извините, но этот код я написал для исправления этой проблемы, я в основном инициировал сущность Manager Manager для каждого запроса, и это исправляет проблему, в моем исходном коде emf является singleton, getEntityManagerFactory() проверяет, it this.emf имеет значение null и создает новый, если он не является null, он вернет this.emf, я обновлю вопрос, извините за это. – Reda

+1

. Вы не храните новый объект в переменной emf в методе 'getEntityManagerFactory'. Также этот код не является многопоточным. – sibnick

+0

Я запускаю все приложение в одном потоке Я отредактировал код – Reda

9

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

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

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

+2

Полностью согласен, C3P0 поддерживает открытие соединений, даже когда выполняется em.close(). Зачем? Просто, потому что это его цель –

+0

Привет, Алекс, спасибо за ответ, я знаю, что C3P0 поддерживает соединение, открытое для использования тем, кто его запрашивает, но здесь соединения НЕ ОТПУСКАЮТСЯ НА БАССЕЙН, прочитайте заголовок моего вопроса – Reda

+0

Привет, Алекс, C3P0 настроен на открытие только 30 или что-то в этом роде, когда я контролирую мой сервер Mysql, открываются 152 соединения. так что это ненормально. – Reda

1

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

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

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

+0

спасибо piet.t, но я уже знаю все это, открытые соединения - это не то, что нужно пулу соединений. 152 открытых соединений, и моя конфигурация сервера поддерживает только 151 – Reda

0

Я столкнулся с той же проблемой и смог ее исправить, создав класс оболочки Singleton для EntityManagerFactory и создав EntityManager там, где это необходимо. У вас проблема с перегрузкой соединения, потому что вы завершаете создание EntityManager в одноэлементном классе, что неверно. 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(); 
Смежные вопросы