【问题标题】:Hibernate using C3P0 Works fine on Oracle but does not close connection with MySQL 5.6使用 C3P0 的休眠在 Oracle 上工作正常,但不关闭与 MySQL 5.6 的连接
【发布时间】:2013-11-25 15:10:46
【问题描述】:

我正在使用 Servlet 2.4、Hibernate 4.2.4 Final、c3p0 0.9.2.1、Tomcat 7.0.42、MySQL 5.6 和 JSP。

我使用 Oracle 11gR2 DB 完成了开发,但后来被要求切换到 MySQL 作为数据库。

我手头有一个不寻常的问题。

问题是为每个单独的数据库请求创建多个 MySQL 进程/连接,尽管发出了 SessionFactoryUtil.close();,但它们既没有关闭也没有返回到池中,而 Oracle 数据库不是这种情况。

我在这两个不同的数据库上测试了完全相同的代码,即在执行函数/请求(例如:登录)之后

应用程序在使用 Oracle (11gR2) 进行测试时,数据库创建了一个连接并将其用于以后的所有请求。 SELECT * FROM V$RESOURCE_LIMIT 给我以下输出
RESOURCE_NAME:进程
CURRENT_UTILIZATION:32
MAX_UTILIZATION:36
INITIAL_ALLOCATION:300
LIMIT_VALUE:300
无论有多少用户登录,连接池都会优雅地维护它。

另一方面,当同一个应用程序在 MySQL 上运行时:
我在 MySQL 上做了一个SHOW PROCESSLIST;,它显示了为每个请求创建了两个进程; c3p0 成功终止了一个连接,但另一个连接仍然存在,直到数据库崩溃,因为它超过了可用的最大连接数。

我的 SessionFactoryUtil 非常简单明了,如下: public class SessionFactoryUtil { private static SessionFactory sessionFactory;

public static SessionFactory getSessionFactory() {
    return sessionFactory = new Configuration().configure()
            .buildSessionFactory();//deprecated method not changed due to official reason
}

public Session getCurrentSession() {
    return sessionFactory.getCurrentSession();
}

public static void close() {
    if (sessionFactory != null) {
        sessionFactory.close();
    }
    sessionFactory = null;
}

我的DAO方法如下

public User getUserByName(String userName) throws FetchException {
User user = null;
Session session = SessionFactoryUtil.getSessionFactory().getCurrentSession();
try {
    session.beginTransaction();
    user = (User) session.createQuery("from User where userName = '" + userName + "'").uniqueResult();
} catch (Exception e) {
    logger.info("UserDaoImpl -> getUserByName() : Error : " +e);
    e.printStackTrace();
} finally {
    SessionFactoryUtil.close();
}
return user;

c3p0 销毁连接的堆栈跟踪如下:

20:45:43,692 INFO com.mchange.v2.resourcepool.BasicResourcePool:1493 - A checked-out resource is overdue, and will be destroyed: com.mchange.v2.c3p0.impl.NewPooledConnection@61f31fff 20:45:43,692 INFO com.mchange.v2.resourcepool.BasicResourcePool:1496 - Logging the stack trace by which the overdue resource was checked-out. java.lang.Exception: DEBUG STACK TRACE: Overdue resource check-out stack trace. at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:555) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:755) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:682) at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140) at org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider.getConnection(C3P0ConnectionProvider.java:84) at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:292) at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:214) at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:157) at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:67) at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:160) at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1426) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:352) at com.sun.proxy.$Proxy7.beginTransaction(Unknown Source) at com.demo.access.impl.ConfDaoImp.showAllEvents(ConfDaoImp.java:939) at com.demo.business.impl.ConfServiceImpl.showAllEvents(ConfServiceImpl.java:404) at com.demo.controller.UserController.getControls(UserController.java:112) at com.demo.controller.UserController.validateUser(UserController.java:93) at com.demo.controller.UserController.process(UserController.java:42) at com.demo.controller.ApplicationServlet.process(ApplicationServlet.java:75) at com.demo.controller.ApplicationServlet.doPost(ApplicationServlet.java:53) at javax.servlet.http.HttpServlet.service(HttpServlet.java:641) at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at com.demo.controller.LoginFilter.doFilter(LoginFilter.java:37) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:185) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:151) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:269) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source)

我已经阅读了几乎所有与此特定场景相关的问题,但似乎没有一个有效,或者线程被中途放弃,或者我错过了一些东西;有人可以帮我解决这个问题。

