Hibernate ORM和c3p0连接池

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



猜你喜欢

转载自blog.csdn.net/tonyhuang_google_com/article/details/46425287