数据库连接的建立及关闭是极耗费系统资源的操作,在多层结构的应用环境中,这种资源的耗费对系统性能影响尤为明显。通过DriverManager获得的数据库连接,一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完后立即关闭连接。频繁地打开、关闭连接将造成系统性能低下。
数据库连接池的解决方案是:当应用程序启动时,系统主动建立足够的数据库连接,并将这些连接组成一个连接池。每次应用程序请求数据库连接时,无须重新打开连接,而是从连接池中取出已有的连接使用,使用完后不再关闭数据库连接,而是直接将连接归还给连接池。通过使用连接池,将大大提高程序的运行效率。
对于共享资源的情况,有一个通用的设计模式:资源池(Resource Pool),这种设计模式用于解决资源的频繁请求、释放所造成的性能下降。为了解决数据库连接的频繁请求、释放,JDBC 2.0规范引入了数据库连接池技术。数据库连接池是Connection对象的工厂。数据库连接池的常用参数有:数据库的初始连接数、连接池的最大连接数、连接池的最小连接数、连接池每次增加的容量等。
JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由商用服务器如WebLogic、WebSphere等提供实现,也有一些开源组织提供实现,如DBCP和C3P0等,这些开源组织提供的实现可供开发者免费使用,本小节将讲解DBCP和C3P0的使用。
DBCP是Apache软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统common-pool。如果需要使用该连接池实现,则应在项目中增加相应的两个jar文件,到本书成书时,这两个jar文件的最新版本名称为:commons-dbcp2-2.9.0.jar和commons-pool2-2.6.2.jar。读者登录http://comons.apache.org即可下载commons-dbcp2-2.9.0-bin.zip和commons-pool2-2.6.2-bin.zip两个压缩文件,解压缩这两个文件即可得到上面提到的两个JAR文件,此外还要额外加入一个commons-logging-1.1.1.jar文件,本书的代码文件夹中会直接对读者提供这三个jar文件。
为了在程序中使用这两个JAR文件,应该把它们添加到CLASSPATH中。把jar文件添加到CLASSPATH中的操作在本章第18.2.2小节已经做过讲解,此处不再赘述。下面的【例18_09】展示了使用DBCP数据源在程序中的使用。
【例18_09 DBCP数据源】
Exam18_09.java
- import org.apache.commons.dbcp2.BasicDataSource;
- import java.sql.*;
- import javax.sql.*;
- public class Exam18_09 {
- public static void main(String[] args) {
- Connection con = null;
- Statement stm = null;
- ResultSet rs = null;
- //创建数据源对象
- BasicDataSource ds = new BasicDataSource() ;
- //设置连接池所需的驱动
- ds.setDriverClassName (Util1.driverName) ;
- //设置连接数据库的URL
- ds. setUrl (Util1.url) ;
- //设置连接数据库的用户名
- ds . setUsername (Util1.user) ;
- //设置连接数据库的密码
- ds. setPassword(Util1.password) ;
- //设置连接池的初始连接数
- ds .setInitialSize(5) ;
- //设置最大连接数
- ds.setMaxTotal(20);
- //设置连接池中最少有2个空闲的连接
- ds. setMinIdle (2);
- try {
- con = ds.getConnection();
- stm = con.createStatement();
- String sql = "select * from users";
- rs = stm.executeQuery(sql);
- System.out.printf("%-10s%-10s%-12s%-15s%-18s\n","玩家QQ号","玩家昵称","玩家性别","玩家生日","玩家手机号");
- while (rs.next()) {
- String userQQ = rs.getString("user_qq");
- String userName = rs.getString("user_name");
- String userSex = rs.getString("user_sex");
- String userBirthday = rs.getString("user_birthday");
- String userPhone = rs.getString("user_phone");
- System.out.printf("%-15s%-15s%-15s%-18s%-18s\n",userQQ,userName,userSex,userBirthday,userPhone);
- }
- }catch (SQLException e){
- e.printStackTrace();
- }finally {
- try {
- Util1.close(rs,stm,con);//①关闭rs和stm并释放con
- }catch (SQLException e){
- e.printStackTrace();
- }
- }
- }
- }
本例代码中配置数据源的各项参数值(驱动类名称、用户名、密码等)都来源于Util1类的静态属性,实际上这些参数也可以被写到配置文件中,在使用时从文件中读出各项参数值。
数据源和数据库连接不同,数据源无须创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可,也就是说,对于一个应用,创建并设置数据源的代码只要写一次即可。一般会把建议把上面程序中的ds设置成static成员变量,并用静态块对其进行初始化,程序中所有需要获取数据库连接的地方直接访问该ds对象,并获取数据库连接即可。此外,程序中的语句①调用了Util1类的静态方法close(),这个方法中有执行了con.close(),需要说明,数据源所产生的Connection对象与用DriverManager获得的Connection对象不同,数据源所产生的Connection对象执行close()方法只是把对象归还给连接池以便再次使用它,并没有真正关闭Connection对象。【例18_09】的执行结果与【例18_01】完全相同。
相比DBCP,C3P0数据源性能更胜一筹。C3P0 连接池不仅可以自动清理不再使用的Connection,还可以自动清理Statement和ResultSet。如果需要使用C3P0连接池,则应在系统中增加相应的jar文件,目前为止最新版本的jar文件名称是c3p0-0.9.5.5.jar,读者登录htp://ourceforge.net/prjects/c3p0/站点即可下载C3P0数据源的最新版本jar文件,本书的代码文件夹也直接为读者提供该文件。下面的【例18_10】展示了如何使用C3P0数据源建立连接并完成相应的操作。
【例18_10 C3P0数据源】
Exam18_10.java
- import com.mchange.v2.c3p0.ComboPooledDataSource;
- import java.sql.*;
- import javax.sql.*;
- public class Exam18_10 {
- public static void main(String[] args) {
- Connection con = null;
- Statement stm = null;
- ResultSet rs = null;
- try {
- //创建数据源对象
- ComboPooledDataSource ds = new ComboPooledDataSource() ;
- //设置连接池连接数据库所需的驱动
- ds.setDriverClass (Util1.driverName) ;
- //设置连接数据库的URL
- ds.setJdbcUrl (Util1.url) ;
- //设置连接数据库的用户名
- ds.setUser (Util1.user) ;
- //设置连接数据库的密码
- ds.setPassword(Util1.password) ;
- //设置连接池的最大连接数
- ds.setMaxPoolSize(40) ;
- //设置连接池的最小连接数
- ds.setMinPoolSize(2) ;
- //设置连接池的初始连接数
- ds.setInitialPoolSize(10) ;
- //设置连接池的缓存Statement的最大数
- ds. setMaxStatements (180) ;
- con = ds.getConnection();
- stm = con.createStatement();
- String sql = "select * from users";
- rs = stm.executeQuery(sql);
- System.out.printf("%-10s%-10s%-12s%-15s%-18s\n","玩家QQ号","玩家昵称","玩家性别","玩家生日","玩家手机号");
- while (rs.next()) {
- String userQQ = rs.getString("user_qq");
- String userName = rs.getString("user_name");
- String userSex = rs.getString("user_sex");
- String userBirthday = rs.getString("user_birthday");
- String userPhone = rs.getString("user_phone");
- System.out.printf("%-15s%-15s%-15s%-18s%-18s\n",userQQ,userName,userSex,userBirthday,userPhone);
- }
- }catch (Exception e){
- e.printStackTrace();
- }
- }
- }
在【例18_10】的代码中,rs、stm、con都无需执行关闭操作,这是因为C3P0数据源能够自动自动释放它们。【例18_10】的运行结果也与【例18_01】相同。