[c3p0] 第二篇:使用c3p0

前言

译文是根据c3p0-0.9.5.1版本的官方文档,加上自己的理解,整理翻译而成。能力有限,译文内容如果有误或是理解有偏差,还请大家纠正!

使用c3p0

从用户的角度看,c3p0只是简单的为用户提供符合jdbc标准的DataSource对象。当创建这些DataSource对象的时候,用户可以控制与其相关的各种属性。一旦DataSource创建完成,DataSource背后的东西对用户来讲,就是完全透明的。

三种方式获取c3p0带连接池的DataSource

  1. 直接实例化并配置一个CombopooledDataSource bean
  2. 使用DataSource工厂类
  3. 通过直接实例化PoolBackedDataSource并设置它的ConnectionPoolDataSource来创建的带有连接池的DataSource

大部分用户会发现实例化CombopooledDataSource一般来讲是最方便的途径。一旦实例化,c3p0的DataSource几乎就可以绑定到任何JNDI兼容的命名服务。

用户创建DataSource的时候,如果没有特别指定其各种属性,c3p0会提供默认的属性值,是通过硬编码的方式实现的。但是,用户也是可以通过配置文件等方式重写这些属性值的,如果是以配置文件的方式重写,那么配置文件需要放在CLASSPATH下或是ClassLoader可以找到的地方。

c3p0数据源可以通过多种方式配置

  1. 命名为c3p0.properties的java.util.Properties属性文件
  2. 更为先进的HOCON配置文件(例如:application.conf, application.json)
  3. 命名为c3p0-config.xml的XML文件

实例化并配置ComboPooledDataSource

最简单最直接的创建c3p0带有连接池的DataSource就是实例化com.mchange.v2.c3p0.ComboPooledDataSource.这是一个JavaBean风格的类,有一个public的无惨构造器,但是在使用DataSource之前,你必须确保至少设置一个jdbcUrl属性。你还可以设置user和password,如果你使用旧的不能预加载的JDBC驱动,你还应该设置driverClass。

ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("org.postgresql.Driver");// loads the driver
cpds.setJdbcUrl("jdbc:postgresql://localhost/testdb");
cpds.setUser("swaldman");
cpds.setPassword("test-password");

// The settings below are potional -- c3p0 can work with defaults
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);

// The DataSource cpds is now a fully configured and usable pooled DataSource

c3p0数据源的属性值可以由你来配置,或者直接使用默认的。c3p0支持命名配置,所以你可以同时配置多个DataSource。如果你想使用命名配置,那么就要使用“配置名称”作为构造参数来构造com.mchange.v2.c3p0.ComboPooledDataSource

ComboPooledDataSource cpds = new ComboPooledDataSource("intergalactoApp");

当然,你也可以重写它的任何配置属性,和上面一样。

使用数据源工厂类

作为可供选择的一种方案,你可以使用静态工厂类com.mchange.v2.c3p0.DataSource以传统的JDBC驱动的方式去创建不带连接池的DataSource,然后从不带连接池的数据源创建带连接池的数据源。

DataSource ds_unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/testdb", "swaldman", "test-password");
DataSource ds_pooled = DataSources.pooledDataSource(ds_unpooled);

// The DataSource ds_pooled is now a fully configured and usable pooled DataSource.
// The DataSource is using a default pool configuration and Postgres' JDBC driver
// is presumed to have already been loaded via the jdbc.drivers system property or an
// explicit call to Class.forName("org.postgresql.Driver") elsewhere.

如果你使用DataSources工厂类,又想以编程的方式重写默认配置参数,你可以提供一个map集合属性:

DataSource ds_unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/testdb", "swaldman", "test-password");
Map overrides = new HashMap();
overrides.put("maxStatements", "200");// Stringified property values work
overrides.put("maxPoolSize", new Integer(50));// "boxed primitives" also work

// create the PooledDataSource using the default configuration and our overrides
DataSource ds_pooled = DataSources.pooledDataSource(ds_unpooled, overrides);

// The DataSource ds_pooled is now a fully configured and usable pooled DataSource,
// with Statement caching enabled for a maximum of up to 200 statements and a maximum of 50 Connection.

如果你使用命名配置,你可以直接指定数据源的默认配置:

// Create the PooledDataSource using the a named configuration and specified overrides "intergalactoApp" is a named configuration
ds_pooled = DataSources.pooledDataSource(ds_unpooled, "intergalactoApp", overrides);

