Java DBCP数据库连接池技术的用法(附带实例)
在开发基于数据库的程序时,传统模式访问数据库基本按照这几个步骤进行:
这种开发模式存在一定的问题,因为 JDBC 的数据库连接对象使用 DriverManager 来获取,每次建立数据库连接时都要将连接对象加载到内存中,然后再验证用户名和密码,这个过程需要花费 0.05~1s 的时间,用完之后还需要把连接释放掉,非常消耗服务器资源。
若同时有几十万人甚至上百万人在线,那么频繁地创建数据库连接将占用很多的系统资源,严重时会造成服务器的崩溃。
为了解决传统开发中的数据库连接问题,可以采用数据库连接池技术。该技术负责分配、管理和释放数据库连接,它允许程序重复使用一个现有的数据库连接,而不是每次新建一个。
数据库连接池技术在初始化时会创建一定数量的数据库连接对象并放到连接池中,这些数据库连接的数量可以通过最小数据库连接数来设定。无论连接池中的连接是否被使用,连接池都将一直保证至少有一定数量的数据库连接。另外,连接池技术通过最大数据库连接数量来限定连接池能创建的最大连接数,当应用程序向连接池请求的连接数超过连接池限定的最大连接数时,这些请求将被加入到等待队列中,直到连接池中有空闲连接时,再分配给这些请求。
目前,使用比较普遍的两种开源的数据库连接池库组件是 DBCP 和 C3P0,本节详细讲解 DBCP 连接池的用法。
DBCP 数据库连接池是 Apache 软件基金组织下的开源连接池组件,该连接池组件依赖于该组织下的另一个开源系统 common-pool。所以,在使用 DBCP 连接池的时候,需要在程序中引用如下 jar 文件:
另外,使用 DBCP 连接池除了需要引入相关的 jar 文件外,还需要创建一个 properties 文件进行连接池参数的配置,具体的参数配置内容如下表所示。
接下来,通过案例来演示 DBCP 连接池的使用。
【实例】以前文创建的数据库 school 为例,使用 DBCP 连接池实现对数据库的访问。在该例中需要创建 DBCP 连接池配置文件 dbcp.properties 和代码文件 Demo1416.java。
1) 在项目 src 目录下新建包 com.biancheng.c,并在该包下创建 DBCP 连接池配置文件 dbcp.properties,代码如下:
2) 在包 com.biancheng.c 下创建代码文件 Demo.java,实现通过 DBCP 连接池访问数据库,代码如下:
创建了一个测试类,在测试类的 main() 方法中,先通过文件输入流的方式,将 properties 文件加载到 Properties 对象中,从而获取了 properties 文件中配置的 DBCP 连接池的所有参数。
使用 DBCP 连接池组件中的工厂类 BasicDataSourceFactory 创建连接池数据源对象 dataSource。然后,使用 dataSource 从连接池中获取一个 Connection 连接对象。通过连接对象创建 PreparedStatement 预编译命令执行对象,并绑定插入的 SQL 语句。接着,使用 PreparedStatement 对象执行插入操作,向数据库插入记录,并将插入的记录数打印输出。
之后,再次通过连接对象创建一个新的 PreparedStatement 对象,并绑定查询的 SQL 语句。接着,通过 PreparedStatement 对象执行查询操作,并返回一个 ResultSet 结果集对象。
然后,使用结果集对象通过循环的方式将查询到的数据逐条输出。最后,依次关闭数据库对象,需要注意的是,调用连接对象的 close() 方法时,连接并没有被销毁,而是被放回到连接池中,下次需要使用时可以直接使用。
- 创建数据库连接;
- 进行SQL操作;
- 断开数据库连接。
这种开发模式存在一定的问题,因为 JDBC 的数据库连接对象使用 DriverManager 来获取,每次建立数据库连接时都要将连接对象加载到内存中,然后再验证用户名和密码,这个过程需要花费 0.05~1s 的时间,用完之后还需要把连接释放掉,非常消耗服务器资源。
若同时有几十万人甚至上百万人在线,那么频繁地创建数据库连接将占用很多的系统资源,严重时会造成服务器的崩溃。
为了解决传统开发中的数据库连接问题,可以采用数据库连接池技术。该技术负责分配、管理和释放数据库连接,它允许程序重复使用一个现有的数据库连接,而不是每次新建一个。
数据库连接池技术在初始化时会创建一定数量的数据库连接对象并放到连接池中,这些数据库连接的数量可以通过最小数据库连接数来设定。无论连接池中的连接是否被使用,连接池都将一直保证至少有一定数量的数据库连接。另外,连接池技术通过最大数据库连接数量来限定连接池能创建的最大连接数,当应用程序向连接池请求的连接数超过连接池限定的最大连接数时,这些请求将被加入到等待队列中,直到连接池中有空闲连接时,再分配给这些请求。
目前,使用比较普遍的两种开源的数据库连接池库组件是 DBCP 和 C3P0,本节详细讲解 DBCP 连接池的用法。
DBCP 数据库连接池是 Apache 软件基金组织下的开源连接池组件,该连接池组件依赖于该组织下的另一个开源系统 common-pool。所以,在使用 DBCP 连接池的时候,需要在程序中引用如下 jar 文件:
- commons-dbcp2-2.7.0.jar(连接池实现库);
- commons-pool2-2.7.0.jar(连接池依赖库);
- commons-logging-1.2.jar(日志工具库)。
另外,使用 DBCP 连接池除了需要引入相关的 jar 文件外,还需要创建一个 properties 文件进行连接池参数的配置,具体的参数配置内容如下表所示。
参数 | 参数描述 |
---|---|
maxActive | 连接池支持的最大连接数,最多能支持的连接数 |
maxIdle | 连接池中最多可空闲的连接数量,其余空闲的会被释放,来保证性能 |
minIdle | 释放连接时,最少保留的空闲的连接数量 |
initialSize | 数据库初始化时,创建的连接数量 |
maxWait | 连接池中连接用完时,新请求等待的时间,毫秒 |
driverClassName | JDBC数据库驱动类的类名 |
url | 连接路径 |
username | 数据库用户名 |
password | 数据库密码 |
connectionProperties | 连接参数 |
defaultAutoCommit | 设置事务的提交状态,默认为true |
接下来,通过案例来演示 DBCP 连接池的使用。
【实例】以前文创建的数据库 school 为例,使用 DBCP 连接池实现对数据库的访问。在该例中需要创建 DBCP 连接池配置文件 dbcp.properties 和代码文件 Demo1416.java。
1) 在项目 src 目录下新建包 com.biancheng.c,并在该包下创建 DBCP 连接池配置文件 dbcp.properties,代码如下:
# 数据库驱动类 driverClassName=com.mysql.jdbc.Driver # 数据库连接地址 url=jdbc:mysql://localhost:3306/school # 数据库服务器用户名 username=root # 数据库服务器密码 password=root # 连接池初始连接数 initialSize=10 # 连接池最大连接数 maxActive=50 # 连接池最大空闲连接数 maxIdle=20 # 连接池最小空闲连接数 minIdle=5 # 连接池最大等待时间(毫秒) maxWait=60000 # 数据库连接参数 connectionProperties=useUnicode=true&characterEncoding=utf-8&useSSL=false # 连接池事务提交状态 defaultAutoCommit=true
2) 在包 com.biancheng.c 下创建代码文件 Demo.java,实现通过 DBCP 连接池访问数据库,代码如下:
package com.biancheng.c; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSourceFactory; // DBCP连接池测试类 public class Demo1416 { public static void main(String[] args) throws Exception { // 加载dbcp.properties配置文件,返回输入流对象 InputStream is = DBCPTest.class.getResourceAsStream("/com/aaa/p1405/dbcp.properties"); // 创建Properties对象 Properties p = new Properties(); // 将properties文件中的数据加载到Properties对象中 p.load(is); // 创建DBCP连接池数据源对象 DataSource ds = BasicDataSourceFactory.createDataSource(p); // 从连接池中获取数据库连接 Connection con = ds.getConnection(); // 定义插入的SQL语句 String sql = "insert into students " + " (name, gender, age) " + " values" + " ('张三','男',20)"; // 创建预编译命令执行对象,绑定插入SQL语句 PreparedStatement pst = con.prepareStatement(sql); // 执行插入操作 int count = pst.executeUpdate(); // 输出插入的记录数 System.out.println("插入的记录数是:" + count); // 定义查询的SQL语句 sql = "select id, name, gender, age from students "; // 创建预编译命令执行对象,绑定查询SQL语句 pst = con.prepareStatement(sql); // 执行查询并返回结果集对象 ResultSet rs = pst.executeQuery(); System.out.println("查询的结果是:"); // 通过结果集对象循环遍历查询的表数据 while (rs.next()) { System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getString(3) + " " + rs.getInt(4)); } // 关闭数据库对象 if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); // 将连接放回连接池 } } }程序的运行结果如下:
插入的记录数是: 1
查询的结果是:
1 张三 男 20
创建了一个测试类,在测试类的 main() 方法中,先通过文件输入流的方式,将 properties 文件加载到 Properties 对象中,从而获取了 properties 文件中配置的 DBCP 连接池的所有参数。
使用 DBCP 连接池组件中的工厂类 BasicDataSourceFactory 创建连接池数据源对象 dataSource。然后,使用 dataSource 从连接池中获取一个 Connection 连接对象。通过连接对象创建 PreparedStatement 预编译命令执行对象,并绑定插入的 SQL 语句。接着,使用 PreparedStatement 对象执行插入操作,向数据库插入记录,并将插入的记录数打印输出。
之后,再次通过连接对象创建一个新的 PreparedStatement 对象,并绑定查询的 SQL 语句。接着,通过 PreparedStatement 对象执行查询操作,并返回一个 ResultSet 结果集对象。
然后,使用结果集对象通过循环的方式将查询到的数据逐条输出。最后,依次关闭数据库对象,需要注意的是,调用连接对象的 close() 方法时,连接并没有被销毁,而是被放回到连接池中,下次需要使用时可以直接使用。