一.什么是连接池
和线程池类似,为了避免数据库连接频繁建立、关闭的开销,在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。
外部使用者可通过getConnection 方法获取连接,使用完毕后再通过releaseConnection方法将连接返回,由连接池管理器回收。
二.为啥使用连接池
节省创建连接与释放连接性能消耗,连接池中连接起到复用的作用,提高程序性能。
减少系统响应时间,程序启动的时候,就已建立数据库连接池,对于业务请求可以直接利用现有可用的连接,避免再次连接和手动释放连接。
三.如何创建连接池
javax.sql包下的 DataSource接口,getConnection()用来获取一个连接对象Connection。所以,支持java的连接池都实现了javax.sql.DataSource接口。用来创建连接池,目前可用的连接池有C3P0、Druid等,可通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。
四.JDBC
1.什么是JDBC
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成,是一种约定。
2.数据库驱动
我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。
图1.1
引用菜鸟教程上的一段代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class DbUtil {
public static final String URL = "jdbc:mysql://localhost:3306/imooc";
public static final String USER = "liulx";
public static final String PASSWORD = "123456";
public static void main(String[] args) throws Exception {
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2. 获得数据库连接
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
//3.操作数据库,实现增删改查
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT user_name, age FROM imooc_goddess");
//如果有数据,rs.next()返回true
while(rs.next()){
System.out.println(rs.getString("user_name")+" 年龄:"+rs.getInt("age"));
}
}
}
想操作数据库,有2个步骤是必须的,
1:加载MySQL驱动
2:获取数据库连接
那么为啥需要先加载驱动呢,我们先进mysql Driver源码看看,这里实现了java.sql.Driver,并且在类加载的时候调用了静态方法,向DriverManager里注册了驱动
package com.mysql.cj.jdbc;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// 静态方法 调用Class.forName("com.mysql.jdbc.Driver")的时候向DriverManager里注册mysql驱动
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
进入java.sql.DriverManager里,发现mysql吧驱动放入了list里,具体啥时候用看后面。
java.sql.DriverManager
public static void registerDriver(Driver driver, DriverAction da) throws SQLException {
if (driver != null) {
// registeredDrivers是个list集合
// private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList();
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
println("registerDriver: " + driver);
} else {
throw new NullPointerException();
}
}
进入DriverManager.getConnection(URL, USER, PASSWORD)方法。伪代码如下,发现是建立连接的时候,需要向registeredDrivers找到mysql的驱动,而这个驱动就是上面加载的。也与图1.1相互照应。
java.sql.DriverManager
private static Connection getConnection(String url, Properties info, Class<?> caller) throws SQLException {
if (url == null) {
throw new SQLException("The url cannot be null", "08001");
} else {
// registeredDrivers集合遍历
Iterator var5 = registeredDrivers.iterator();
while(true) {
while(var5.hasNext()) {
DriverInfo aDriver = (DriverInfo)var5.next();
if (isDriverAllowed(aDriver.driver, callerCL)) {
try {
// 使用自己的driver建立链接
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
println("getConnection returning " + aDriver.driver.getClass().getName());
return con;
}
} catch (SQLException var8) {
if (reason == null) {
reason = var8;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
}
}
}
五.druid源码分析
连接池即一组数据库连接的集合,是不是单个连接具有的特征连接池也具有呢?拿druid来说,我们可以进入到DruidDataSource内部,发现其同样继承了DataSource
com.alibaba.druid.pool.DruidDataSource
public class DruidDataSource extends DruidAbstractDataSource
public abstract class DruidAbstractDataSource extends WrapperAdapter implements DruidAbstractDataSourceMBean, DataSource, DataSourceProxy, Serializable
全局搜索getConnection()方法,伪代码如下,连接池的创建是在init()方法
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
// 初始化
init();
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {
return getConnectionDirect(maxWaitMillis);
}
}
进入init
public void init() throws SQLException {
if (inited) {
return;
}
// 懒加载DruidDriver实例
DruidDriver.getInstance();
// 加可重入锁
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
throw new SQLException("interrupt", e);
}
boolean init = false;
try {
if (inited) {
return;
}
initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());
// 创建DataSourceId
this.id = DruidDriver.createDataSourceId();
if (this.id > 1) {
long delta = (this.id - 1) * 100000;
this.connectionIdSeedUpdater.addAndGet(this, delta);
this.statementIdSeedUpdater.addAndGet(this, delta);
this.resultSetIdSeedUpdater.addAndGet(this, delta);
this.transactionIdSeedUpdater.addAndGet(this, delta);
}
// 数据库类型判断 mysql oracle
initCheck();
// 创建连接数组,最大活跃数
connections = new DruidConnectionHolder[maxActive];
evictConnections = new DruidConnectionHolder[maxActive];
keepAliveConnections = new DruidConnectionHolder[maxActive];
SQLException connectError = null;
if (createScheduler != null && asyncInit) {
for (int i = 0; i < initialSize; ++i) {
submitCreateTask(true);
}
} else if (!asyncInit) {
// init connections
while (poolingCount < initialSize) {
try {
// 创建连接
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
// 向连接池中添加连接
connections[poolingCount++] = holder;
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
if (initExceptionThrow) {
connectError = ex;
break;
} else {
Thread.sleep(3000);
}
}
}
if (poolingCount > 0) {
poolingPeak = poolingCount;
poolingPeakTime = System.currentTimeMillis();
}
}
} catch (SQLException e) {
} finally {
}
}
看到PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection()这个方法,是创建数据库连接的方法,进入createPhysicalConnection
com.alibaba.druid.pool.DruidAbstractDataSource
public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
try {
conn = createPhysicalConnection(url, physicalConnectProperties);
connectedNanos = System.nanoTime();
if (conn == null) {
throw new SQLException("connect error, url " + url + ", driverClass " + this.driverClass);
}
initPhysicalConnection(conn, variables, globalVariables);
initedNanos = System.nanoTime();
validateConnection(conn);
validatedNanos = System.nanoTime();
setFailContinuous(false);
setCreateError(null);
} catch (SQLException ex) {
setCreateError(ex);
JdbcUtils.close(conn);
throw ex;
} finally {
}
return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos, variables, globalVariables);
}
进入conn = createPhysicalConnection(url, physicalConnectProperties);看到了熟悉的代码
conn = getDriver().connect(url, info);
public Connection createPhysicalConnection(String url, Properties info) throws SQLException {
Connection conn;
if (getProxyFilters().size() == 0) {
conn = getDriver().connect(url, info);
} else {
conn = new FilterChainImpl(this).connection_connect(info);
}
createCountUpdater.incrementAndGet(this);
return conn;
}
然后循环上面的逻辑,把连接都加入到connections[poolingCount++]数组里
while (poolingCount < initialSize) {
try {
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
connections[poolingCount++] = holder;
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
if (initExceptionThrow) {
connectError = ex;
break;
} else {
Thread.sleep(3000);
}
}
}
更多推荐
MySQL- Connection Pool(数据库连接池)
发布评论