1、什么是数据库连接池?
这个是一项池化技术,是为了提高数据库连接的使用率的一项技术。我们现在使用JDBC的时候,每次都需要和数据库新建立一个新的连接,使用完了就把这个连接关闭了,那么这样其实是很不环保的,也不利于提高程序的效率,所以就有了数据库连接池。
2、自己实现一个数据库连接池
/**
* 创建自己的数据库连接池
*
* 带有扩容的规则 自己定义的扩容的规则
*/
public class MyConnectionPool {
// 使用链表结构的list,规定了这个集合我们从头部添加,从尾部获取
static LinkedList<Connection> linkedList;
// 初始化容量大小
static int INIT_SIZE = 10;
// 需要扩容的大小
static int MIN_SIZE = 5;
// 每次扩容增加的数量
static int INCREMENT = 10;
static {
linkedList = new LinkedList<>();
// 往这个集合里面去初始化放入一些连接
addCapacity(INIT_SIZE);
}
private static void addCapacity(int size) {
for (int i = 0; i < size; i++) {
Connection connection = JDBCUtils.getConnection();
linkedList.addFirst(connection);
}
}
// 获取连接
public static Connection getConnection(){
// 如果连接池里面的连接小于临界值了,那么我们应该去给他扩容
if (linkedList.size() < MIN_SIZE) {
addCapacity(INCREMENT);
}
Connection connection = linkedList.removeLast();
return connection;
}
// 返回连接
public static void returnConnection(Connection connection){
linkedList.addFirst(connection);
}
}
目前为止,我们的数据库连接池还存在以下几个问题:
- 连接池里面没有一个连接数的上限
- 缺少了一套连接自动回收的机制
- 我们自己实现的数据库连接池没有遵循JDBC给我们提供的数据库连接池的标准
其实,JDBC给我们提供了一个数据库连接池的一个标准接口,这个接口里面定义了获取连接的方法,所以我们自己实现的连接池需要实现这个接口javax.sql.Datasource
我们发现,Datasource这个接口里面,并没有给我们定义一个 返回连接的方法,为什么没有定义呢?
是因为即使定义了返回连接的方法,也不能阻止用户先把连接关闭,然后再把连接返回给连接池。假如用户这么做了以后,我们的整个数据库连接池就废了,那么怎么样去阻止用户调用 connection.close()
方法呢?
我们作为工具的设计者,没有办法阻止用户去调用connection.close()
这个方法,但是我们可以去想一些别的办法。
-
我们的连接池里面放入的是JDBC4Connection 的子类,这个子类重写了
close()
方法 -
我们的连接池里面放入的是我们自己实现的Connection接口的实现类,这个实现类的
close()
方法是把连接放入到连接池
3、开源的数据库连接池
3.1 DBCP
(1)导包
commons-dbcp-1.4.jar
commons-pool-1.6.jar
(2)配置一个properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/31th_sql4
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk;characterEncoding=utf8;useSSL=false;serverTimezone=Asia/Shanghai
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READ
(3)使用
public class DBCPUtils {
// 是一个连接池的接口,通常叫做数据源
private static DataSource dataSource;
static {
Properties properties = new Properties();
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("dbcp.properties");
properties.load(fileInputStream);
// 创建一个基础的数据源工厂
BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory();
// 加载配置文件
dataSource = basicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接的方法
public static Connection getConnection() {
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
3.2 C3P0
(1)导包:
(2)在src目录下配置一个文件,这个文件的路径必须在src目录下,这个文件的名字必须是 c3p0-config.xml。
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!--
在xml文件里面,有一些特殊的字符是不能使用的,使用这些字符需要使用他们的转义字符
> gt;
< lt;
& &
-->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/31th_sql4?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<!--<named-config name="mysql">-->
<!--<property name="driverClass">com.mysql.jdbc.Driver</property>-->
<!--<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>-->
<!--<property name="user">root</property>-->
<!--<property name="password">root</property>-->
<!--<property name="acquireIncrement">5</property>-->
<!--<property name="initialPoolSize">10</property>-->
<!--<property name="minPoolSize">5</property>-->
<!--<property name="maxPoolSize">20</property>-->
<!--</named-config>-->
<!--<named-config name="oracle">-->
<!--<property name="driverClass">com.mysql.jdbc.Driver</property>-->
<!--<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>-->
<!--<property name="user">root</property>-->
<!--<property name="password">root</property>-->
<!--<property name="acquireIncrement">5</property>-->
<!--<property name="initialPoolSize">10</property>-->
<!--<property name="minPoolSize">5</property>-->
<!--<property name="maxPoolSize">20</property>-->
<!--</named-config>-->
</c3p0-config>
(3)使用
package com.cskaoyan.datasource.C3P0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class C3p0Utils {
private static DataSource dataSource;
static {
// 约定大于配置 方便开发者的使用
// 给了一些约定俗成的配置,不需要修改
dataSource = new ComboPooledDataSource();
}
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
3.3 Druid
这个是阿里巴巴开源的一个数据库连接池,在国内因为良好的性能被广泛使用。
(1)导包:
(2)配置:
url=jdbc:mysql://localhost:3306/31th_sql4?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
(3)使用:
package com.cskaoyan.datasource.Druid;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class DruidUtils {
static DataSource dataSource;
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("druid.properties"));
DruidDataSourceFactory druidDataSourceFactory = new DruidDataSourceFactory();
dataSource = druidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}