使用Apache DBCP连接池重构DBUtility

4.1 问题

本案例要求使用Apache DBCP连接池重构类DBUtility为ConnectionSource类,并重构案例“实现DBUtility,提供连接的获取,关闭功能”中的EmpDAO类,在该类中使用ConnectionSource来获取连接。

4.2 方案

直接使用JDBC访问数据库时,需要避免以下隐患:

1.每一次数据操作请求都需要建立数据库连接、打开连接、存取数据和关闭连接等步骤。而建立和打开数据库连接是一件既耗资源又费时的过程,如果频繁发生这种数据库操作,势必会使系统性能下降。

2. 连接对象代表着数据库系统的连接进程,是有限的资源。如果系统的使用用户非常多,有可能超出数据库服务器的承受极限,造成系统的崩溃。

数据库连接池是解决上述问题最常用的方法。所谓连接池,即可以创建并持有数据库连接的组件。连接池可以预先创建并封装一些连接对象并将其缓存起来,当需要使用连接对象时可以向连接池“借”一个连接,用完之后将其“归还”到连接池中。数据库连接池的主要功能如下:

1. 连接池对象的创建和释放。

2. 服务器启动时,创建指定数量的数据库连接。

3. 为用户请求提供可用连接。如果没有空闲连接,且连接数没有超出最大值,创建一个新的数据库连接。

4. 将用户不再使用的连接标识为可用连接,等待其他用户请求。

5. 当空闲的连接数过多时,释放连接对象。

连接池组件一般都需要实现JDBC规范中的javax.sql.DataSource接口。DataSource接口定义了获取连接的方法getConnection方法。常用的连接池组件有DBCP、c3p0和proxool等,本案例以Apache的DBCP组件为例来实现数据库连接池。简单的应用代码如下所示:


    
    
  1.      BasicDataSource ds = new BasicDataSource();
  2.     ds.setUrl("jdbc:oracle:thin:@192.168.0.23:1521:tarena");
  3.     ds.setDriverClassName("oracle.jdbc.OracleDriver");
  4.     ds.setUsername("openlab");
  5.     ds.setPassword("open123");
  6.     Connection con = ds.getConnection();
  7.     
  8.     Statement stmt = con.createStatement();
  9.     ResultSet rs = stmt.executeQuery("select count(*) from emp");
  10.     if (rs.next()) {
  11.         System.out.println(rs.getInt(1));
  12.     }
  13.     stmt.close();
  14.     con.close();

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:导入使用DBCP组件所需的jar包

在当前工程下,导入使用DBCP组件所需的jar包,包括commons-dbcp.jar以及commons-pool.jar两个jar包,这两个jar包的名字可能会因为版本的不同,名字的最后为版本信息,例如:commons-dbcp-1.2.1.jar。

步骤二:重构db.properties文件

重构db.properties文件,在该文件中添加创建数据库连接池所需的信息,包括初始化连接数、最大空闲连接数、大小空闲连接数、最大连接数量以及超时回收时间。该文件内容如下所示:


    
    
  1. jdbc.driverClassName=oracle.jdbc.OracleDriver
  2. jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
  3. jdbc.username=scott
  4. jdbc.password=tiger
  5. #<!-- 初始化连接 -->
  6. dataSource.initialSize=10
  7. #<!-- 最大空闲连接 -->
  8. dataSource.maxIdle=20
  9. #<!-- 最小空闲连接 -->
  10. dataSource.minIdle=5
  11. #最大连接数量
  12. dataSource.maxActive=50
  13. #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60-->
  14. dataSource.maxWait=1000

步骤三:创建ConnectionSource类,在该类中初始化数据源信息

首先创建ConnectionSource类;然后在该类中添加init方法,在该方法中数据源信息进行初始,代码如下所示:


    
    
  1. import java.io.IOException;
  2. import java.sql.Connection;
  3. import java.sql.SQLException;
  4. import java.util.Properties;
  5. import org.apache.commons.dbcp.BasicDataSource;
  6. public class ConnectionSource {
  7.     private static BasicDataSource dataSource = null;
  8.     public ConnectionSource() {
  9.     }
  10.     public static void init() {
  11.         Properties dbProps = new Properties();
  12.         // 取配置文件可以根据实际的不同修改
  13.         try {
  14.             dbProps.load(ConnectionSource.class.getClassLoader().getResourceAsStream(
  15.                     "day01/v4/db.properties"));
  16.         } catch (IOException e) {
  17.             e.printStackTrace();
  18.         }
  19.         try {
  20.             String driveClassName = dbProps.getProperty("jdbc.driverClassName");
  21.             String url = dbProps.getProperty("jdbc.url");
  22.             String username = dbProps.getProperty("jdbc.username");
  23.             String password = dbProps.getProperty("jdbc.password");
  24.             String initialSize = dbProps.getProperty("dataSource.initialSize");
  25.             String minIdle = dbProps.getProperty("dataSource.minIdle");
  26.             String maxIdle = dbProps.getProperty("dataSource.maxIdle");
  27.             String maxWait = dbProps.getProperty("dataSource.maxWait");
  28.             String maxActive = dbProps.getProperty("dataSource.maxActive");
  29.             dataSource = new BasicDataSource();
  30.             dataSource.setDriverClassName(driveClassName);
  31.             dataSource.setUrl(url);
  32.             dataSource.setUsername(username);
  33.             dataSource.setPassword(password);
  34.             // 初始化连接数
  35.             if (initialSize != null)
  36.                 dataSource.setInitialSize(Integer.parseInt(initialSize));
  37.             // 最小空闲连接
  38.             if (minIdle != null)
  39.                 dataSource.setMinIdle(Integer.parseInt(minIdle));
  40.             // 最大空闲连接
  41.             if (maxIdle != null)
  42.                 dataSource.setMaxIdle(Integer.parseInt(maxIdle));
  43.             // 超时回收时间(以毫秒为单位)
  44.             if (maxWait != null)
  45.                 dataSource.setMaxWait(Long.parseLong(maxWait));
  46.             // 最大连接数
  47.             if (maxActive != null) {
  48.                 if (!maxActive.trim().equals("0"))
  49.                     dataSource.setMaxActive(Integer.parseInt(maxActive));
  50.             }
  51.         } catch (Exception e) {
  52.             e.printStackTrace();
  53.             System.out.println("创建连接池失败!请检查设置!!!");
  54.         }
  55.     }
  56. }