【问题讨论】:

    标签: mysql oracle hibernate servlets


    【解决方案1】:

    你的这段代码对我有用:

    public static void close() {
    if(sessionFactory instanceof SessionFactoryImpl) {
          SessionFactoryImpl sf = (SessionFactoryImpl)sessionFactory;
          ConnectionProvider conn = sf.getConnectionProvider();
          if(conn instanceof C3P0ConnectionProvider) { 
            ((C3P0ConnectionProvider)conn).close(); 
          }
       }
    sessionFactory.close(); }
    

    在此之前,Tomcat 一直(正确地)抱怨每次热部署时的内存泄漏。谢谢!

    【讨论】:

    • 请注意:C3P0ConnectionProvider.close() 已弃用,请改用 close()。
    • @James “... close() 已弃用,请改用 close()”。在哪个物体上?
    • 对不起,我的意思是 stop() 在同一个对象上。 ((C3P0ConnectionProvider)conn).stop();
    【解决方案2】:

    一些想法:

    1)您永远不会关闭您创建的会话(通过要求“当前会话”来隐式)。这就是为什么您可能会有一个未返回的连接最终超时的一个直接原因。

    2)您将 SessionFactory 视为 Session,建立然后拆除整个事物(包括连接池)只是为了获取和使用一个连接。不太好。您的 SessionFamily 应该有一个较长的生命周期,您的会话应该是一次性的短期使用。

    【讨论】:

    • 感谢您的 cmets 史蒂夫。我同意您的 cmets,我将关闭每个会话并返回结果。
    【解决方案3】:

    我已经有一段时间没有找到我奇怪问题的答案了,我认为分享它会很有帮助。

    首先,我做错的几件事是...
    首先,我从 hibernate 3.6 迁移到 4.2,在此过程中我仍在使用已弃用的 buildSessionFactory() 方法。

    其次,我在 DAO 中的每个查询语句结束后使用SessionFactoryUtil.close(),这违背了使用连接池的目的。

    最后,Oracle 似乎在执行语句后成功关闭连接而 MySql 无法关闭该连接的奇怪问题仍然是一个谜。
    我怀疑这是因为我要求 SessionFactoryUtil 关闭最初由 C3P0ConnectionProvider 打开的连接(我认为这反过来又导致连接泄漏)。

    经过大量研究并环顾四周,我重新编写了 SessionFactoryUtil,如下所示...

    public class SessionFactoryUtil {
    private static SessionFactory sessionFactory;
    private static ServiceRegistry serviceRegistry;
    
    public static SessionFactory getSessionFactory() {
        Configuration configuration = new Configuration();
        configuration.configure();
        serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();        
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        return sessionFactory;
    }
    
    public static Session getCurrentSession() {
        if(sessionFactory == null){
            getSessionFactory();
        }
        return sessionFactory.getCurrentSession();
    }
    
    public static void close() {
        if(sessionFactory instanceof SessionFactoryImpl) {
              SessionFactoryImpl sf = (SessionFactoryImpl)sessionFactory;
              ConnectionProvider conn = sf.getConnectionProvider();
              if(conn instanceof C3P0ConnectionProvider) { 
                ((C3P0ConnectionProvider)conn).close(); 
              }
           }
        sessionFactory.close();
    }
    

    请注意,我的所有连接都是由 C3P0ConnectionProvider 打开的,因此使用 C3P0ConnectionProvider 本身关闭它是合乎逻辑的。

    以下是我的 hibernate.cfg.xml 以及 c3p0 设置。

        <!-- Database connection settings -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/application</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
    
        <property name="show_sql">true</property>
        <property name="format_sql">false</property>
    
         <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    
         <!-- Enable Hibernate's automatic session context management -->
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.connection.release_mode">auto</property>
    
        <!-- Create or update the database schema on startup -->
        <property name="hibernate.hbm2ddl.auto">none</property>
    
        <!-- DEPRECATED -->
        <!--        <property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> -->
        <!-- C3p0 connection pooling configuration -->
        <property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
        <property name="c3p0.unreturnedConnectionTimeout">600</property>
        <property name="c3p0.debugUnreturnedConnectionStackTraces">false</property>
        <!--        configuration pool via c3p0    -->
        <property name="c3p0.acquire_increment">1</property>   
        <property name="c3p0.idle_test_period">600</property>    
        <property name="c3p0.max_size">75</property>   
        <property name="c3p0.max_statements">5</property>   
        <property name="c3p0.min_size">5</property>   
        <property name="c3p0.timeout">600</property>
        <property name="c3p0.checkoutTimeout">6000000</property>
    
        <property name="c3p0.testConnectionOnCheckout">false</property>
        <property name="c3p0.testConnectionOnCheckin">true</property>   
    
        <!-- Mapping -->
    
    </session-factory>
    

    这又是我的 DAO 类中的方法之一...

    公共用户 getUserByName(String userName) 抛出 FetchException {
    用户用户=空;
    会话会话 = SessionFactoryUtil.getCurrentSession();
    尝试 {
        session.beginTransaction();
        user = (User) session.createQuery("from User where userName = '" + userName + "'").uniqueResult();
        session.getTransaction().commit();
    } 捕捉(异常 e){
        logger.info("UserDaoImpl -> getUserByName() : 错误:" +e);
        e.printStackTrace();
    } 最后 {
    
    }
    返回用户;
    

    请注意,在 finally 块的 DAO 中,我不必再关闭连接,我让 c3p0 处理连接池。

    然后瞧……!!应用程序运行!!!在 2 小时内,单日交易点击量超过 2000 次。
    我希望这可以帮助像我这样的新手休眠用户。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-15
      • 2014-10-03
      • 1970-01-01
      • 1970-01-01
      • 2016-05-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多