背景:
在项目中遇到下面这个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的连接池配置不熟,所以就找到下面的文章。


  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&amp;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 <!--初始化时获取的连接数,取值应在minPoolSizemaxPoolSize之间。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 如果maxStatementsmaxStatementsPerConnection均为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 时候都将校验其有效性。建议使用idleConnectionTestPeriodautomaticTestTable
 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>


应该是配置的原因。然后有找到这篇个说明

解决这个异常需要修改设置成如下:

<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 能不能解决问题还需要进一步检验。