数据库连接池
文章目录
1. 概述
####1.1 连接对象的使用问题
-
数据库创建连接通常需要消耗相对较多的资源,创建时间也较长,而每次操作都要重新获取新的连接对象,执行一次操作就把连接关闭,这样数据库连接对象的使用率低。
-
假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出
1.2 连接池解决问题的原理
连接对象 | 操作特点 |
---|---|
创建时 | 由其它程序在服务器启动的时候创建一定数量的连接对象放在服务器的内存中,这个内存区域称为连接池 |
使用时 | 不由自己来创建连接,而是直接从连接池中得到一个空闲的连接对象返回即可 |
关闭时 | 不是真正的关闭连接对象,而是将连接对象放回到连接池中,供下一个用户使用。 |
1.3 数据库连接池接口
javax.sql.DataSource
连接池接口,所有连接池必须实现这个接口。由第三方厂商实现
方法: Connection getConnection()
从连接池获得一个连接对象。
常用参数 | 描述 |
---|---|
初始连接数 | 连接池在创建的时候一开始创建多少个连接对象 |
最大连接数 | 连接池中最多可以创建多少个连接 |
最长等待时间 | 如果当前连接池中没有可用的连接,等待多长抛出异常,单位是毫秒。 |
最长空闲回收时间 | 如果连接池中某个连接对象长时间没有使用,则过多久服务器进行回收。这个功能不是所有的连接池都有。 |
在默认的情况下,连接池中所有的配置参数都有默认值
2. 常见的连接池
2.1 介绍
- 阿里巴巴-德鲁伊druid连接池:Druid是阿里巴巴开源平台上的一个项目
- DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。
- C3P0是一个开源的JDBC连接池,目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能。
2.2 C3P0使用
步驟
- 导包
- c3p0-config.xml配置文件:(放在src文件夹下)
<?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/day19</property>
<!--用户名-->
<property name="user">root</property>
<!--密码-->
<property name="password">root</property>
<!--与连接池相关的配置-->
<!--初始连接数-->
<property name="initialPoolSize">5</property>
<!--最大连接数-->
<property name="maxPoolSize">10</property>
<!--等待多久以后超时-->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="javaee">
<!--数据库连接配置-->
<!--驱动-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!--连接字符串-->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day18</property>
<!--用户名-->
<property name="user">root</property>
<!--密码-->
<property name="password">root</property>
<!--与连接池相关的配置-->
<!--初始连接数-->
<property name="initialPoolSize">5</property>
<!--最大连接数-->
<property name="maxPoolSize">15</property>
<!--等待多久以后超时-->
<property name="checkoutTimeout">2000</property>
</named-config>
</c3p0-config>
构造方法 | 描述 |
---|---|
ComboPooledDataSource() | 使用默认配置 |
ComboPooledDataSource(命名配置) | 带参数,参数是:命名配置的名字 |
- java代码
package com.zmysna;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 使用c3p0连接池
*/
public class Demo1C3P0 {
//从连接池中得到10个连接对象,输出每个对象
public static void main(String[] args) throws SQLException {
//创建数据源对象,使用默认的配置
ComboPooledDataSource ds = new ComboPooledDataSource();
//创建数据源对象,使用命名配置
ComboPooledDataSource ds2 = new ComboPooledDataSource("javaee");
for (int i = 1; i <=11 ; i++) {
Connection connection = ds.getConnection();
System.out.println("第" + i + "个连接:" + connection);
if (i==5) {
connection.close(); //释放连接到连接池
}
}
}
}
2.4 Druid使用
步骤
-
导包
-
配置文件
# 与数据库连接有关的配置
url=jdbc:mysql://localhost:3306/day19
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
# 连接池有关的配置
# 初始连接数
initialSize=5
# 最大连接数
maxActive=10
# 最长等待时间
maxWait=2000
- java代码
package com.zmysna;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* 从连接池中得到连接对象,输出得到的连接对象
*/
public class Demo1Druid {
public static void main(String[] args) throws Exception {
//创建一个属性对象
Properties info = new Properties();
//从类路径下加载一个输入流, /表示类路径根目录
InputStream in = Demo1Druid.class.getResourceAsStream("/druid.properties");
//从文件中读取所有配置
info.load(in);
//通过工厂类创建一个数据源
DataSource ds = DruidDataSourceFactory.createDataSource(info);
//得到10个连接对象
for (int i = 1; i <=11 ; i++) {
//从数据源中得到连接对象
Connection connection = ds.getConnection();
System.out.println("第"+ i + "个连接:" + connection);
if (i==7) {
connection.close(); //释放连接
}
}
}
}
3. Druid连接池工具类
3.1 工具类实现
为了简化Druid连接池的使用,创建一个工具类,用于获得连接池和关闭资源。
package com.zmysna.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DruidUtils {
private static DataSource ds;
//加载配置文件,并创建连接池
static {
Properties pro = new Properties();
try {
pro.load(DruidUtils.class.getResourceAsStream("/druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//从连接池中取得一个连接对象
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//获得连接池
public static DataSource getDataSource() {
return ds;
}
//关闭资源
public static void close(Connection conn, Statement statement, ResultSet rs) {
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, Statement statement) {
close(conn, statement,null);
}
}
3.2 工具类使用
package com.zmysna.test;
import com.zmysna.utils.DruidUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Test02 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps = null;
try {
//从连接池取出一个连接对象
connection = DruidUtils.getConnection();
ps = connection.prepareStatement("create table user (id int primary key auto_increment, username varchar(30), gender char(1), addr varchar(50))");
ps.execute();
PreparedStatement ps2 = connection.prepareStatement("insert into user values(?, ?, ?, ?)");
ps2.setInt(1,1);
ps2.setString(2,"孙悟空");
ps2.setString(3,"男");
ps2.setString(4,"地球");
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
DruidUtils.close(connection,ps);
}
}
}
4. 自定义连接池
4.1 步骤
-
编写一个接口:DataSource,包含getConnection()抽象方法
-
在配置文件中定义与连接池相关的参数,如:初始连接池,最大连接数,当前连接数
-
编写实现类MyDataSource实现DataSource接口,实现接口中的方法
-
创建集合LinkedList用于保存创建好的连接对象,即连接池。
-
提供获取连接的公共方法
-
提供关闭连接的公共方法
4.2 实现
DataSource接口
public interface DataSource {
Connection getConnection()
}
配置文件
#数据库连接的参数
url=jdbc:mysql://localhost:3306/day19
name=root
password=root
实现类MyDataSource
package com.zmysna.jdbc;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;
public class MyDataSource implements DataSource{
private static LinkedList<Connection> pool;
private int initPoolSize = 5; //初始化连接数,默认为5
private int maxPoolSize = 10; //最大连接数,默认为10
private int liveTimeOut = 4000; //等待时间,睡4秒,没有实现最长等待时间,有点复杂
private int currSize = 0; //当前连接数
private static String url;
private static String name;
private static String password;
public MyDataSource() {
initPool();
}
public MyDataSource(int initPoolSize, int maxPoolSize, int liveTimeOut) {
this.initPoolSize = initPoolSize;
this.maxPoolSize = maxPoolSize;
this.liveTimeOut = liveTimeOut;
initPool();
}
private void initPool() {
pool = new LinkedList<>();
for(int i = 0; i < initPoolSize; i++){
Connection connection = createConnection();
if(connection != null){
currSize++;
pool.addLast(connection);
}
}
}
/**
创建一个连接
*/
private Connection createConnection() {
Connection proxy = null;
try {
//读取配置文件
Properties properties = new Properties();
properties.load(MyDataSource.class.getResourceAsStream("/dataSource.properties"));
String name = properties.getProperty("name");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
//从DriveManager获取一个连接并进行动态代理,重写close()方法
Connection connection = DriverManager.getConnection(url,name,password);
proxy = (Connection) Proxy.newProxyInstance(connection.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("close")) {
pool.addLast((Connection) proxy);
return null;
} else return method.invoke(connection, args);
}
});
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return proxy;
}
/**
获取一个连接
*/
@Override
public Connection getConnection() throws SQLException {
if(pool.size() > 0){
return pool.removeFirst();
}
else if(currSize < maxPoolSize){
Connection connection = createConnection();
if(connection != null) {
currSize++;
}
return connection;
}else {
try {
Thread.sleep(liveTimeOut);
if(pool.size() > 0) return pool.removeFirst();
throw new SQLException("超过最大连接数: " + maxPoolSize);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
}