查询PooledDataSource的当前状态

c3p0 DataSource 支持池,包含ComboPooledDataSource实现并通过DataSources.pooledDataSource(...)返回对象,所有com.mchange.v2.c3p0.PooledDataSource的接口实现,都可以利用多种方法查询DataSource连接池的状态。下面是查询DataSource的简单代码:

// Fetch a JNDI-bound DataSource
InitialContext ictx = new InitialContext();
DataSource ds = (DataSource) ictx.lookup("java:comp/env/jdbc/myDataSource");

// make sure it's a c3p0 PooledDataSource
if (ds instanceof PooledDataSource) {
	PooledDataSource pds = (PooledDataSource) ds;
	System.err.println("num_connections: " + pds.getNumConnectionsDefaultUser());
	System.err.println("num_busy_connections: " + pds.getNumBusyConnectionsDefaultUser());
	System.err.println("num_idle_connections: " + pds.getNumIdleConnectionsDefaultUser());
	System.err.println();
} else {
	System.err.println("Not a c3p0 PooledDataSource!");
}

状态查询方法都类似于以下三个重载形式:

public int getNumConnectionsDefaultUser();
public int getNumCOnnections(String username, String password);
public int getNumConnectionsAllUsers();

c3p0以不同的验证信息维护各自的连接池。有众多的方法让你查询单独某个池的状态,或者是汇总所有的数据。值得注意的是像maxPoolSize这样的池配置参数是在每个认证上都会使用的。例如,如果你已经设置了maxPoolSize=20,DataSource正在管理着两对username-password,默认的一个,另一个通过调用getConnection(user,password)建立,调用getNumConnectionsAllUsers(),最大能看到的连接数应该是40.

多数应用只需要从DataSource获取默认的认证连接,典型的通过getXXXDefaultUser()收集连接数据。

还有连接池的真实数据,你可以取到每个DataSource的线程池状态数据。请参考PooledDataSource可选操作的完整列表。

使用C3P0Registry引用DataSource

如果通过JNDI或者是其它方式不方便或是不太可能得到DataSource的引用,你可以使用C3P0Registry类找到有效的c3p0 DataSource,它包含三个静态方法:

public static Set getPooledDataSources();
public static Set pooledDataSourcesByName(String dataSourceName);
public static PooledDataSource pooledDataSourceByName(String dataSourceName);

第一个方法返回给你c3p0 PooledDataSources的Set集合。如果你确信你的应用只会产生一个PooledDataSources,或者你能通过配置参数区分DataSource(通过"getters"检查),第一种方法就足够了。

因为并不总是如此,c3p0 PooledDataSources有一个叫做dataSourceName的特殊属性,当你构造DataSource的时候,你可以直接设置dataSourceName属性,或者像其它属性一样对其进行配置。否则,dataSourceName将会默认两种情况:

  1. 如果你用一个命名配置构造DataSource,就是你的配置名称。
  2. 如果你使用默认的配置,就会有一个唯一的名字,但不可预测。

这里并没有保证dataSourceName是唯一的。例如,如果两个c3p0 DataSource共享一个命名配置,而且你没有编程式的设置dataSourceName,那么这两个数据源将会共享配置名称。可以使用pooledDataSourcesByName(...)得到特定dataSourceName的所有数据源。

如果你确定DataSource的名字是唯一的(如果你想要使用C3P0Registry找到DataSource,你通常会愿意这么做),你可以使用更方便的方法pooledDataSourceByName(...),它将直接返回指定名字的DataSource的引用或者是null(如果没有DataSource)。如果你在多个共享名称的DataSource中使用pooledDataSourceByName(...),将会返回哪一个DataSource是没有明确定义的。

c3p0 PooledDataSources使用之后的清理

c3p0创建DataSources之后,最简单的清理方法是使用DataSources类定义的静态销毁方法。仅有PooledDataSources需要清理,如果在一个不带连接池的DataSource或者是non-c3p0 DataSource中使用,DataSources.destroy(...)也是没有危害的。

DataSource ds_pooled = null;
try {
	DataSource ds_unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/testdb", "swaldman", "test-password");
	ds_pooled = DataSources.pooledDataSource(ds_unpooled);
	// Do all kinds of stuff with that sweet pooled DataSource...
} finally {
	DataSources.destroy(ds_pooled);
}

