0

私は 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 に切り替えるように求められました。

私はかなり珍しい問題を抱えています。

問題は、単一の DB リクエストごとに複数の MySQL プロセス/接続が作成されることですSessionFactoryUtil.close();。これは、Oracle DB の場合ではありませんでしたが、発行しても閉じられず、プールにも返されません。

これら 2 つの異なるデータベースでまったく同じコードをテストしました。つまり、関数/要求 (例: ログイン) を実行した後です。

アプリケーションを Oracle (11gR2) でテストしたとき、DB は単一の接続を作成し、それ以降のすべてのリクエストに使用しました。 SELECT * FROM V$RESOURCE_LIMIT 次の出力
RESOURCE_NAME: processes
CURRENT_UTILIZATION: 32
MAX_UTILIZATION: 36
INITIAL_ALLOCATION: 300
LIMIT_VALUE: 300
接続プールにログインするユーザーの数に関係なく、正常に維持されます。

一方、同じアプリケーションが MySQL で実行された場合: MySQL で実行したところ、
リクエストSHOW PROCESSLIST;ごとに 2 つのプロセスが作成されていることがわかりました。c3p0 は 1 つの接続を正常に終了しますが、使用可能な最大接続数を超えたため、DB がクラッシュするまで他の接続は残ります。

私の 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)

この特定のシナリオに関連するほとんどすべての質問を読みましたが、どれも機能しないようです。または、スレッドが途中で放棄されたか、何かを見逃しています。誰かがこれをやり遂げるのを手伝ってくれませんか。

4

3 に答える 3

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 は各ホット デプロイでメモリ リークについて (正しく) 不満を漏らしていました。ありがとう!

于 2014-02-19T14:55:17.663 に答える
0

私の奇妙な問題の答えを見つけてからしばらく経ちましたが、それを共有することが最も役立つと思いました.

まず、私が間違っていたことがいくつかありました...
まず、休止状態 3.6 から 4.2 に移行しましたが、その際、まだ非推奨のbuildSessionFactory()方法を使用していました。

第 2 にSessionFactoryUtil.close()、DAO の各クエリ ステートメントの終了後に使用していたため、接続プールを使用する目的が無効になっていました。

最後に、ステートメントの実行後に Oracle が正常に接続を閉じたように見えるのに、MySql が同じ接続を閉じることができなかったという奇妙な問題は、依然として謎のままです。
これは、C3P0ConnectionProvider によって最初に開かれた接続を閉じるように SessionFactoryUtil に要求していたために発生したと思われます (これにより、接続リークが発生したと思われます)。

多くの調査と見回した後、次のように 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 クラスのメソッドの 1 つです...

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

}
return user;

私の DAO では、finally ブロックで接続を閉じる必要がなくなったことに注意してください。c3p0 に接続プールを処理させます。

そして出来上がり... !! アプリケーションが実行されます!!! 1 日 2 時間で 2000 件を超えるトランザクション ヒットがありました。
これが初心者の冬眠ユーザーに役立つことを願っています。

于 2013-11-27T16:01:53.263 に答える
0

いくつかのアイデア:

1)作成したセッションを閉じることはありません(暗黙のうちに「現在のセッション」を要求することにより)。これが、返されない Connection が最終的にタイムアウトになる単純な理由です。

2) SessionFactory をセッションのように扱い、1 つの接続を取得して使用するためだけに (接続プールを含む) 全体を構築してから破棄します。あまり良くない。SessionFamily は長いライフサイクルを持つ必要があり、セッションは 1 回限りの短期間で使用する必要があります。

于 2013-11-15T03:31:23.173 に答える