wake-up-neo.com

Ruhezustand, der keine Verbindungen vom Verbindungspool freigibt

Ich erstelle eine Anwendung mit Hibernate JPA und verwende c3p0 für das Verbindungs-Pooling mit MySQL. Ich habe ein Problem mit der Anzahl der Verbindungen zur MySQL-Datenbank, da sie auf die 152 geöffneten Verbindungen trifft. Dies ist nicht erwünscht, da ich in meiner c3p0-Konfigurationsdatei die maximale Poolgröße auf 20 definiere. Natürlich schließe ich jeden Entity-Manager, den ich bekomme von der EntityManagerFactory nach jeder Transaktion.

Bei jeder Ausführung eines Controllers werden mehr als 7 Verbindungen geöffnet, und wenn ich die Aktualisierung aufnehme, werden 7 Verbindungen erneut geöffnet, ohne dass die vorherigen inaktiven Verbindungen geschlossen werden. In jeder DAO-Funktion, die ich anrufe, wird em.close () ausgeführt. Ich gebe zu, dass das Problem in meinem Code steckt, aber ich weiß nicht, was ich hier falsch mache.

Dies ist die Entität 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;
}

Und hier ist meine DAO-Klasse:

@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;
}

Wie Sie sehen, ist em geschlossen. In meiner JSP mache ich Folgendes: Ich weiß, dass dies nicht der richtige Weg ist, auf der Ansichtsseite etwas zu tun.

<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>

Ich vermute, dass bei jedem Aufruf von user.getType() eine Anforderung eingerichtet wird. Wenn ja, wie kann ich das verhindern?

Für die c4p0-Konfigurationsdatei habe ich sie in persistence.xml eingefügt. Ich habe mehrere Beiträge gesehen, die besagten, dass ich die c3p0-Konfigurationsdatei in c3p0-config.xml einfügen muss. In meinem Setup wird der c3p0 jedoch mit den Werten initialisiert, die ich in der Persistenz übergeben habe .xml-Datei, auch die mysql-Verbindungen erreichen 152 Verbindungen, aber die maxpoolsize ist bei 20, hier ist die persistence.xml-Datei

<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>

BEARBEITEN: Ich implementiere die Anwendung auf einem Red Hat-Server, auf dem Tomcat und MySQL installiert sind. Ich frage mich nur, warum Hibernate zu viele Verbindungen zu MySQL aufbaut. Wenn alle Entitätsmanager geschlossen sind, bleibt keine Verbindung offen. Dies ist jedoch nicht der Fall. Ich vermute und korrigiere mich, wenn ich wahr bin, dass die Verbindungen geöffnet werden, wenn ich so etwas mache: 

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

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

Wenn ich sondage.getQuestions() verwende, öffnet Hibernate eine Verbindung zur Datenbank und schließt sie nicht danach. In der Konfigurationsdatei fehlt etwas, das die Verbindung zum Pool schließt oder zurückgibt, wenn es damit fertig ist. Vielen Dank im Voraus für jede Hilfe.

EDIT2: Da die Leute nach Versionen fragen, sind sie hier: 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 Vielen Dank im Voraus

Die MySQL-Version ist Mysql 5.6.17, wenn das helfen kann ...

EDIT 4: Da die Leute verwirrt sind, ob die Version des Codes, den ich gepostet habe, fehlerhaft ist, lassen Sie mich das hier bearbeiten, damit Sie wissen, was genau passiert:

Zuerst werde ich zeigen, was der fehlerhafte Code ist, da es euch egal ist, was funktioniert:

@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;
  }

Dies ist im Grunde das, was ich für alle meine Dao-Funktionen getan habe. Ich weiß, dass hier keine Transaktion erforderlich ist, da ich Fragen sah, die darauf hindeuten, dass Transaktionen für das Schließen der Verbindung wichtig sind. Abgesehen davon habe ich getEntityManager aus der PersistenceManager-Klasse, die ein EntityManagerFactory-Singleton-Objekt enthält. Daher erstellt getEntityManager einen EntityManager aus dem EntityManagerFactory-Singleton-Objekt: => Code ist besser als 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;
        }
}

Ja das ist cool und alles gut, aber wo ist das Problem?

Das Problem hierbei ist, dass diese Version die Verbindungen öffnet und sie niemals schließt. Em.close () hat keine Auswirkung. Sie hält die Verbindung zur Datenbank offen.

Der Noob-Fix:

Was ich zur Behebung dieses Problems getan habe, ist das Erstellen einer EntityManagerFactory für jede Anforderung. Dies bedeutet, dass der Dao ungefähr so ​​aussieht: 

    @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;
}

Nun, das ist schlecht und ich werde es behalten, solange ich keine Antwort auf diese Frage habe (es scheint Forver: D). Mit diesem Code werden also grundsätzlich alle Verbindungen geschlossen, wenn der Ruhezustand sie nicht benötigt. Vielen Dank im Voraus für Ihre Bemühungen in dieser Frage :) 

21
Reda

Ich denke, dass sich Hibernate und C3P0 hier korrekt verhalten. In der Tat sollten Sie feststellen, dass gemäß Ihrer C3P0-Konfiguration immer mindestens drei Verbindungen zur Datenbank geöffnet sind.

