Hibernate不从连接池

时间:2015-09-08 14:24:29

标签: java mysql hibernate jpa c3p0

我正在使用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>
编辑:我正在使用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.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函数所做的事情,我知道这里不需要事务,因为我看到了一些问题,指出事务对于关闭连接很重要。除此之外,我从PersistenceManager类获取具有EntityManagerFactory单例对象的EntityManager,因此getEntityManager从Ent​​ityManagerFactory singleton Object创建一个entityManager:=&gt;代码优于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;
}

现在这很糟糕,我会保留它而我没有回答这个问题(看起来好像是:D)。所以使用这段代码基本上所有连接都在hibernate不需要它们之后关闭。提前感谢您在此问题中付出的任何努力:)

7 个答案:

答案 0 :(得分:10)

我认为Hibernate和C3P0在这里表现正常。事实上,根据您的C3P0配置,您应该看到始终至少有三个连接到数据库。

执行查询时,Hibernate将使用池中的连接,然后在完成后返回。它不会关闭连接。如果超过最小值并且某些连接超时,C3P0可能会缩小池。

在最后一个示例中,您会看到连接已关闭,因为您已关闭实体管理器工厂,因此也关闭了连接池。

答案 1 :(得分:8)

您每次都致电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();
    }
}

否则每次重新部署都是泄漏连接的来源。

答案 2 :(得分:3)

您可以尝试以下方法:

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

而不是您当前的发布模式?

答案 3 :(得分:1)

由于sibnick已经回答了技术问题,我试图解决一些您似乎感到困惑的问题。那么,让我给你一些关于hibernate应用程序和连接池如何工作的想法:

  1. 打开数据库连接是一种非常昂贵的问题。操作。为了避免为每个请求支付该费用,您使用连接池。池提前打开一定数量的数据库连接,当您需要时,可以借用其中一个现有连接。在交易结束时,这些连接将不会被关闭,而是返回到池中,以便下次请求可以借用它们。在负载很重的情况下,可能会有太少的连接来为所有请求提供服务,因此池可能会打开可能在以后关闭但不会立即关闭的其他连接。
  2. 创建EntityManagerFactory甚至更昂贵(它将创建缓存,打开新的连接池等),因此无论如何都要避免为每个请求执行此操作。您的响应时间将变得非常缓慢。创建太多的EntityManagerFactories也可能会耗尽您的PermGen空间。因此,每个应用程序/持久性上下文只创建一个EntityManagerFactory,在应用程序启动时创建它(否则第一个请求将花费太长时间)并在应用程序关闭时关闭它。
  3. 结论:使用连接池时,您应该期望在应用程序的生命周期内保持打开一定数量的数据库连接。不可能发生的是,每次请求的数量都会增加。如果您坚持在会话结束时关闭连接,请不要使用池并准备支付价格。

答案 4 :(得分:0)

我遇到了同样的问题,并且能够通过为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();

答案 5 :(得分:0)

在我的应用程序属性中,我有一些与数据源相关的参数。那些在下面:

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

在这里,**maxIdleTime**值是主要原因。我以秒为价值。在这里maxIdleTime = 5表示5秒钟后如果未使用连接,它将释放连接,并将使用minPoolSize:5连接。这里的maxPoolSize:100表示​​一次最多只能连接100个。

在我的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;
    }

希望这会解决您的问题:)

答案 6 :(得分:-4)

看起来问题与Hibernate bug有关。请尝试在OneToMany注释中指定获取策略EAGER。

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