连接池
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。
为什么要使用连接池
普通的JDBC请求MySQL连接,当完成所有SQL请求任务之后,MySQL连接就会关闭。这样一来MySQL的连接就变成了单次使用,每次使用前都需要重新创建,使用之后都需要将连接关闭。如果此时有多个用户使用JDBC的话,就会出现连接频繁的创建和关闭的过程。而连接池的出现就是为了让连接能够重复使用。
连接池的工作原理
数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。当用户第一次获取连接时,会先请求一定数量(初始连接数量)的连接存放在连接池中,并且会记录每个连接的初始(未使用)时间,然后再将连接池中的连接给有需要的用户。当缓冲池中初始连接用完了,链接池会判断所创建的连接数量是否大于最大连接数,如果不大于则可以继续创建连接给请求的用户使用(创建的新连接的个数可自己控制,链接池中有相应的增长参数);如果已经到到最大连接数则当前用户需要等待,直到有用户使用完之后关闭连接,这个用户才可以重复使用。当我们开始使用连接池的时候连接池内部就会进行计时(超时时间),每隔空闲连接的存活时间就会对连接池内部的连接进行一次清理。把所有(当前时间 - 链接最后使用的时间 )> 空闲连接的存活时间的连接释放掉。
连接池的使用流程
在此处我使用的是maven
所快速搭建的项目。且采用的是c3p0连接池
。
1、导入依赖
当 A jar 包用到了 B jar 包中的某些类时,A 就对 B 产生了依赖。在项目中以依赖的方式引入一个我们需要的 jar 包,只需要使用 dependency
标签指定被依赖 jar 包的坐标就可以了。如果我们将maven
的镜像配置文件更改为了阿里云,那么我们就可以直接去阿里仓库寻找我们所需要的依赖。
2、参数配置
//参数配置
private final String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
private final String useName;
private final String passWord;
private final String jdbcDriver = "com.mysql.jdbc.Driver"; //jdbc驱动
private final int initSize = 5; //初始连接数量
private final int maxSize = 10; //最大连接数量
private final int minSize = 2; //最小连接数量
private final int acq = 2; //增长因子
private final int idle = 60; //单位秒 空闲时间
//参数配置及驱动加载
public PoolService(){
dataSource.setJdbcUrl(url);
dataSource.setUser(useName);
dataSource.setPassword(passWord);
try {
dataSource.setDriverClass(jdbcDriver); //jdbc驱动
} catch (PropertyVetoException e) {
e.printStackTrace();
}
dataSource.setInitialPoolSize(initSize); //初始连接数量
dataSource.setMaxPoolSize(maxSize); //最大连接数量
dataSource.setMinPoolSize(minSize); //最小连接数量
dataSource.setAcquireIncrement(acq); //增长因子
dataSource.setMaxIdleTime(idle); //单位秒 空闲时间
}
常用配置参数介绍
最基础的参数配置:
- driverClass : 数据库驱动。(比如mysql,或者oracle数据库的驱动)
- jdbcUrl: 数据库连接地址。(例如jdbc:mysql://127.0.0.1:3306/test)
- useName:数据库用户名。
- passWord:和数据库用户名对应的数据库密码。
基础的参数配置:
参数 | 默认值 | 解释 |
---|---|---|
initialPoolSize | 3 | 连接池初始化时创建的连接数(介于maxPoolSize和minPoolSize之间) |
maxPoolSize | 15 | 连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大 |
minPoolSize | 3 | 连接池保持地最小连接数,后面地maxIdleTimeExcessConnections跟这个配合使用来减轻连接池地负载 |
acquireIncrement | 3 | 连接池在无空闲连接可用时一次性创建的新数据库连接数 |
管理连接池大小和连接时间的配置:
参数 | 默认值 | 解释 |
---|---|---|
maxIdleTime | 0 | 连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开这个连接,如果值为0,则永远不会断开连接 |
maxConnectorAge | 0 | 连接的最大绝对年龄,单位是秒,0表示绝对年龄无限大 |
maxIdleTimeExcessConnection | 0 | 单位秒,为了减轻连接池的负载,当连接池经过数据访问高峰创建了很多连接,但是后面连接池不需要维护这么多的连接,必须小于maxIdleTime。配置不为0,则将连接池的数量保持到minPoolSize |
用于连接测试的配置:
参数 | 默认值 | 解释 |
---|---|---|
automaticTestTable | null | 如果不为null,c3p0将生成指定名称的空表,并使用该表来测试连接 |
connectionTesterClassName | com.mchange.v2.c3p0.impl.DefaultConnectionTester | 通过实现ConnectionTester或QueryConnectionTester的类来测试连接,类名需要指定全路径 |
idleConnectionTestPeriod | 0 | 每隔几秒检查所有连接池中的空闲连接 |
preferredTestQuery | null | 定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个可以显著提高测试速度。注意:测试的表必须在初始数据源的时候就存在 |
testConnectionOnCheckin | false | 如果为true,那么将在取得连接的同时校验连接的有效性 |
testConnectionOnCheckout | false | 如果为true,那么将在连接释放的同时校验连接的有效性 |
- 在这几个参数中,idleConnectionTestPeriod、testConnectionOnCheckout和testConnectuonOnCheckin控制什么时候连接将被校验检测。
- automaticTestTable、connectionTesterClassName和perferedTestQuery控制连接将怎么样被检测。
关于SQL语句操作对象的配置:
参数 | 默认值 | 解释 |
---|---|---|
maxStatements | 0 | JDBC的标准参数,用以控制数据源中加载的PreparedStatement的数量 |
maxStatementsPerConnection | 0 | maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存的PreparedStatement数 |
statementCacheNumDeferredCloseThreads | 0 | 如果大于0,则语句池会延迟物理close()缓存语句直到其父连接未被任何客户端使用,或者在其内部(例如在测试中)由池本身使用 |
关于数据库中断恢复的配置:
参数 | 默认值 | 解释 |
---|---|---|
acquireRetryAttempts | 30 | 连接池在获得新连接失败时重试的次数,如果小于等于0则无限重试直至连接获得成功 |
acquireRetryDelay | 1000 | 连接池在获得新连接时的间隔时间,单位毫秒 |
breakAfterAcquireFailure | false | 获取连接失败将会引起所有等待连接池获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设置为true,那么在尝试获取连接失败后该数据源将声明已断开并永久关闭 |
关于未解决的事务处理的配置:
参数 | 默认值 | 解释 |
---|---|---|
autoCommitOnClose | false | 连接池在回收数据库连接时是否自动提交事务。如果为false,则会回滚未提交的事务,如果为true,则会自动提交事务 |
forceIgnoreUnresolvedTransactions | false | 这个配置强烈不建议为true。一般来说事务当然由自己关闭了,为什么要让连接池来处理这种不细心问题呢? |
其他数据源配置:
参数 | 默认值 | 解释 |
---|---|---|
checkoutTimeout | 0 | 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如果设置为0,则将无限期等待,单位为毫秒 |
factoryClassLocation | 0 | 指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可 |
numHelperThreads | 3 | 异步操作,提升性能通过多线程实现多个操作同时被执行 |
3、获取连接
//创建c3p0连接池的数据对象
private final ComboPooledDataSource dataSource = new ComboPooledDataSource();
//给用户提供获取连接的接口
public Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection(); //核心代码
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
4、创建发送SQL请求的对象,进行相关操作
public static void main(String[] args) {
PoolService service = new PoolService();
//从连接池获取连接
Connection connection = service.getConnection();
String sql = "select * from jdbc_test";
PreparedStatement pre = null;
try {
pre = connection.prepareStatement(sql);
ResultSet resultSet = pre.executeQuery();
while (resultSet.next()){
System.out.println("id: "+resultSet.getString(1)
+ " name: "+resultSet.getString(2));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
pre.close();
connection.close(); //并不是真实的关闭连接,只是将连接返回到连接池当中
} catch (SQLException e) {
e.printStackTrace();
}
}
}
详细操作请见JDBC编程。
5、关闭连接
connection.close(); //并不是真实的关闭连接,只是将连接返回到连接池当中
这里的关闭连接只是关闭了单一用户使用JDBC的连接,此时连接将会返回到连接池中等待下一个用户的使用。如果等待时间超过所设置的空闲时间,则此连接才会被真正的关闭。