可供选择的,c3p0的PooledDataSource接口包含一个close()方法,当你使用DataSource完成工作以后你可以调用它。所以,你可以转换一个c3p0派生出的DataSource为PooledDataSource然后关闭它。

static void cleanup(DataSource ds) throws SQLException {
// Make sure it's a c3p0 PooledDataSource
if (ds instanceof PooledDataSource) {
	PooledDataSource pds = (PooledDataSource) ds;
	pds.close();
} else
	System.err.println("Not a c3p0 PooledDataSource!");
}

ComboPooledDataSource是PooledDataSource的实例,可以直接通过close()关闭。PooledDataSource实现了java.lang.AutoCloseable,所以他们可以被Java 7 + try-with-resources块管理。

不是PooledDataSource的实例不能用close()方法,如果想要关闭他们优先在它们的finalize()中使用垃圾回收机制(garbage collection)。一如既往,析构(finalization)应该被认为是一个捕手,而不是一个提示或是清理资源的途径。

高级:构建自己的PoolBackedDataSource

大部分程序员这么做是有一些原因的,但是你可以一步步的构建PooledDataSource。通过实例化并配置一个无池DriverManagerDataSource,实例化一个WrapperConnectionPoolDataSource并设置一个无池DataSource作为它的nestedDataSource属性,然后PoolBackedDataSource使用这些设置connectionPoolDataSource属性。

如果你的驱动提供一个ConnectionPoolDataSource实现,这些事件序列就是首要的关键点,你会更倾向于这样使用c3p0,而不是使用c3p0的WrapperConnectionPoolDataSource,你可以创建一个PoolBackedDataSource设置它的connectionPoolDataSource属性。Statement池,ConnectionCustomizers,和很多c3p0特定的属性不被第三方ConnectionPoolDataSource支持(第三方DataSource实现只能替代c3p0的DriverManagerDataSource没有重大损失的功能)。

高级:原始连接和语句操作

注意:自c3p0-0.9.5起,通过代理c3p0支持标准JDBC4的unwrap()方法。注意如果你使用unwrap()方法,c3p0就不能清理任何你从原始Connections或是Statements生成的Statement或是ResultSet对象。使用者必须小心仔细的直接清理这些对象。此外,使用者应该注意不要以某种方式修改底层的Connections,致使它们不能再与其它的Connections替换,因为必须保持它们是适合于连接池的。

JDBC驱动有时候根据特定供应商,非标准API去定义Connection和Statement的实现。C3P0通过代理包装这些对象,所以你不能转换由C3P0返回的Connections或是Statements到特定供应商实现的类。C3P0并不提供任何方式去直接访问原始Connections和Statements,因为C3P0需要保持追踪Statements和ResultSets的创建,防止资源泄露和破坏连接池。

C3P0确实提供了一个API,允许你在底层Connection上反射调用非标准方法。为了使用它,第一步,转换Connection为一个C3P0ProxyConnection。然后调用rawConnectionOperation(),应用java.lang.reflect.Method对象,你希望调用的非标准方法作为其参数。你提供的Method对象将会被你提供的第二个参数目标调用(静态方法则为null),并且使用你提供的第三个参数。对于这个目标,任何的方法参数,你都可以应用特定的C3P0ProxyConnection.RAW_CONNECTION,在Method被调用之前,它将被底层特定供应商Connection对象替代。

C3P0ProxyStatement提供一个类似的API。
任何原始操作返回的Statements(包括Prepared和CallableStatements)和ResultSets都将被c3p0-managed,被父代理Connection的close()正确的清理。使用者必须仔细清理任何通过特定供应商返回的非标准资源。

这里有一个例子,在Oracle-specific API上调用原始Connection的静态方法:

C3P0ProxyConnection castCon = (C3P0ProxyConnection) c3p0DataSource.getConnection();
Method m = CLOB.class.getMethod("createTemporary", new Class[] { Connection.class, boolean.class, int.class });
Object[] args = new Object[] { C3P0ProxyConnection.RAW_CONNECTION, Boolean.valueOf(true), new Integer(10) };
CLOB oracleCLOB = (CLOB) castCon.rawConnectionOperation(m, null, args);

C3P0包含了对一些Oracle-specific方法特殊的支持,请另行参考官方文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值