简介
数据库连接池C3P0框架是个非常优异的开源类库,它根据JDBC3规范和JDBC2规范的标准扩展部分定义实现,使得传统的JDBC更适合企业级开发。在0.9.5版本中,C3P0完全支持JDBC4规范。 C3P0能够高性能的管理数据源,它提供了如下几个有用的服务:
- 获取数据库连接的方式。由传统的基于DriverManager的JDBC驱动获取转换为新的javax.sql.DataSource模式获取。
- 将连接池和PreparedStatement隐藏在DataSource后面。DataSource可以"包装"传统的驱动或者是任何不带连接池的DataSource。
C3P0库在以下几方面表现优秀:
- C3p0数据源都是Referenceable(可引用的)和Serializable(可序列化的),因此适合绑定基于JNDI的命名服务。
- 当把从连接池中取出的Connection和Statement放回连接池的时候,Statement和ResultSet将被仔细的清理,用来预防因懒惰采用仅仅清理Connection的资源管理策略而导致的资源耗尽的情况发生。
- C3p0库采用由JDBC2和JDBC3规范定义的方法。
driverClass=xxx
jdbcUrl=xxx
user=xxx
password=xxx
然后代码中实现:
Properties props = new Properties();
InputStream in = Thread.class.getResourceAsStream("cfg.properties");
props.load(in);
in.close();
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass(props.getProperty("driverClass"));
cpds.setJdbcUrl(props.getProperty("jdbcUrl"));
cpds.setUser(props.getProperty("user"));
cpds.setPassword(props.getProperty("password"));
上面的属性文件也可以写成xml的形式:
root
xxx
xxx
xxx
...
然后自己解析xml文件,这样可以实现多个数据源的配置。
2. 配置默认的属性文件在类路径下提供一个c3p0.properties文件(不能改名),配置如下:
c3p0.driverClass=com.mysql.jdbc.Driver #驱动
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/jdbc #地址
c3p0.user=root #用户名
c3p0.password=lovejava #密码
c3p0.initialPoolSize=6 #连接池初始化时创建的连接数
c3p0.minPoolSize=3 #连接池保持的最小连接数
c3p0.acquireIncrement=3 #连接池在无空闲连接可用时一次性创建的新数据库连接数,默认值为3
#连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大,默认值为15
c3p0.maxPoolSize=15
#连接的最大空闲时间,单位秒。如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接
c3p0.maxIdleTime=100
#连接池在获得新连接失败时重试的次数,如果小于等于0则无限重试直至连接获得成功
c3p0.acquireRetryAttempts=30
c3p0.acquireRetryDelay=1000 #连接池在获得新连接时的间隔时间
...
上面只提供了最基本的配置项,其他配置项参照文档配置,记得是c3p0.后面加属性名就是了,最后初始化数据源的方式就是这样简单:
DataSource ds = new ComboPooledDataSource();
return ds;
然后就可以使用数据源了,C3P0会对c3p0.properties进行自动解析的。
3. 在类路径下提供一个c3p0-config.xml文件这种方式可以为多个数据源服务,提供default-config和named-config两种配置方式
root
java
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/jdbc
6
30
100
10
root
java
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/jdbc
10
30
100
10
在Java代码中,只需要通过如下语句就能获取到数据源了:
DataSource ds = new ComboPooledDataSource("mySource");
注意: 使用连接池后,Connection还需要关闭,即仍然需要connection.close()方法。当然,这个close()最终执行的代码,并不是直接关闭物理连接,而是将其返回到连接池,以便后来者能继续使用。
示例:基于C3P0的JDBC工具类:public class DBUtils {
private static DataSource ds = null;
//使用ThreadLocal存储当前线程中的Connection对象
private static ThreadLocal threadLocal = new
ThreadLocal();
static{ //在静态代码块中创建数据库连接池
try{
ds = new ComboPooledDataSource("mySource");//使用C3P0的命名配置创建数据源
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
// 从数据源中获取数据库连接
public static Connection getConnection() throws SQLException{
Connection conn = threadLocal.get();//从当前线程中获取Connection
if(conn==null){
conn = getDataSource().getConnection();//从数据源中获取数据库连接
threadLocal.set(conn); //将Connection绑定到当前线程
}
return conn;
}
public static void startTransaction(){ //开启事务
try{
Connection conn = threadLocal.get();
if(conn==null){
conn = getConnection();
threadLocal.set(conn); //把 Connection绑定到当前线程上
}
conn.setAutoCommit(false); //开启事务
}catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void rollback(){ //回滚事务
try{
Connection conn = threadLocal.get();//从当前线程中获取Connection
if(conn!=null){
conn.rollback(); //回滚事务
}
}catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void commit(){ //提交事务
try{
Connection conn = threadLocal.get(); //从当前线程中获取Connection
if(conn!=null){
conn.commit(); //提交事务
}
}catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void close(){ //关闭连接(并不是真的关闭,而是把连接还给数据库连接池)
try{
Connection conn = threadLocal.get(); //从当前线程中获取Connection
if(conn!=null){
conn.close(); //----------①
threadLocal.remove();//解除当前线程上绑定Connection
}
}catch (Exception e) {
throw new RuntimeException(e);
}
}
public static DataSource getDataSource(){ //获取数据源
return ds; //从数据源中获取数据库连接
}
}
通过ComboPooledDataSource获取到的Connection是NewProxyConnection,该类重写了close()方法,所以编号①处不会直接释放关闭到物理连接,而是将其重新放回到连接池中。