Hibernate: закрытие фабрики сеансов не закрывает пул соединений c3p0

Недавно я начал использовать спящий режим вместе с c3p0 в качестве ORM в своем приложении. Однако, когда я закрываю фабрику сеансов, пул соединений не закрывается! Это единственное место в моем приложении, где я могу что-то делать с сеансом.

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

Это мой файл конфигурации hibernate.cfg.xml

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

Обратите внимание, что причина очень короткого простоя соединения в том, что это единственный способ, который я нашел, чтобы пройти мои интеграционные тесты. Они часто открывают и закрывают фабрику сеансов, и поэтому у меня всегда заканчиваются соединения. Поскольку мы находимся в начале проекта, я думаю, что это не очень устойчивая стратегия в долгосрочной перспективе.

«Интересно» отметить, что, несмотря на то, что я установил начальный пул соединений равным одному, c3p0 все еще пытается открыть два соединения при запуске. Я предполагаю, что где-то есть какая-то скрытая сессия, которая не закрывается (но где? Побейте меня).

Итак, как я могу заставить этот надоедливый пул соединений закрыться?

Дополнительная информация: как я создаю и уничтожаю свою фабрику сеансов

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

}
  • Спящая версия: 4.1.10.Final
  • Версия C3p0: 0.9.1.2

person Laurent Bourgault-Roy    schedule 08.03.2013    source источник
comment
Почему вы хотите закрыть пул соединений? Вы не сможете выбрать из него новое соединение, если оно закрыто.   -  person dotvav    schedule 08.03.2013
comment
SessionFactory закрывается и перезапускается между некоторыми моими интеграционными тестами, чтобы избежать затяжного состояния, которое может повлиять на них. Я также закрываю свой веб-сервер, закрываю какое-то соединение Unix Socket, удаляю тестовую базу данных ... все на самом деле. Он должен быть идеально чистым, когда я начну второй тест. Конечно, в производстве я не трачу время на закрытие и перезапуск этих вещей, это было бы довольно бесполезно.   -  person Laurent Bourgault-Roy    schedule 08.03.2013
comment
согласно документам hibernate, SessionFactor.close() должен закрыть пул: уничтожьте этот SessionFactory и освободите все ресурсы (кэши, пулы соединений и т. д.). [ docs.jboss.org/hibernate/orm/4.1 /javadocs/org/hibernate/ ] можете ли вы увидеть в своих журналах, что ваш метод close() вызывается, когда вы ожидаете? некоторые идеи: 1) c3p0-0.9.1.2 немного длинноват, попробуйте обновиться до вполне зрелого c3p0-0.9.2; 2) имейте в виду, что c3p0 close() не обязательно мгновенный: c3p0 управляет кучей потоков, которые прерваны, и очищает материал при закрытии, необработанные cxns должны быть возвращены   -  person Steve Waldman    schedule 09.03.2013
comment
Да, я ясно вижу в своем журнале призыв к закрытию. Я предполагаю, что мой тест выполняется слишком быстро (ха-ха, тест селена выполняется слишком быстро), и пул не успевает закрыться.   -  person Laurent Bourgault-Roy    schedule 09.03.2013
comment
Если я отменю развертывание своего приложения и подожду несколько минут, чтобы повторно развернуть его, пул c3p0 все еще будет открыт. Используйте ответ ниже до возникновения проблемы (см. hibernate.atlassian.net/browse/HHH-7364) исправлено.   -  person drvdijk    schedule 26.07.2013


Ответы (2)


У меня была та же проблема, и я успешно использовал обходной путь, предложенный в этом отчете об ошибке:

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

Вы должны сослаться на банку hibernate-c3p0-4.x.x.

person Dennie    schedule 17.07.2013
comment
Спасибо, мы получили немного более сложный обходной путь, который будет полезен. - person Laurent Bourgault-Roy; 17.07.2013