前几天我把我之前运用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来操作数据库,后面这样修改了源码,发现正确。