Как отключить пул соединений при выгрузке контекста?

после разработки нескольких веб-приложений, которые имели аналогичную настройку с spring, hibernate и c3p0 в качестве пула соединений, я хотел исследовать проблему, которую я замечал каждый раз: пул соединений поддерживает соединения до тех пор, пока вы не выключите tomcat (или ваш сервер приложений).

Сегодня я создаю самый простой проект, который я мог бы использовать с этими четырьмя зависимостями:

org.springframework:spring-web
org.springframework:spring-orm
org.hibernate:hibernate-core
c3p0:c3p0

(плюс специальный драйвер JDBC).

Мой web.xml создает только ContextLoaderListener, который настраивает контекст приложения.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/applicationContext.xml
    </param-value>
</context-param>

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

контекст приложения состоит из двух bean-компонентов — источника данных и фабрики сеансов:

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass">
        <value>org.postgresql.Driver</value>
    </property>
    <property name="jdbcUrl">
        <value>jdbc:postgresql://localhost/mydb</value>
    </property>
    <property name="user">
        <value>usr</value>
    </property>
    <property name="password">
        <value>pwd</value>
    </property>
</bean>

Когда я запускаю веб-приложение и либо просматриваю MBeans jconsole, либо проверяю свою СУБД на наличие открытых соединений, я замечаю три начальных соединения, созданных c3p0.

ПРОБЛЕМА: когда я говорю tomcat остановить веб-приложение, они все еще остаются!

Я создал еще один ServletContextListener, который имеет только реализованный метод contextDestroyed и программно отключает sessionFactory (а также отменяет регистрацию драйверов JDBC, что также не выполняется автоматически). Код:

@Override
public void contextDestroyed(ServletContextEvent sce) {

    ...
    sessionFactory().close();

    // deregister sql driver(s)
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
        Driver driver = drivers.nextElement();
        try {
            DriverManager.deregisterDriver(driver);
            log.info("deregistering jdbc driver: " + driver);
        } catch (SQLException e) {
            log.error("error deregistering jdbc driver: " + driver, e);
        }
    }
}

Но так ли это? Нет ли встроенного механизма, о котором я не знаю?


person realsim    schedule 23.11.2012    source источник


Ответы (1)


Вы пытались указать метод уничтожения?

<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
person Stanislav Bashkyrtsev    schedule 23.11.2012
comment
Нет ничего ценнее опыта... это сработало! Спасибо (но как насчет отмены регистрации драйвера jdbc?) - person realsim; 23.11.2012
comment
Отмена регистрации драйверов просто удаляет их из внутреннего статического списка DriverManager. Когда в следующий раз пул соединений захочет получить новый ресурс, он запрашивает его DriverManager. Но если соединение уже было установлено и осталось в пуле, то ничто не мешает ему продолжать работать. Кстати, это также приводит к утечке памяти загрузчика классов, потому что потоки пула соединений не позволяют уничтожать классы. Пожалуйста, отметьте вопрос как решенный, если считаете, что у вас больше нет вопросов. - person Stanislav Bashkyrtsev; 23.11.2012
comment
Вы когда-нибудь видели такой вывод при остановке контекста? --- СЕРЬЕЗНАЯ: веб-приложение [/example] зарегистрировало драйвер JDBC [org.postgresql.Driver], но не смогло отменить его регистрацию, когда веб-приложение было остановлено. Чтобы предотвратить утечку памяти, JDBC Driver был принудительно отменен. --- Интересно, почему ребята из tomcat принудительно разрегистрируют его, когда это не вредит. - person realsim; 25.11.2012
comment
На самом деле это наносит такой же вред, как и потоки, которые не были остановлены - утечка загрузчика классов. Каждый класс содержит ссылку на загрузчик классов, который его загрузил, а загрузчик классов содержит ссылки на все загруженные им классы. Таким образом, загрузчик классов не может быть подвергнут сборке мусора, а также все классы, и вы получите permgen OutOfMemory. Вот как можно избавиться от этого предупреждения: github.com/jtalks-org/poulpe/blob/master/poulpe-view/ - person Stanislav Bashkyrtsev; 25.11.2012
comment
Хорошо, спасибо! Как видите, это уже было в моем последнем фрагменте кода. - person realsim; 26.11.2012