DataSource数据源
所有的数据源组件都实现了javax.sql.DataSource接口,Mybatis实现了两个接口实现,分别为PooledDataSource和UnpooledDataSource,使用不同的DataSourceFactory接口实现创建不同类型的DataSource
UnpooledDataSource
每次通过UnpooledDataSource.getConnection()方法获取数据库连接时都会创建一个新连接。
UnpooledDataSource加载时首先会通过以下代码将已经在DriverManger中注册的JDBC驱动复制一份存到registeredDrivers中
1 2 3 4 5 6 7 8 9
| static { Enumeration drivers = DriverManager.getDrivers();
while(drivers.hasMoreElements()) { Driver driver = (Driver)drivers.nextElement(); registeredDrivers.put(driver.getClass().getName(), driver); }
}
|
使用getConnection()方法以及所有重载时都会调用doGetConnection()方法
1 2 3 4 5 6 7 8
| private Connection doGetConnection(Properties properties) throws SQLException { this.initializeDriver(); Connection connection = DriverManager.getConnection(this.url, properties); this.configureConnection(connection); return connection; }
|
initializeDriver()负责数据库驱动的初始化,创建指定的Driver驱动对象,并将其注册到DriverManager中以及registeredDrivers中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| private synchronized void initializeDriver() throws SQLException { if (!registeredDrivers.containsKey(this.driver)) { try { Class driverType; if (this.driverClassLoader != null) { driverType = Class.forName(this.driver, true, this.driverClassLoader); } else { driverType = Resources.classForName(this.driver); }
Driver driverInstance = (Driver)driverType.getDeclaredConstructor().newInstance(); DriverManager.registerDriver(new UnpooledDataSource.DriverProxy(driverInstance)); registeredDrivers.put(this.driver, driverInstance); } catch (Exception var3) { throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + var3); } }
}
|
可能大家都不知道为什么会有注册驱动这一步,之前使用JDBC连接的时候只使用过DriverManager.getConnection获取连接,没有使用过DriverManager.registerDriver,而且可以正常使用驱动连接数据库获取数据,在这里给大家普及一下数据库的驱动
以Mysql驱动为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.mysql.jdbc;
import java.sql.DriverManager; import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { }
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
|
如果还是没明白,就看下述代码
1 2 3 4 5
| Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc_demo","root","root");
|
PooledDataSource
数据库连接的创建是非常耗时的,数据库能够建立的连接数也是有限的,像上述UnpooledDataSource中这种每次都会创建连接的方式是存在弊端的,很多系统都是使用数据库连接池。
数据库连接池的好处
可以实现数据库连接的重用
提高响应速度
防止数据库连接过多造成数据库假死
避免数据库连接泄露
数据库连接池
数据库连接池在初始化时,会创建一定数量的数据库连接并添加到连接池中备用。当程序需要使用数据库连接时,从池中获取连接;当程序不再使用该连接时,会将其返回到池中缓存,等待下次使用,而不是直接关闭。数据库连接池可以设置连接总数上限以及空闲连接数上限,如果连接池创建的总连接数已达到上限,且都已被占用,则后续请求连接的线程会进入阻塞队列等待,直到有线程释放可用连接。如果连接池中空闲连接数较多,达到其上限,则后续返回的空闲连接不会放到池中,而是直接关闭,这样可以减少系统维护多余数据库连接的开销。
PooledDataSource
在使用PooledDataSource.getConnection的时候会调用popConnection方法,
1 2 3
| public Connection getConnection(String username, String password) throws SQLException { return this.popConnection(username, password).getProxyConnection(); }
|
在popConnection中操作的是PooledConnection,PooledConnection中封装了真正的数据库连接对象以及其代理对象(使用JDK动态代理产生的)
在PooledDataSource中有一个特别重要的属性
1
| private final PoolState state = new PoolState(this);
|
PoolState是用来管理PooledConnection对象状态的组件,使用idleConnections和activeConnections两个集合来管理连接
1 2 3 4
| protected final List<PooledConnection> idleConnections = new ArrayList();
protected final List<PooledConnection> activeConnections = new ArrayList();
|
PoolState还有一些是定义了一系列用来统计的字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| protected long requestCount = 0L;
protected long accumulatedRequestTime = 0L;
protected long accumulatedCheckoutTime = 0L;
protected long claimedOverdueConnectionCount = 0L;
protected long accumulatedCheckoutTimeOfOverdueConnections = 0L;
protected long accumulatedWaitTime = 0L;
protected long hadToWaitCount = 0L;
protected long badConnectionCount = 0L;
|
PooledConnection是实现了InvocationHandler接口,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if ("close".equals(methodName)) { this.dataSource.pushConnection(this); return null; } else { try { if (!Object.class.equals(method.getDeclaringClass())) { this.checkConnection(); }
return method.invoke(this.realConnection, args); } catch (Throwable var6) { throw ExceptionUtil.unwrapThrowable(var6); } } }
|