1.Hibernate ORM及c3p0的问题
在使用hibernate ORM 4.3.9时发现,hibernate附带的c3p0连接池不起作用.使用的数据库是MySql5.6
下载 hibernate 并解压后,可以在 lib/optional/c3p0 下发现c3po连接池实现类。在 documentation/devguide/en-US/html_single/index.html 可以找到配置c3p0的文档。
将lib/required下的所有jar和lib/optional/c3p0所有jar 加到Java程序的classpath.
按照文档说明 org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider
应该是 ConnectionProvider接口的实现.但在所有jar包中没有找到这个类,这应该是文档的一个bug.
打开 lib\optional\c3p0\hibernate-c3p0-4.3.9.Final.jar 发现 org\hibernate\c3p0\internal\C3P0ConnectionProvider.class 实现了ConnectionProvider (org.hibernate.engine.jdbc.connections.spi.ConnectionProvider) .因此将hibernate.connection.provider_class改写为
hibernate.connection.provider_class=org.hibernate.c3p0.internal.C3P0ConnectionProvider
hibernate.properties 配置池的最小值是1,最大值是10.如下所示
hibernate.connection.driver_class =com.mysql.jdbc.Driver hibernate.connection.url =jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 hibernate.connection.username =User001 hibernate.connection.password =XXX hibernate.connection.provider_class=org.hibernate.c3p0.internal.C3P0ConnectionProvider hibernate.c3p0.min_size=1 hibernate.c3p0.max_size=10 hibernate.c3p0.timeout= 10 #秒对应于c3p0的? hibernate.c3p0.max_statements= 100 hibernate.c3p0.idle_test_period= 300 #秒 hibernate.c3p0.testConnectionOnCheckin= true hibernate.c3p0.maxIdleTime=1200 #秒测试程序是每隔3秒往数据库插入10条记录, 在运行到第20几次(程序运行60多秒)后,开始报错 :too many open connection.
在测试程序运行期间, 在MySql admin console 间歇运行 SHOW FULL PROCESSLIST 发现MySql开的进程越来越多。可以断定hibernate的 c3p0连接池不起作用(如果起作用,数据库服务器端显示的User001的进程始终应该不大于10,而非不断增加).
可见是 hibernate自身出了问题,并没有使用连接池的重用机制. 经测试发现版本4.3.10, 4.3.9 , 4.2.19 , 3.6 似乎都有这样的问题.
可行的方案是写自己的ConnectionProvider实现类,修改配置文件hibernate.connection.provider_class指向自己的实现类。
package com.hdp.smp.connection.MyConnectionProvider import com.mchange.v2.c3p0.DataSources; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; public class MyConnectionProvider implements ConnectionProvider { static String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8"; static String user = "User001"; static String password = "xxxx"; static String dirverName = "com.mysql.jdbc.Driver"; static DataSource cpds = getDataSource(); static DataSource getDataSource() { DataSource pooled = null; try { Class.forName(dirverName); DataSource unpooled = DataSources.unpooledDataSource(url, user, password); pooled = DataSources.pooledDataSource(unpooled); return pooled; } catch (Exception e) { e.printStackTrace(); } return pooled; } public MyConnectionProvider() { super(); } @Override public Connection getConnection() throws SQLException { Connection conn = cpds.getConnection(); return conn; } @Override public void closeConnection(Connection connection) throws SQLException { connection.close(); } @Override public boolean supportsAggressiveRelease() { return true; } @Override public boolean isUnwrappableAs(Class c) { return false; } @Override public <T extends Object> T unwrap(Class<T> c) { return null; } }然后将hibernate.properties中的hibernate.connection.provider_class改为
hibernate.connection.provider_class=com.hdp.connection.MyConnectionProvider然后再次运行测试程序,发现一切正常,程序跑了1个多小时都没出现任何错误。
观察mysql服务器端processlist,发现连接数始终在2-3个变化。停止程序后,观察mysql服务器端processlist发现user001的process只剩一个,这刚好符合hibernate.c3p0.min_size=1的设置. 20分钟后再观察processlist,发现user001的进程已经全部没有,这正好符合hibernate.c3p0.maxIdleTime=1200的设置.
2. 连接池及其c3p0设置
连接池的目的是重用连接,常用的数据库连接池有c3p0,dbcp和boneCp等,还有其他的连接池比如http连接池。由于创建和销毁连接是个开销比较大的过程,因此对每个操作都产生新的连接是不能支持大并发的需求的。
一旦连接池化,就有其他相应问题考虑:
1) 连接重用
2)空闲连接回收 (长时间不用的连接应该被销毁),否则会导致连接泄露(另一种内存泄露)。
2)无效连接清除。如果客户端保持了若干连接,而数据库服务器重启,那么客户端哪些仍被保持的连接实际已经失效。因此必须有检测机制定期清除无效连接。
3)失败恢复。如果数据服务器重启以后,客户端仍在运行的程序不能恢复到正常状态,那么连接池机制就有问题,比如Apache dbcp就有这样的问题,已经被很多产品弃用。这个问题可以这样发现:写一个测试程序,一直运行,不时往数据库插入或查询数据,在服务器 down后,应看到程序抛错,而数据库服务器回到ok状态后,那么测试程序的后续操作应该正常。当然测试程序要健壮,比如在循环内对每个操作都try ..catch..。
选用连接池时,把上述3种情况都测到,否则在生产系统会遇到严重问题。
如果要独立使用c3p0 连接池 ,可以参考2种情况:
1) pooled datasource
写一个c3p0.properties放置在classpath
###c3p0.properties c3p0.acquireIncrement=3 c3p0.initialPoolSize=0 c3p0.maxPoolSize=3 c3p0.maxIdleTime=600 ##秒数 一个空闲连接最多在池中呆的时间,缺省是0意味永不过期 c3p0.minPoolSize=1 c3p0.idleConnectionTestPeriod=300 ##秒数 隔期检测空闲但没被使用的连接。缺省值是0意味永不检测 c3p0.testConnectionOnCheckin=true ##c3p0将连接放入连接池时是否检查有效性,默认是 false c3p0.testConnectionOnCheckout=true ##从连接池取出连接时是否检测有效性,默认false c3p0.maxStatements=100 ##sql语句缓冲个数 c3p0.checkoutTimeout=6000 ##客户端获取连接最大等待时间。默认是0即如果数据库连不上则无限等待.如果程序没有设置对应属性, c3p0会在classpath搜索c3p0.properties并载入.
private static DataSource cpds=getDataSource(); private static DataSource getDataSource() { DataSource pooled = null; try { Class.forName(dirverName); DataSource unpooled = DataSources.unpooledDataSource(url, user, password); pooled = DataSources.pooledDataSource(unpooled); return pooled; } catch (Exception e) { e.printStackTrace(); } return pooled; } public static Connection getConnection() { Connection conn = cpds.getConnection(); }2) ComboPooledDataSource
ComboPooledDataSource dataSource=new ComboPooledDataSource(); dataSource.setUser( "id "); dataSource.setPassword( "pw "); dataSource.setJdbcUrl( "jdbc:mysql://127.0.0.1:3306/test") dataSource.setDriverClass( "com.mysql.jdbc.Driver "); dataSource.setInitialPoolSize(2); dataSource.setMinPoolSize(1); dataSource.setMaxPoolSize(10); dataSource.setMaxStatements(50); dataSource.setMaxIdleTime(60);
3.其他连接池
boneCP :性能比c3p0快倍以上,文档不全。
Apache DBCP:有bug不建议使用.(当数据库重启后,客户端的程序不能恢复对数据的正常操作)
Hibernate ORM url: http://hibernate.org/orm/
c3p0 url: http://www.mchange.com/projects/c3p0/#configuration_properties