po opracowaniu kilku aplikacji internetowych, które wszystkie miały podobną konfigurację ze sprężyną, hibernacją i c3p0 jako connectionpool, chciałem zbadać problem, który zauważyłem za każdym razem: Connectionpool utrzymuje połączenia, dopóki nie zamkniesz tomcat (lub serwera aplikacji).Jak zamknąć pulę połączeń po rozładowaniu kontekstu?
Dziś tworzę projekt najbardziej podstawowe mogłem z tych czterech zależności:
org.springframework:spring-web
org.springframework:spring-orm
org.hibernate:hibernate-core
c3p0:c3p0
(plus odpowiedni sterownik JDBC).
Mój web.xml tworzy tylko ContextLoaderListener, który konfiguruje kontekst aplikacji.
<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>
kontekst aplikacji składa się z dwóch ziaren - źródła danych i sesja fabryczne:
<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>
Kiedy uruchomić webapp i albo zajrzeć do MBean'ami JConsole lub sprawdź mój DBMS dla otwartych połączeń Zauważyłem trzy początkowe połączenia wykonane przez c3p0.
PROBLEM: Kiedy mówię Tomcat, aby zatrzymać webapp, nadal pozostają!
Utworzono inny obiekt ServContContextListener, który ma tylko metodę contextDestroyed zaimplementowaną i programowo wyłącza sessionFactory (a także wyrejestrowuje sterowniki JDBC, które również nie są wykonywane automatycznie). Kod:
@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);
}
}
}
Ale czy to naprawdę jest? Czy nie ma wbudowanego mechanizmu, którego nie jestem świadomy?
Nie ma nic tak cennego jak doświadczenie ... zadziałało! Dziękuję (ale co z wyrejestrowaniem sterownika jdbc?) – realsim
Wyrejestrowanie sterowników po prostu usuwa je z wewnętrznej statycznej listy 'DriverManager'. Gdy następnym razem pula połączeń chce zdobyć nowy zasób, prosi o podanie "DriverManager". Ale jeśli połączenie zostało już nabyte i pozostaje w puli, nic nie przeszkadza mu w kontynuowaniu pracy. BTW, powoduje to również wycieki pamięci klasy Loader, ponieważ wątki puli połączeń nie pozwalają na niszczenie klas. Zaznacz pytanie jako rozwiązane, jeśli uważasz, że nie masz więcej pytań. –
@realsim: nie zapomnij przyjąć tej odpowiedzi! –