步骤四:添加获取连接的方法

在ConnectionSource类中添加获取连接的方法getConnection,代码如下所示:


    
    
  1. import java.io.IOException;
  2. import java.sql.Connection;
  3. import java.sql.SQLException;
  4. import java.util.Properties;
  5. import org.apache.commons.dbcp.BasicDataSource;
  6. public class ConnectionSource {
  7.     private static BasicDataSource dataSource = null;
  8.     public ConnectionSource() {
  9.     }
  10.     public static void init() {
  11.         Properties dbProps = new Properties();
  12.         // 取配置文件可以根据实际的不同修改
  13.         try {
  14.             dbProps.load(ConnectionSource.class.getClassLoader().getResourceAsStream(
  15.                     "day01/v4/db.properties"));
  16.         } catch (IOException e) {
  17.             e.printStackTrace();
  18.         }
  19.         try {
  20.             String driveClassName = dbProps.getProperty("jdbc.driverClassName");
  21.             String url = dbProps.getProperty("jdbc.url");
  22.             String username = dbProps.getProperty("jdbc.username");
  23.             String password = dbProps.getProperty("jdbc.password");
  24.             String initialSize = dbProps.getProperty("dataSource.initialSize");
  25.             String minIdle = dbProps.getProperty("dataSource.minIdle");
  26.             String maxIdle = dbProps.getProperty("dataSource.maxIdle");
  27.             String maxWait = dbProps.getProperty("dataSource.maxWait");
  28.             String maxActive = dbProps.getProperty("dataSource.maxActive");
  29.             dataSource = new BasicDataSource();
  30.             dataSource.setDriverClassName(driveClassName);
  31.             dataSource.setUrl(url);
  32.             dataSource.setUsername(username);
  33.             dataSource.setPassword(password);
  34.             // 初始化连接数
  35.             if (initialSize != null)
  36.                 dataSource.setInitialSize(Integer.parseInt(initialSize));
  37.             // 最小空闲连接
  38.             if (minIdle != null)
  39.                 dataSource.setMinIdle(Integer.parseInt(minIdle));
  40.             // 最大空闲连接
  41.             if (maxIdle != null)
  42.                 dataSource.setMaxIdle(Integer.parseInt(maxIdle));
  43.             // 超时回收时间(以毫秒为单位)
  44.             if (maxWait != null)
  45.                 dataSource.setMaxWait(Long.parseLong(maxWait));
  46.             // 最大连接数
  47.             if (maxActive != null) {
  48.                 if (!maxActive.trim().equals("0"))
  49.                     dataSource.setMaxActive(Integer.parseInt(maxActive));
  50.             }
  51.         } catch (Exception e) {
  52.             e.printStackTrace();
  53.             System.out.println("创建连接池失败!请检查设置!!!");
  54.         }
  55.     }
  56.     public static synchronized Connection getConnection() throws SQLException {
  57.         if (dataSource == null) {
  58.             init();
  59.         }
  60.         Connection conn = null;
  61.         if (dataSource != null) {
  62.             conn = dataSource.getConnection();
  63.         }
  64.         return conn;
  65.     }
  66. }

getConnection方法使用synchronized修饰,来保证线程安全。

步骤四:重构EmpDAO类

重构EmpDAO类,在该类中使用ConnectionSource类的getConnection方法获取连接,代码如下所示:


    
    
  1. import java.sql.Connection;
  2. import java.sql.ResultSet;
  3. import java.sql.SQLException;
  4. import java.sql.Statement;
  5. public class EmpDAO {
  6.     public static void main(String[] args) {
  7.         EmpDAO dao = new EmpDAO();
  8.         dao.findAll();
  9.     }
  10.     public void findAll() {
  11.         Connection con = null;
  12.         Statement stmt = null;
  13.         ResultSet rs = null;
  14.         try {
  15.             con = ConnectionSource.getConnection();
  16.             stmt = con.createStatement();
  17.             rs = stmt
  18.                     .executeQuery("select empno, ename, sal, hiredate from emp");
  19.             while (rs.next()) {
  20.                 System.out.println(rs.getInt("empno") + ","
  21.                         + rs.getString("ename") + "," + ","
  22.                         + rs.getDouble("sal") + "," + rs.getDate("hiredate"));
  23.             }
  24.         } catch (SQLException e) {
  25.             System.out.println("数据库访问异常!");
  26.             throw new RuntimeException(e);
  27.         } finally {
  28.             try {
  29.                 if (rs != null) {
  30.                     rs.close();
  31.                 }
  32.                 if (stmt != null) {
  33.                     stmt.close();
  34.                 }
  35.                 if (con != null) {
  36.                     con.close();
  37.                 }
  38.             } catch (SQLException e) {
  39.                 System.out.println("释放资源时发生异常");
  40.             }
  41.         }
  42.     }
  43. }

在此,调用Connection类的close方法关闭连接,会将该连接归还到连接池中。

运行类EmpDAO,输出结果与案例“JDBC实现对Emp数据的简单查询(Oracle)”是相同的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值