背景:
在项目中遇到下面这个exception
Exception occurred while logging on
Hibernate operation: Cannot open connection; uncategorized SQLException for SQL [???];
SQL state [null]; error code [0]; An SQLException was provoked by the following failure:
com.mchange.v2.resourcepool.ResourcePoolException: Attempted to use a closed or
broken resource pool; nested exception is java.sql.SQLException: An SQLException was
provoked by the following failure: com.mchange.v2.resourcepool.ResourcePoolException:
Attempted to use a closed or broken resource pool
分析:
看字面的意思,是连接池出问题。因为对spring的连接池配置不熟,所以就找到下面的文章。
<引用:
http://hi.baidu.com/javazyw/blog/item/4e668fc7e3557ac039db49c3.html>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:jee="http://www.springframework.org/schema/jee"
5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
6 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
7 <bean id="dataSource"
8 class="com.mchange.v2.c3p0.ComboPooledDataSource"
9 destroy-method="close">
10 <property name="driverClass">
11 <value>com.mysql.jdbc.Driver</value>
12 </property>
13 <property name="jdbcUrl">
14 <value>jdbc:mysql://192.168.3.110:3306/DBName?useUnicode=true&characterEncoding=GBK</value>
15 </property>
16 <property name="user">
17 <value>root</value>
18 </property>
19 <property name="password">
20 <value>root</value>
21 </property>
22
23 <!--连接池中保留的最小连接数。-->
24 <property name="minPoolSize">
25 <value>5</value>
26 </property>
27
28 <!--连接池中保留的最大连接数。Default: 15 -->
29 <property name="maxPoolSize">
30 <value>30</value>
31 </property>
32
33 <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
34 <property name="initialPoolSize">
35 <value>10</value>
36 </property>
37
38 <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
39 <property name="maxIdleTime">
40 <value>60</value>
41 </property>
42
43 <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
44 <property name="acquireIncrement">
45 <value>5</value>
46 </property>
47
48 <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
49 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
50 如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
51 <property name="maxStatements">
52 <value>0</value>
53 </property>
54
55 <!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
56 <property name="idleConnectionTestPeriod">
57 <value>60</value>
58 </property>
59
60 <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
61 <property name="acquireRetryAttempts">
62 <value>30</value>
63 </property>
64
65 <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
66 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
67 获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
68 <property name="breakAfterAcquireFailure">
69 <value>true</value>
70 </property>
71
72 <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
73 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
74 等方法来提升连接测试的性能。Default: false -->
75 <property name="testConnectionOnCheckout">
76 <value>false</value>
77 </property>
78 </bean>
79 <!-- Hibernate SessionFactory -->
80 <bean id="sessionFactory"
81 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
82 <property name="dataSource">
83 <ref local="dataSource" />
84 </property>
85 <property name="mappingResources">
86 <list>
87 <value>com/xh/hibernate/vo/User.hbm.xml</value>
88 </list>
89 </property>
90 <property name="hibernateProperties">
91 <props>
92 <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
93 <prop key="hibernate.show_sql">true</prop>
94 <prop key="hibernate.generate_statistics">true</prop>
95 <prop key="hibernate.connection.release_mode">auto</prop>
96 <prop key="hibernate.autoReconnect">true</prop>
97 </props>
98 </property>
99 </bean>
100 </beans>
应该是配置的原因。然后有找到这篇个说明
<引用:http://home.bendixinwen.cn:8080/blog/2010/01/27/1264560312821.html>
解决这个异常需要修改设置成如下:
<property name="acquireRetryAttempts">
<value>30</value>
</property>
<property name="acquireRetryDelay">
<value>100</value>
</property>
<property name="breakAfterAcquireFailure">
<value>false</value>
</property>
- acquireRetryAttempts
Default: 30
Defines how many times c3p0 will try to acquire a new Connection from the database before giving up.
If this value is less than or equal to zero, c3p0 will keep trying to fetch a Connection indefinitely
- acquireRetryDelay
Default: 1000
Milliseconds, time c3p0 will wait between acquire attempts.
- breakAfterAcquireFailure
Default: false
If true, a pooled DataSource will declare itself broken and be permanently closed
if a Connection cannot be obtained from the database after making acquireRetryAttempts to acquire one.
If false, failure to obtain a Connection will cause all Threads waiting for the pool to acquire a Connection
to throw an Exception, but the DataSource will remain valid, and will attempt to acquire again following a
call to getConnection().
再分析:
为什么会说
Attempted to use a closed or broken resource pool,应该是有链接没关闭。因为一直对<session.save>和<geHibernateTemplate().save> 这两种DAO实现方式不理解。找到下面这篇文章
<引用:http://jeoff.blog.51cto.com/186264/133434>
1 自动生成hibernate配置文件的时候,会在dao层用到getSession()方法来操作数据库记录,但是他还有个方法getHibernateTemplate(),这两个方法究竟有什么区别呢?
2 1.使用getSession()方法你只要继承sessionFactory,而使用getHibernateTemplate()方法必须继承 HibernateDaoSupport当然包括sessionFactory,这点区别都不是特别重要的,下面这些区别就很重要了
3 2.getSession()方法是没有经过spring包装的,spring会把最原始的session给你,在使用完之后必须自己调用相应的 close方法,而且也不会对声明式事务进行相应的管理,一旦没有及时关闭连接,就会导致数据库连接池的连接数溢出,getHibernateTemplate()方法是经过spring封装的,例如添加相应的声明式事务管理,由spring管理相应的连接。
4 在实际的使用过程中发现的确getHibernateTemplate()比getSession()方法要好很多,但是有些方法在getHibernateTemplate()并没有提供,这时我们用HibernateCallback 回调的方法管理数据库.
看看黑体标注的那句话,再看看程序代码会发现确实有很多使用session方式的DAO在最后没有关闭。root cause应该就是他了。
总结:
1. 是用session实现DAO,必须要在最后关闭session,不然会导致连接池链接溢出
2.可以使用 set
breakAfterAcquireFailure =false 来规避这个问题。
尾巴:
set
breakAfterAcquireFailure =false 能不能解决问题 还需要进一步检验。下回分解。