数据库连接池
我们知道程序在对数据库中的数据进行操作时,需要先和数据库建立连接,传统的程序对数据库操作的过程中,尤其是在web程序中,一次web请求就要建立一个连接,请求完毕就关闭连接,建立好连接后,往往也只是执行一条SQL语句或者执行一次事务,而“数据库连接”是一种稀缺的资源,频繁的建立连接,就会造成资源的浪费。
数据库连接池的出现就极大的解决了这个问题,它将多个可用的数据库连接存储起来进行有效的管理,预先在缓冲池中放入一定数量的连接,当需要数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去,也就是说它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。
DBCP
简介
DBCP是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,而且它是tomcat内置的连接池。
用法
1.导入相关Jar包
新建完项目后,导入相关Jar包,这里使用的MySql,要包括mysql的驱动包
commons-dbcp-1.4.jar DBCP核心Jar包
mysql-connector-java-5.1.45-bin.jar MySql连接核心包
commons-pool-1.5.6.jar 配置数据池连接用的,DBCP依赖Jar包
2.创建BasicDataSource数据库连接池对象
1
| BasicDataSource dataSource = new BasicDataSource();
|
3.配置数据库连接池对象
DBCP配置参数:
username |
\ |
传递给JDBC驱动的用于建立连接的用户名 |
password |
\ |
传递给JDBC驱动的用于建立连接的密码 |
url |
\ |
传递给JDBC驱动的用于建立连接的URL |
driverClassName |
\ |
使用的JDBC驱动的完整有效的Java 类名 |
initialSize |
0 |
初始化连接:连接池启动时创建的初始化连接数量,1.2版本后支持 |
maxActive |
8 |
最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 活动连接超出了这个数量就等待,如果设置为非正数则表示不限制。 |
maxIdle |
8 |
最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制 |
minIdle |
0 |
最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建 |
maxWait |
无限 |
最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数)超过时间则抛出异常,如果设置为-1表示无限等待 |
testOnReturn |
false |
是否在归还到池中前进行检验 |
testWhileIdle |
false |
连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.设置为true后如果要生效,validationQuery参数必须设置为非空字符串 |
minEvictableIdleTimeMillis |
1000 *60 *30 |
连接在池中保持空闲而不被空闲连接回收器线程(如果有)回收的最小时间值,单位毫秒 |
numTestsPerEvictionRun |
3 |
在每次空闲连接回收器线程(如果有)运行时检查的连接数量 |
timeBetweenEvictionRunsMillis |
-1 |
在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位.如果设置为非正数,则不运行空闲连接回收器线程 |
validationQuery |
null |
SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录 |
testOnBorrow |
true |
是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个. |
maxTotal |
8 |
可以在这个池中同时被分配的有效连接数的最大值,如设置为负数,则不限制 |
1.通过配置参数的Set方法对连接池的参数进行设置。
1 2
| dataSource.setUsername("root"); dataSource.setPassword("root");
|
2.使用配置文件对连接池的参数进行设置
使用配置文件来设置连接池中连接的用户名和密码:
database.properties
1 2
| username=root password=root
|
在初始化连接时,读取配置文件的配置项,然后设置连接池参数,在后面的第三个案例中,详细介绍了如何使用DBCP提供的类来读取配置文件设置数据库连接池。
注:一般将DBCP相关的类都设置为工具类,BasicDataSource设置为全局私有静态变量,然后将配置数据库连接池对象的部分放到一个静态代码块中。如果涉及多线程一定要设置成静态的,不然每次获取连接时都会创建一个连接池,连接池过多就会导致程序崩溃。
4.获取连接并使用连接
使用数据库连接池对象的getConnection()方法来获取一个数据库连接。
1
| Connection conn = dataSource.getConnection();
|
5.将连接放回数据库池中
Connection用完了是需要关闭,无论是否使用连接池。如果没使用连接池那么Connection关闭是真正的关闭数据库连接,使用连接池的话Connection关闭实际上是将Connection放回到连接池而非真正关闭连接。使用连接池的目的就是防止频繁创建关闭Connection,连接池会对Connection复用,就是多次重复使用,也就是说从连接池获取的连接关闭并不是真正的关闭,而是将其放回数据库池中。
DataSource在使用完后也是需要关闭的,通常情况是在程序停止前关闭。大部分项目在代码中没有手动关闭DataSource是因为关闭DataSource的动作一般是某些框架帮你做好了。
案例
1.JDBC获取DBCP连接池中的连接,并进行数据库操作
调用数据库连接池对象的getConnection()方法,获取连接池并使用,注意在用完后要连接对象的调用close()方法将数据库连接还给数据库连接池。
案例一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.imhowie;
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;
import org.apache.commons.dbcp.BasicDataSource;
public class DBCPDemo1 { public static void main(String[] args) { BasicDataSource dataSource = PoolTools.getDataSource(); try { Connection conn = dataSource.getConnection(); String sql = "select * from sort where sid = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, "1"); ResultSet rs = ps.executeQuery(); while (rs.next()) { System.out.println( rs.getString(1) + ":" + rs.getString(2) + ":" + rs.getString(3) + ":" + rs.getString(4)); } conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package com.imhowie;
import org.apache.commons.dbcp.BasicDataSource;
public class PoolTools {
private static BasicDataSource dataSource = new BasicDataSource();
static { dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/jdbc"); dataSource.setUsername("root"); dataSource.setPassword("howie1996"); dataSource.setInitialSize(10); dataSource.setMaxActive(8); dataSource.setMaxIdle(5); dataSource.setMinIdle(1); }
public static BasicDataSource getDataSource() { return dataSource; } }
|
2.DbUtils和DBCP整合使用
1.整合使用的方法就是在创建DbUtils的QueryRunner对象时,为其构造方法传入一个数据库连接池对象。
例: 1 2
| BasicDataSource dataSource = PoolTools.getDataSource(); QueryRunner queryRunner = new QueryRunner(dataSource);
|
2.之后QueryRunner在进行数据库操作时,原先一些需要传入数据库连接的方法,这时候就可以将这个数据库连接参数直接省去。
例: 1
| List<Object[]> query = queryRunner.query(sql, new ArrayListHandler());
|
上例中原先第一个参数是数据库连接对象,这时候就直接省去了,因为在创建QueryRunner已经为其设置了数据库连接池。
3.数据库连接不用手动关闭,将连接还回数据池的操作,QueryRunner已经在内部实现了
案例二
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.imhowie;
import java.sql.SQLException; import java.util.List;
import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.ArrayListHandler;
public class DBCPDemo2 { public static void main(String[] args) { BasicDataSource dataSource = PoolTools.getDataSource(); QueryRunner queryRunner = new QueryRunner(dataSource); String sql = "select * from sort"; try { List<Object[]> query = queryRunner.query(sql, new ArrayListHandler()); for (Object[] objects : query) { for (Object object : objects) { System.out.print(object + "::"); } System.out.println(); } } catch (SQLException e) { e.printStackTrace(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package com.imhowie;
import org.apache.commons.dbcp.BasicDataSource;
public class PoolTools {
private static BasicDataSource dataSource = new BasicDataSource();
static { dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/jdbc"); dataSource.setUsername("root"); dataSource.setPassword("howie1996"); dataSource.setInitialSize(10); dataSource.setMaxActive(8); dataSource.setMaxIdle(5); dataSource.setMinIdle(1);
}
public static BasicDataSource getDataSource() { return dataSource; } }
|
3.DBCP读取配置文件中的配置,创建线程池并与DbUtils搭配使用
在使用DBCP时可以从配置文件中读取相应的数据库连接池配置,将配置和程序分开。
DBCP提供了一个BasicDataSourceFactory工程类,它可以通过createDataSource方法来接受一个Properties对象,然后直接对数据库池进行设置,但是配置文件中的配置参数名称要和DBCP规定的配置参数名称一致。上面已经介绍了常用的配置参数,这里就不再赘述。
createDataSource返回的是javax.sql包下的DataSource接口,BasicDataSource是org.apache.commons.dbcp包下的,但是BasicDataSource是DataSource的实现类,所以如果用的是BasicDataSource就需要强转一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private static Properties properties = new Properties(); private static DataSource dataSource; try { FileInputStream is = new FileInputStream("config/database.properties"); properties.load(is); } catch (IOException e) { e.printStackTrace(); }
try { dataSource = BasicDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); }
|
案例三
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.imhowie;
import java.sql.SQLException; import java.util.List;
import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.ArrayListHandler;
public class DBCPDemo3 { public static void main(String[] args) { BasicDataSource dataSource = PoolTools.getDataSource(); QueryRunner queryRunner = new QueryRunner(dataSource); String sql = "select * from sort"; try { List<Object[]> query = queryRunner.query(sql, new ArrayListHandler()); for (Object[] objects : query) { for (Object object : objects) { System.out.print(object + "::"); } System.out.println(); } } catch (SQLException e) { e.printStackTrace(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| package com.imhowie;
import java.io.FileInputStream; import java.io.IOException; import java.util.Properties;
import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSourceFactory;
public class PoolTools2 {
private static BasicDataSource dataSource; private static Properties properties; static { properties = new Properties(); try { properties.load(new FileInputStream("config/database.properties")); dataSource = (BasicDataSource) BasicDataSourceFactory.createDataSource(properties); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }
public static BasicDataSource getDataSource() { return dataSource; }
public static void main(String[] args) { BasicDataSource dataSource2 = getDataSource(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/jdbc
username=root
password=howie1996
initialSize=30
maxTotal=30
maxIdle=10
minIdle=5
maxWaitMillis=1000
|