Wenn Sie eine Abfrage ausführen, verwendet Hibernate eine Verbindung aus dem Pool und gibt sie zurück, wenn sie abgeschlossen ist. Die Verbindung wird nicht geschlossen. C3P0 kann den Pool verkleinern, wenn die Mindestgröße überschritten wird und bei einigen Verbindungen ein Timeout auftritt.

In Ihrem letzten Beispiel sehen Sie die Verbindungen geschlossen, weil Sie Ihre Entity Manager Factory und damit auch Ihren Verbindungspool geschlossen haben.

10
Alex Barnes

Sie rufen jedes Mal Persistence.createEntityManagerFactory("CAOE") auf. Es ist falsch. Jeder Aufruf createEntityManagerFactory erstellt einen neuen (unabhängigen) Verbindungspool. Sie sollten das EntityManagerFactory-Objekt irgendwo zwischenspeichern.

BEARBEITEN:

Sie sollten EntityManagerFactory auch manuell herunterfahren. Sie können es in @WebListener tun:

@WebListener
public class AppInit implements ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {}

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

Ansonsten ist jeder Umverteilungsvorgang die Quelle der durchgesickerten Verbindungen.

7
sibnick

Kannst du folgendes probieren:

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

anstelle Ihres aktuellen Freigabemodus?

3

Da sibnick die technischen Fragen bereits beantwortet hat, werde ich versuchen, einige Punkte zu behandeln, über die Sie verwirrt zu sein scheinen. Lassen Sie mich Ihnen einige Ideen geben, wie eine Anwendung und ein Verbindungspool im Ruhezustand funktionieren sollen:

  1. Das Öffnen einer Datenbankverbindung ist ein "teurer" Vorgang. Um zu vermeiden, dass diese Kosten für jede Anfrage bezahlt werden müssen, verwenden Sie einen Verbindungspool. Der Pool öffnet im Voraus eine bestimmte Anzahl von Verbindungen zur Datenbank, und wenn Sie eine benötigen, können Sie eine dieser Verbindungen ausleihen. Am Ende der Transaktion werden diese Zusammenhänge nicht geschlossen, sondern an den Pool zurückgegeben, sodass sie bei der nächsten Anforderung ausgeliehen werden können. Bei hoher Auslastung gibt es möglicherweise zu wenige Verbindungen, um alle Anforderungen zu bedienen, sodass der Pool möglicherweise zusätzliche Verbindungen öffnet, die möglicherweise später geschlossen werden, jedoch nicht sofort.
  2. Das Erstellen einer EntityManagerFactory ist noch teurer (es werden Caches erstellt, ein neuer Verbindungspool geöffnet usw.). Vermeiden Sie es daher unbedingt, dies für jede Anforderung zu tun. Ihre Antwortzeiten werden extrem langsam. Wenn Sie zu viele EntityManagerFactories erstellen, kann der PermGen-Speicherplatz erschöpft sein. Erstellen Sie also nur eine EntityManagerFactory pro Anwendung/Persistenz-Kontext, erstellen Sie sie beim Anwendungsstart (andernfalls dauert die erste Anforderung zu lange) und schließen Sie sie beim Herunterfahren der Anwendung.

Fazit: Wenn Sie einen Verbindungspool verwenden, sollten Sie davon ausgehen, dass eine bestimmte Anzahl von DB-Verbindungen für die gesamte Lebensdauer Ihrer Anwendung geöffnet bleibt. Was nicht passieren darf, ist, dass die Anzahl mit jeder Anfrage steigt. Wenn Sie darauf bestehen, dass die Verbindungen am Ende der Sitzung geschlossen werden, verwenden Sie keinen Pool und seien Sie bereit, den Preis zu zahlen.

1
piet.t

In meiner Anwendungseigenschaft habe ich einige datenquellenbezogene Parameter. Diese sind unten angegeben:

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

Hier ist **maxIdleTime** value der Hauptschuldige. I t nimmt den Wert in Sekunden ein. Hier bedeutet maxIdleTime = 5 nach 5 Sekunden, wenn die Verbindung nicht verwendet wird, wird die Verbindung freigegeben und es wird die minPoolSize: 5-Verbindung hergestellt. Hier bedeutet maxPoolSize: 100, dass maximal 100 Verbindungen gleichzeitig benötigt werden.

In meiner DataSource Konfigurationsklasse habe ich eine Bean. Hier ist der Beispielcode:

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;
    }

Hoffe das wird dein Problem lösen :)

0

Ich hatte das gleiche Problem und konnte dieses Problem beheben, indem ich eine Singleton-Wrapper-Klasse für EntityManagerFactory und den EntityManager dort erstellt, wo er benötigt wird. Sie haben das Problem der Verbindungsüberlastung, weil Sie die EntityManager-Erstellung in die Singleton-Klasse einbetten, was falsch ist. Der EntityManager stellt den Transaktionsbereich bereit (sollte nicht wiederverwendet werden), die EntityManagerFactory stellt die Verbindungen bereit (sollte erneut verwendet werden).

von: 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;
    }
}

verwenden Sie dann die Factory-Instanz, um für jede Anforderung einen EntityManager zu erstellen.

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

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