我们用 JDBC 操作数据库的时候,每次操作完都会将连接关闭。数据库连接是极其宝贵的资源,频繁的创建和销毁会极大地降低服务器的性能。因此,我们可以利用池化技术,重复利用数据库资源,避免没必要的开销。
用口语来说,相当于我们创建了一个池子,这个池子里面放的都是一些JDBC的连接,也就是Connection对象,每当我们需要时,我们去从连接池里面获取连接,使用完了连接,放回连接池,做到连接的反复使用。
手写一个数据库连接池
思路:
- 要实现DataSource接口(SUN公司就制定的连接池的标准)
- 要有一个getConnection()方法用来获取连接池中的connection对象
- 连接池中的connection对象有进有出,所以把他们放入队列使用起来是比较方便的
- 当connection对象调用close()方法的时候不应该关闭连接池,而是要把连接放回连接池中,所以我们要自建一个连接对象(通过实现connection接口)
伪代码如下:
重写close():
public class MyWrapperConnection implements Connection{
// 维护一个Connection对象,重写除close()以外的方法我们直接用Connection对象的方法
private Connection connection;
// 维护一个队列用来
private LinkedList<Connection> queue;
// 无参构造
public MyWrapperConnection() {
}
// 有参构造
public MyWrapperConnection(Connection connection, LinkedList<Connection> linkedList) {
this.connection = connection;
this.linkedList = linkedList;
}
//重写close()方法
@Override
public void close() throws SQLException {
// 用完放回队列
queue.offer(this);
}
构建连接池:
public class MyConnectionPool implements DataSource {
// 得用一个数据结构去存放连接
static LinkedList<Connection> queue;
static {
init(10);
}
// 往连接池里面放连接的方法
private static void init(int num) {
if (connectionPool == null) {
connectionPool = new LinkedList<>();
}
for (int i = 0; i < num; i++) {
// 添加一个连接
Connection connection = JDBCUtils.getConnection();
MyWrapperConnection myWrapperConnection = new MyWrapperConnection(connection, queue);
connectionPool.addFirst(myWrapperConnection);
}
}
@Override
public Connection getConnection() throws SQLException {
return queue.pull();
}
总结:我们发现自己手动实现这一系列连接池比较麻烦,并且很多功能自己都没有去实现,所以网上有很多第三方的开源的数据库连接池供我们使用,如DBCP、C3p0、Druid。
开源的数据库连接池
1️⃣DBCP
第一步,导包
<!--DBCP-->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
第二步,配置
dbcp.properties文件中进行如下配置:
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/28_jdbc
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=utf-8;useSSL=false
#指定由连接池所创建的连接的自动提交(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
第三步,创建DBCPUtils对象
创建一个工具类用来获取连接
public class DBCPUtils {
// 声明一个数据源对象
private static DataSource dataSource;
static {
// 加载配置文件
Properties properties = new Properties();
ClassLoader classLoader = DBCPUtils.class.getClassLoader();
InputStream stream = classLoader.getResourceAsStream("dbcp.properties");
try {
properties.load(stream);
} catch (IOException e) {
e.printStackTrace();
}
// 涉及到了一个设计模式:工厂模式(后面会讲)
// 创建一个数据源工厂对象
BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory();
try {
// 给Datasource赋值
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;
}
2️⃣C3p0
第一步,导包
<!--c3p0-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
第二步,配置
在c3p0-config中进行如下配置:
<?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/28_jdbc</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>
</c3p0-config>
第三步,创建C3p0Utils对象
创建一个工具类用来获取连接
public class C3p0Utils {
// 首先声明一个数据源对象
private static DataSource dataSource;
static {
// 给Datasource对象去赋值
dataSource = new ComboPooledDataSource();
}
// 获取连接
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
3️⃣Druid(最重要!!!)
第一步,导包
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.19</version>
</dependency>
第二步,配置
在druid.properties文件中进行如下配置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db1
username=root
password=123456
第三步,创建DruidUtils对象
创建一个工具类用来获取连接
public class DruidUtils {
private static DataSource dataSource;
static {
// 加载配置文件
Properties properties = new Properties();
ClassLoader classLoader = DruidUtils.class.getClassLoader();
InputStream stream = classLoader.getResourceAsStream("druid.properties");
try {
properties.load(stream);
} catch (IOException e) {
e.printStackTrace();
}
// 创建一个Druid数据源工厂对象
DruidDataSourceFactory dataSourceFactory = new DruidDataSourceFactory();
try {
// 通过工厂创建一个数据源并且去赋值
dataSource = dataSourceFactory.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;
}
}