Spring不對Hibernate原方法操作資料庫提供連線關閉服務
前幾天我把我之前運用Hibernate+struts1.x框架開發的web專案搬到Spring框架上,其中Hibernate和struts都配置到applicationContext.xml檔案上,通過初步測試發現所有線都已經打通了。到今天,我同樣是執行該專案,發現一個很詭異的問題就是,當我連續地點選訪問資料庫的時候,前幾次還是沒有問題的,但點選多幾次就發現專案就死了,所有需要連線資料庫的操作的不行了,點其他不用連線資料庫的操作還是可以的。同時專案後臺並沒有報什麼錯誤。一下子就懵逼了,無從下手,因為這個並不是編譯或者執行錯誤。
靜下心來細想一下,出現這種現象第一種:中可能性就是出現死迴圈,但我檢查java中並沒有死迴圈(因為就算我點選同一個按鈕,呼叫同一個方法,點選幾次之後也會出現這種情況);第二種:就是有執行緒非同步造成死鎖的原因,但是就算我加上同步鎖還是這樣的情況,所以也不是死鎖的問題;第三種:就是記憶體溢位,但如果記憶體溢位時會報錯的,並且就點選幾次去連線資料庫也不會出現記憶體溢位。還會是什麼問題呢?還有就是這個專案很奇葩的問題就是訪問資料庫就會出現這種問題,而訪問其他的就正常,所以也許是資料庫的問題。這時才突然想起如果資料庫連線不及時關閉也會出現這種問題,但檢視一下Spring的配置,資料來源已經配置了關閉。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}" ></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
還會是什麼問題呢?並且一開始就可以訪問資料庫,說明也不是資料庫驅動的問題。想一下還是想去看一下資料庫那邊有沒有及時關閉連線了沒(也是一種走投無路的嘗試了),我用的是sql server 2014資料庫所以用下面的語句查詢:
SELECT * FROM [Master].[dbo].[SYSPROCESSES] WHERE [DBID] IN
(
SELECT [DBID] FROM [Master].[dbo].[SYSDATABASES] WHERE NAME='資料庫名'
)
出現的結果出我意料,竟然所有的資料庫連線都sleeping狀態
這個說明資料庫連線並沒有及時關閉,所有都是sleeping狀態,學過java多執行緒核心同學的都知道,sleeping沒有釋放鎖,所以其他執行緒只能等待鎖,這個也就是為什麼我們點選去訪問資料庫的操作一直沒有反應,同時沒有報錯,因為後面點選的在等待鎖。
我們再回來問題的原點,我們在spring中明明配置了,資料來源關閉,但為什麼事實上並沒有關閉。spring框架中明明寫著:在Spring裡,Hibernate的資源要交給Spring來管理,Hibernate及其SessionFactory等只是Spring一個特殊的bean,由Spring負責例項化與銷燬。因此,Dao曾只需要繼承HibernateDaoSupport,而不需要與Hibernate的Api打交道,不需要開啟,關閉Hibernate的session、Transaction,Spring自動維護這些物件。
我查詢了相關的資料,都說Spring會自動關閉資料庫的連線,我反覆檢查我的程式碼,再與別人的原始碼比較,發現我用的是Hibernate框架提供的方法,即session=this.getSession(true)獲取Session。而不是Spring框架提供的getHibernateTemplate()來操作資料庫。難道是這裡出現問題嗎?我決定寫一個測試類看一下:
介面類:(Spring需要介面程式設計)
package com.cph.daoInter;
import java.util.List;
import com.cph.domain.Cat;
public interface CatInter {
public List listCat();
}
實現類:
package com.cph.daoImpl;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.cph.daoInter.CatInter;
import com.cph.domain.Cat;
public class CatImpl extends HibernateDaoSupport implements CatInter {
@Override
public List listCat() {
// TODO Auto-generated method stub
String hql="from Cat";
// return getHibernateTemplate().find(hql);
return this.getSession(true).createQuery(hql).list();
}
}
applicationContext.xml檔案:
<?xml version="1.0" encoding="utf-8"?>
<beans xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.microsoft.jdbc.sqlserver.SQLServerDriver"></property>
<property name="url" value="jdbc:sqlserver://localhost:1433;DatabaseName=cph"></property>
<property name="username" value="sa"></property>
<property name="password" value="chenpeihong"></property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" destroy-method="destroy">
<property name="dataSource" ref="dataSource"></property>
<property name="mappingResources">
<list>
<value>/com/cph/domain/cat.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="catImpl" class="com.cph.daoImpl.CatImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
測試類:
package com.cph.texts;
import java.util.List;
import org.hibernate.hql.ast.tree.FromClause;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import com.cph.daoInter.CatInter;
import com.cph.domain.Cat;
import com.cph.servicesInter.hibernateServiceInter;
public class HibernateText {
public static void main(String[] args) {
// TODO Auto-generated method stub
XmlBeanFactory xmlBeanFactory=new XmlBeanFactory(new ClassPathResource("hibernateApplicationContexnt.xml"));
CatInter catInter=(CatInter) xmlBeanFactory.getBean("catImpl");
for(int i=0;i>=0;i++){
catInter.listCat();
}
}
}
發現用這樣寫,後臺發出八條的sql語句後就再也沒有列印了,同時任務按鈕一直是紅色的,說明執行緒沒有結束:
在資料庫那邊顯示的連線執行緒全都是sleeping狀態:
而如果把實現類的修改如下:
package com.cph.daoImpl;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.cph.daoInter.CatInter;
import com.cph.domain.Cat;
public class CatImpl extends HibernateDaoSupport implements CatInter {
@Override
public List listCat() {
// TODO Auto-generated method stub
String hql="from Cat";
return getHibernateTemplate().find(hql);
// return this.getSession(true).createQuery(hql).list();
}
}
後臺一直在列印sql語句,同時sql server資料庫在sleeping 和runnable之間跳轉,說明資料庫得到正常的關閉。
所以我發現了問題的所在了,是因為Spring框架對Hibernate框架提供的方法,即session=this.getSession(true)獲取Session來操作資料庫不提供連線關閉服務,要用Spring提供的通過繼承HibernateDaoSupport提供的getHibernateTemplate來操作資料庫,後面這樣修改了原始碼,發現正確。