连接池
连接池的概述:
- 连接池是创建和管理数据库连接的缓冲吃技术。
- 连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的。
为什么要使用连接池:
- 因为频繁建立连接和销毁连接对象是很耗时间很耗资源的过程,为了提高连接对象的复用性,需要使用连接池来保证连接对象在执行完毕操作之后不被销毁,等待下一次复用。
连接池的核心思想:
- 连接复用。
连接池解决数据库资源浪费问题的原理:
连接对象 | 操作特点 |
创建时 | 在程序启动时找数据库服务器创建一定数量连接对象存放在集合中 |
使用时 | 直接从连接池对象中获得连接对象使用 |
关闭时 | 不是真正关闭连接对象,而是放回连接池中等待下一次复用。 |
自定义连接池步骤
1)定义一个类实现javax.sql.DataSource接口。
2)实现接口DataSource中的抽象方法。
3)定义连接池相关参数。
- 初始化连接个数。(空参构造方法构造)
- 最大的连接个数。
- 当前已经创建的连接个数。
4)创建容器保存连接。(例:LinkedList<Connection>)
5)提供获取连接方法。
5)提供关闭连接方法。(把连接放回集合,不是真正意义的关闭)
自定义连接存在的问题:
- connection.close()只能用于关闭连接,不能将连接还回连接池。
解放方案:
- 我们可以使用动态代理来增强Connection的close方法,变成将连接还回连接池。
改良后的自定义连接池示例代码:
public class MyPool implements DataSource {
//初始化池子链接个数
private int initCount = 3;
//最大链接个数
private int maxCount=10;
//当前已经创建的连接个数
private int curCount=0;
//定义一个容器用于存储链接
private LinkedList<Connection> list = new LinkedList<>();
//创建链接
public Connection createConnection() throws SQLException {
//被代理对象
Connection connection = JDBCUtils.getConnection();
//产生一个代理对象
Connection proxyConnection = (Connection) Proxy.newProxyInstance(MyPool.class.getClassLoader(),new Class[]{Connection.class},
(proxy,method,args)->{
//获取方法名
String methodName =method.getName();
//判断是否是我需要代理的方法
if("close".equalsIgnoreCase(methodName)){
//我需要代理,如果是调用了close方法,那么应该把当前的连接添加到连接池中.
list.add((Connection) proxy);
return null;
}else{
return method.invoke(connection,args);
}
});
curCount++;
return proxyConnection;
}
public MyPool() throws SQLException {
//一旦创建连接池的时候就会存在3个连接
for (int i = 0 ; i<initCount ; i++){
Connection connection = createConnection();
list.add(connection);
}
}
//获取连接
@Override
public Connection getConnection() throws SQLException {
//情况一: 池子中如果还有连接,那么直接获取即可。
if(list.size()>0){
Connection connection = list.removeFirst();
return connection;
}
//情况二: 当前创建的连接个数并没超出最大值
if(curCount<maxCount){
Connection connection = createConnection();
return connection;
}
throw new RuntimeException("当前网站访问的人数多过,请稍后!");
}
}
C3P0
C3P0连接池概述:
- 免费开源的,现在开源项目使用到c3p0的有:Spring和Hibernate。
c3p0的使用步骤:
1)拷贝c3p0的jar包到lib目录中,并且导入项目中。
2)导入c3p0-config.xml文件到src根目录下。(不能更换目录,不能修改该文件名)
3)配置数据库连接参数:连接字符串,用户名,密码,数据库驱动类。
4)创建数据库连接池对象。
5)调用连接池对象的方法获得连接对象使用。
6)调用连接对象的close方法关闭连接:也不是真正关闭而是返回连接池中。
常用的配置参数:
参数 | 说明 |
initialPoolSize | 初始连接数:刚创建好连接池的时候准备的连接数量 |
maxPoolSize | 最大连接数:连接池最大可以放多少个连接 |
checkoutTimeout | 最大等待时间:如果连接池中没有可用的连接对象时最长等待时间,如果在等待时间内仍然没有获得连接对象则抛出异常。 |
maxIdleTime | 最大空闲时间:当连接对象在指定的空闲时间内没有被使用,则会关闭该连接对象。 |
xml配置文件示例:
命名要求:一定要是c3p0-config.xml
位置要求:文件放在src文件夹下。
使用配置文件方式好处:
- 只需要单独修改配置文件,不用修改代码。
多个配置的好处:
- 可以连接不同的数据库:db1,db2.
- 可以使用不同的连接池参数:maxPoolSize
- 可以连接不同厂商的数据库:Oracle或MySQL
<?xml version="1.0" encoding="utf-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
<property name="user">root</property>
<property name="password">abc123</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<!--单位是毫秒-->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mysql</property>
<property name="user">root</property>
<property name="password">abc123</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<!--单位是毫秒-->
<property name="checkoutTimeout">3000</property>
</named-config>
</c3p0-config>
两种创建连接池的方式:
1)无参构造,使用默认配置。
public ComboPooledDataSource()
- 无参构造使用默认配置(使用xml中default-config标签中对应的参数)。
public ComboPooledDataSource(String configName)
- configName:xml配置的名称,使用xml中named-config标签中对应的参数。
DRUID
Druid概述:
- Druid是阿里巴巴开发的号称为监控而生的数据库连接池。
常用的配置参数:
参数 | 说明 |
jdbcUrl | 连接数据库的URL:mysql : jdbc:mysql://localhost:3306/数据库名 |
username | 数据库的用户名 |
password | 数据库的密码 |
driverClassName | 驱动类型,根据URL自动识别,可不配 |
initialSize | 初始化时建立物理链接的个数 |
maxActive | 最大连接数量 |
maxIdle | 最大空闲连接个数,配置也没有效果,默认为8 |
minIdle | 最小连接数量 |
maxWait | 获取连接时最大等待时间,单位毫秒 |
druid.properties配置文件示例代码:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/数据库名
username=root
password=abc123
initialSize=5
maxActive=10
maxWait=3000
maxIdle=8
minIdle=3
使用步骤
1)添加jar包到bin目录。并导入项目。
2)在src目录下创建一个properties文件,并设置对应参数。
3)创建Properties集合,并加载Properties文件的内容到Properties对象中。
1)得到druid.properties文件的输入流,使用类文件文件路径去找src目录下的资源最方便。
2)如果需要使用类文件路径,那么必须要先得到一个class对象,只有class对象才能使用类文件路径。
- 使用方法:getResourceAsStream( String path )
3)类文件路径我们一定需要用 / , / 代表scr目录。
- 示例: InputStream inputStream = JDBCUtils.class.getResourceAsStream("/durid.properties");
4)加载properties文件的内容到Properties对象中。
- 示例:properties.load(inputStream);
5)创建连接池
com.alibaba.druid.pool.DruidDataSourceFactory类有创建连接池的方法:
public static DataSource createDataSource(Properties properties)
- 创建一个连接池,连接池的参数使用properties中的数据。
6)用DataSource的getConnection()方法获取连接。
7)执行SQL语句。
8)关闭资源。
jdbc工具类
概述:
- 我们每次操作数据库都需要创建连接池,获取连接,关闭资源,都是重复的代码。我们可以将创建连接池的获取连接池的代码放到一个工具类中,简化代码。
示例代码:
package com.itheima.travel.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/*
1. 声明静态数据源成员变量
2. 创建连接池对象
3. 定义公有的得到数据源的方法
4. 定义得到连接对象的方法
5. 定义关闭资源的方法
*/
public class JdbcUtils {
// 1. 声明静态数据源成员变量
private static DataSource ds;
// 2. 创建连接池对象
static {
// 加载配置文件中的数据
InputStream is = JdbcUtils.class.getResourceAsStream("/druid.properties");
Properties pp = new Properties();
try {
pp.load(is);
// 创建连接池,使用配置文件中的参数
ds = DruidDataSourceFactory.createDataSource(pp);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 3. 定义公有的得到数据源的方法
public static DataSource getDataSource() {
return ds;
}
// 4. 定义得到连接对象的方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// 5.定义关闭资源的方法
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}
// 6.重载关闭方法
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}