1. 程式人生 > >Spring不對Hibernate原方法操作資料庫提供連線關閉服務

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來操作資料庫,後面這樣修改了原始碼,發現正確。