8
votes

I recently started using hibernate along with c3p0 as the ORM in my application. However, when I close the session factory, the connection pool does not close itself! This is the one and only place in my application where I do anything with a session.

    StatelessSession session = null;
    Transaction transaction = null;


    try {
        session = sessionFactory.openStatelessSession();
        transaction = session.beginTransaction();

        List<Thingy> list = session.getNamedQuery("getAvailableThingy").list();

        transaction.commit();
        return list;

    } catch (Exception error) { 
        if (transaction != null) {
            transaction.rollback();
        }
        throw error;
    } finally {
        if (session != null) {
            session.close();
        }
    }

This is my hibernate.cfg.xml configuration file

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">


<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
        <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
        <property name="javax.persistence.validation.mode">none</property>
        <property name="hibernate.connection.release_mode">after_transaction</property>

        <property name="hibernate.c3p0.minPoolSize">1</property>
        <property name="hibernate.c3p0.maxPoolSize">2</property>
        <property name="hibernate.c3p0.acquireIncrement">1</property>
        <property name="hibernate.c3p0.initialPoolSize">1</property>
        <property name="hibernate.c3p0.timeout">30</property>
        <property name="hibernate.c3p0.maxIdleTimeExcessConnections">5</property>
        <property name="hibernate.c3p0.idleConnectionTestPeriod">300</property>
    </session-factory>
</hibernate-configuration>

Note that the reason for the very short idle connection it that its the only way I found yet to make my integration tests to pass. They open and close the session factory a lot and thus I always run out of connections. As we are at the beginning of the project, I guess it's not a very sustainable strategy in the long run.

An "interesting" thing to note is that despite the fact that I set the initial connection pool to one, c3p0 still try to open two connection on start. My guess is that there is some kind of hidden session somewhere that don't get closed (but where? beat me).

So how can I get that annoying connection pool to close itself up?

Additional info : how I create and destroy my session factory

import static com.google.common.base.Preconditions.*;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Provides;


@Singleton 
public class PostgisConnection implements Provider<SessionFactory>, AutoCloseable {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final ConnectionInfo connectionInfo;
    private SessionFactory sessionFactory = null;

    @Inject 
    public PostgisConnection(ConnectionInfo connectionInfo) {
        this.connectionInfo = connectionInfo;
    }

    public AutoCloseable open() {
        checkState(sessionFactory == null, "Connections to postgis are already open");

        logger.info("Creating sessionFactory for connection to postgis: {}", connectionInfo.getJdbcUrl());
        sessionFactory = newPostgisSessionFactory(connectionInfo);

        return this;
    }

    @Override
    public void close() throws Exception {
        try {
            if (sessionFactory != null) {
                logger.info("Closing sessionFactory for postgis: {}", connectionInfo.getJdbcUrl());
                sessionFactory.close();
                checkState(sessionFactory.isClosed(), "Session factory should be closed at this point");
            }
        } catch (Exception error) {
            logger.error("Error closing SessionFactory", error);
        }
    }

    @Provides 
    public SessionFactory get() {
        return sessionFactory;
    }

    public static SessionFactory newPostgisSessionFactory(ConnectionInfo connectionInfo) {
        Configuration configuration = configurationWith(connectionInfo);
        return configuration.buildSessionFactory(registryFrom(configuration));
    }

    private static Configuration configurationWith(ConnectionInfo connectionInfo) {
        Configuration configuration = new Configuration();
        setConnectionInfo(connectionInfo, configuration);
        configuration.addURL(PostgisConnection.class.getResource("mapping.hbm.xml"));
        configuration.configure(PostgisConnection.class.getResource("hibernate.cfg.xml"));

        return configuration;
    }

    private static void setConnectionInfo(ConnectionInfo connectionInfo, Configuration configuration) {
        configuration.setProperty("hibernate.connection.url", connectionInfo.getJdbcUrl());
        configuration.setProperty("hibernate.connection.username", connectionInfo.getUsername());
        configuration.setProperty("hibernate.connection.password", connectionInfo.getPassword());
    }

    private static ServiceRegistry registryFrom(Configuration configuration) {
        return new ServiceRegistryBuilder()
                .applySettings(configuration.getProperties())
                .buildServiceRegistry();
    }

}
  • Hibernate version : 4.1.10.Final
  • C3p0 version : 0.9.1.2
2
Why do you want to close your connection pool? You won't be able to pick a new connection from it if it is closed.dotvav
The SessionFactory is closed and restarted between some of my integration test to avoid lingering state that could affect them. I also close my WebServer, close some Unix Socket connection, drop the test database... everything really. It should be pristine clean when I start the second test. Of course in production I don't spend my time closing and restarting those thing, that would pretty useless.Laurent Bourgault-Roy
per hibernate's docs, SessionFactor.close() should close the pool: "Destroy this SessionFactory and release all resources (caches, connection pools, etc)." [ docs.jboss.org/hibernate/orm/4.1/javadocs/org/hibernate/… ] can you see in your logs your close() method being called when you expect? some ideas: 1) c3p0-0.9.1.2 is a bit long in the tooth, try upgrading to the quite mature c3p0-0.9.2; 2) be aware that c3p0 close() is not necessarily instantaneous: c3p0 manages a bunch of Threads that are interrupted and clean stuff up on close, outstanding cxns must be returnedSteve Waldman
Yep, I can see clearly in my log the call to close. I guess my test are running too "quickly" (haha, selenium test running too quickly) and the pool don't has time to close.Laurent Bourgault-Roy
If I undeploy my app, wait minutes to reploy it, the c3p0 pool still is open. Using answer below until the issue (see hibernate.atlassian.net/browse/HHH-7364) is fixed.drvdijk

2 Answers

14
votes

I had the same issue and successfully used the work-around offered in this bug report:

private void closeSessionFactory(SessionFactory factory) { 
   if(factory instanceof SessionFactoryImpl) {
      SessionFactoryImpl sf = (SessionFactoryImpl)factory;
      ConnectionProvider conn = sf.getConnectionProvider();
      if(conn instanceof C3P0ConnectionProvider) { 
        ((C3P0ConnectionProvider)conn).close(); 
      }
   }
   factory.close();
}

You have to reference the hibernate-c3p0-4.x.x jar.

0
votes

I had the same issue and successfully used the ehnanced (January 2014) work-around offered in this bug report:

private static boolean closeSessionFactoryIfC3P0ConnectionProvider(SessionFactory factory) {

    boolean done = false;
    if(factory instanceof SessionFactoryImpl) {
        SessionFactoryImpl sf = (SessionFactoryImpl)factory;
        ConnectionProvider conn = sf.getConnectionProvider();
        if(conn instanceof C3P0ConnectionProvider) { 
            ((C3P0ConnectionProvider)conn).close(); 
            try {
                Thread.sleep(2000); //Let give it time...it is enough...probably
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            done = true;
        }
        factory.close();
    }
    return done;

}

You have to reference the hibernate-c3p0-4.x.x jar.