和所有的框架一样,凡是功能性的,都会自定义异常.
所以就有了 DataSourceException 这个类.
public class DataSourceException extends PersistenceException {
private static final long serialVersionUID = -5251396250407091334L;
public DataSourceException() {
super();
}
public DataSourceException(String message) {
super(message);
}
public DataSourceException(String message, Throwable cause) {
super(message, cause);
}
public DataSourceException(Throwable cause) {
super(cause);
}
}
DataSourceFactory 创建 DataSource 的工厂.
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
我们先看下 UnpooledDataSource 这个类. 这个是没有池化的类,也就是说调用一次 getConnection 方法就会创建一个 connection.
public class UnpooledDataSource implements DataSource {
// 加载 driverClass 的 classLoader.
private ClassLoader driverClassLoader;
// 可以将 username,password 等信息用 map 的形式传入.
private Properties driverProperties;
// 存放以注册的 driver.
private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();
// 连接数据库基本参数.
private String driver;
private String url;
private String username;
private String password;
// 是否自动提交和事务隔离级别.
private Boolean autoCommit;
private Integer defaultTransactionIsolationLevel;
static {
Enumeration drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
registeredDrivers.put(driver.getClass().getName(), driver);
}
}
// 构造函数
public UnpooledDataSource() {
}
public UnpooledDataSource(String driver, String url, String username, String password) {
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
}
public UnpooledDataSource(String driver, String url, Properties driverProperties) {
this.driver = driver;
this.url = url;
this.driverProperties = driverProperties;
}
public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
this.driverClassLoader = driverClassLoader;
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
}
public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
this.driverClassLoader = driverClassLoader;
this.driver = driver;
this.url = url;
this.driverProperties = driverProperties;
}
@Override
public Connection getConnection() throws SQLException {
return doGetConnection(username, password);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return doGetConnection(username, password);
}
@Override
public void setLoginTimeout(int loginTimeout) throws SQLException {
DriverManager.setLoginTimeout(loginTimeout);
}
@Override
public int getLoginTimeout() throws SQLException {
return DriverManager.getLoginTimeout();
}
@Override
public void setLogWriter(PrintWriter logWriter) throws SQLException {
DriverManager.setLogWriter(logWriter);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return DriverManager.getLogWriter();
}
public ClassLoader getDriverClassLoader() {
return driverClassLoader;
}
public void setDriverClassLoader(ClassLoader driverClassLoader) {
this.driverClassLoader = driverClassLoader;
}
public Properties getDriverProperties() {
return driverProperties;
}
public void setDriverProperties(Properties driverProperties) {
this.driverProperties = driverProperties;
}
public String getDriver() {
return driver;
}
public synchronized void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean isAutoCommit() {
return autoCommit;
}
public void setAutoCommit(Boolean autoCommit) {
this.autoCommit = autoCommit;
}
public Integer getDefaultTransactionIsolationLevel() {
return defaultTransactionIsolationLevel;
}
public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
this.defaultTransactionIsolationLevel = defaultTransactionIsolationLevel;
}
private Connection doGetConnection(String username, String password) throws SQLException {
Properties props = new Properties();
if (driverProperties != null) {
props.putAll(driverProperties);
}
if (username != null) {
props.setProperty(“user”, username);
}
if (password != null) {
props.setProperty(“password”, password);
}
return doGetConnection(props);
}
// 获取 connection 并配置是否自动提交和事务隔离级别.
private Connection doGetConnection(Properties properties) throws SQLException {
initializeDriver();
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
// 初始化 driver.
private synchronized void initializeDriver() throws SQLException {
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
if (driverClassLoader != null) {
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
Driver driverInstance = (Driver)driverType.newInstance();
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
private void configureConnection(Connection conn) throws SQLException {
if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
conn.setAutoCommit(autoCommit);
}
if (defaultTransactionIsolationLevel != null) {
conn.setTransactionIsolation(defaultTransactionIsolationLevel);
}
}
private static class DriverProxy implements Driver {
private Driver driver;
DriverProxy(Driver d) {
this.driver = d;
}
@Override
public boolean acceptsURL(String u) throws SQLException {
return this.driver.acceptsURL(u);
}
@Override
public Connection connect(String u, Properties p) throws SQLException {
return this.driver.connect(u, p);
}
@Override
public int getMajorVersion() {
return this.driver.getMajorVersion();
}
@Override
public int getMinorVersion() {
return this.driver.getMinorVersion();
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
return this.driver.getPropertyInfo(u, p);
}
@Override
public boolean jdbcCompliant() {
return this.driver.jdbcCompliant();
}
// @Override only valid jdk7+
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
@Override
public T unwrap(Class iface) throws SQLException {
throw new SQLException(getClass().getName() + " is not a wrapper.");
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
// @Override only valid jdk7+
public Logger getParentLogger() {
// requires JDK version 1.6
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
UnpooledDataSourceFactory 产生 DataSource 的工厂.
public class UnpooledDataSourceFactory implements DataSourceFactory {
private static final String DRIVER_PROPERTY_PREFIX = “driver.”;
private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();
protected DataSource dataSource;
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
@Override
public void setProperties(Properties properties) {
Properties driverProperties = new Properties();
// MetaObject 为 Mybatis 自己实现的一个东西,它可以利用反射获取到 dataSource 的方法和属性等.
MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
for (Object key : properties.keySet()) {
String propertyName = (String) key;
if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
String value = properties.getProperty(propertyName);
driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
} else if (metaDataSource.hasSetter(propertyName)) {
String value = (String) properties.get(propertyName);
Object convertedValue = convertValue(metaDataSource, propertyName, value);
metaDataSource.setValue(propertyName, convertedValue);
} else {
throw new DataSourceException("Unknown DataSource property: " + propertyName);
}
}
// 设置 driverProperties 的值.
if (driverProperties.size() > 0) {
metaDataSource.setValue(“driverProperties”, driverProperties);
}
}
@Override
public DataSource getDataSource() {
return dataSource;
}
// 根据 MetaObject 中该属性的 setter 方法的参数类型,确定 properties 文件中的值如何转换.
private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
Object convertedValue = value;
Class<?> targetType = metaDataSource.getSetterType(propertyName);
if (targetType == Integer.class || targetType == int.class) {
convertedValue = Integer.valueOf(value);
} else if (targetType == Long.class || targetType == long.class) {
convertedValue = Long.valueOf(value);
} else if (targetType == Boolean.class || targetType == boolean.class) {
convertedValue = Boolean.valueOf(value);
}
return convertedValue;
}
}
PooledConnection 类分析. 该类对 connection 进行了增强,在调用 close 方法的时候,是将 connection 放进线程池.
class PooledConnection implements InvocationHandler {
private static final String CLOSE = “close”;
private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
private final int hashCode;
private final PooledDataSource dataSource;
// 真正的数据库连接
private final Connection realConnection;
// 代理数据库连接
private final Connection proxyConnection;
// 从连接池中取出该连接的时间戳
private long checkoutTimestamp;
// 该连接创建的时间戳
private long createdTimestamp;
// 最后一次被使用的时间戳
private long lastUsedTimestamp;
// 由数据库URL,用户名,密码计算出来的hash值,可用于标识该连接所在的连接池
private int connectionTypeCode;
// 检测当前PooledConnection是否有效,主要是为了防止程序通过close()方法将连接归还给连接池之后,依然通过该连接操作数据库
private boolean valid;
/*
- Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in
- @param connection - the connection that is to be presented as a pooled connection
- @param dataSource - the dataSource that the connection is from
*/
public PooledConnection(Connection connection, PooledDataSource dataSource) {
this.hashCode = connection.hashCode();
this.realConnection = connection;
this.dataSource = dataSource;
this.createdTimestamp = System.currentTimeMillis();
this.lastUsedTimestamp = System.currentTimeMillis();
this.valid = true;
this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
}
/*
- Invalidates the connection
*/
public void invalidate() {
valid = false;
}
/*
- Method to see if the connection is usable
- @return True if the connection is usable
*/
public boolean isValid() {
return valid && realConnection != null && dataSource.pingConnection(this);
}
/*
- Getter for the real connection that this wraps
- @return The connection
*/
public Connection getRealConnection() {
return realConnection;
}
/*
- Getter for the proxy for the connection
- @return The proxy
*/
public Connection getProxyConnection() {
return proxyConnection;
}
/*
- Gets the hashcode of the real connection (or 0 if it is null)
- @return The hashcode of the real connection (or 0 if it is null)
*/
public int getRealHashCode() {
return realConnection == null ? 0 : realConnection.hashCode();
}
/*
- Getter for the connection type (based on url + user + password)
- @return The connection type
*/
public int getConnectionTypeCode() {
return connectionTypeCode;
}
/*
- Setter for the connection type
- @param connectionTypeCode - the connection type
*/
public void setConnectionTypeCode(int connectionTypeCode) {
this.connectionTypeCode = connectionTypeCode;
}
/*
- Getter for the time that the connection was created
- @return The creation timestamp
*/
public long getCreatedTimestamp() {
return createdTimestamp;
}
/*
- Setter for the time that the connection was created
- @param createdTimestamp - the timestamp
*/
public void setCreatedTimestamp(long createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
/*
- Getter for the time that the connection was last used
- @return - the timestamp
*/
public long getLastUsedTimestamp() {
return lastUsedTimestamp;
}
/*
- Setter for the time that the connection was last used
- @param lastUsedTimestamp - the timestamp
*/
public void setLastUsedTimestamp(long lastUsedTimestamp) {
this.lastUsedTimestamp = lastUsedTimestamp;
}
/*
- Getter for the time since this connection was last used
- @return - the time since the last use
*/
public long getTimeElapsedSinceLastUse() {
return System.currentTimeMillis() - lastUsedTimestamp;
}
/*
- Getter for the age of the connection
- @return the age
*/
public long getAge() {
return System.currentTimeMillis() - createdTimestamp;
}
/*
- Getter for the timestamp that this connection was checked out
- @return the timestamp
*/
public long getCheckoutTimestamp() {
return checkoutTimestamp;
}
/*
- Setter for the timestamp that this connection was checked out
- @param timestamp the timestamp
*/
public void setCheckoutTimestamp(long timestamp) {
this.checkoutTimestamp = timestamp;
}
/*
- Getter for the time that this connection has been checked out
- @return the time
*/
public long getCheckoutTime() {
return System.currentTimeMillis() - checkoutTimestamp;
}
@Override
public int hashCode() {
return hashCode;
}
/*
- Allows comparing this connection to another
- @param obj - the other connection to test for equality
- @see Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof PooledConnection) {
return realConnection.hashCode() == ((PooledConnection) obj).realConnection.hashCode();
} else if (obj instanceof Connection) {
return hashCode == obj.hashCode();
} else {
return false;
}
}
/*
- Required for InvocationHandler implementation.
- @param proxy - not used
- @param method - the method to be executed
- @param args - the parameters to be passed to the method
- @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// 如果调用的方法为close,则将其重新放入连接池,而不是真正关闭数据库连接
if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
} else {
try {
if (!Object.class.equals(method.getDeclaringClass())) {
// issue #579 toString() should never fail
// throw an SQLException instead of a Runtime
checkConnection();
}
// 调用真正数据库连接对象的对应方法
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
private void checkConnection() throws SQLException {
if (!valid) {
throw new SQLException(“Error accessing PooledConnection. Connection is invalid.”);
}
}
}
在看下 PoolState 类. 用于管理连接池的状态.
public class PoolState {
protected PooledDataSource dataSource;
// 空闲连接集合
protected final List idleConnections = new ArrayList();
// 活跃连接集合
protected final List activeConnections = new ArrayList();
// 请求数据库连接次数
protected long requestCount = 0;
// 获取连接的累计时长
protected long accumulatedRequestTime = 0;
// 记录所有连接累计的 checkout 时长
protected long accumulatedCheckoutTime = 0;
// 当连接长时间未归还给连接池时,会被认为该连接超时,记录超时的连接个数
protected long claimedOverdueConnectionCount = 0;
// 累计超时时长
protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
// 累计等待时间
protected long accumulatedWaitTime = 0;
// 等待次数
protected long hadToWaitCount = 0;
无效的连接数
protected long badConnectionCount = 0;
public PoolState(PooledDataSource dataSource) {
this.dataSource = dataSource;
}
public synchronized long getRequestCount() {
return requestCount;
}
public synchronized long getAverageRequestTime() {
return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount;
}
public synchronized long getAverageWaitTime() {
return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount;
}
public synchronized long getHadToWaitCount() {
return hadToWaitCount;
}
public synchronized long getBadConnectionCount() {
return badConnectionCount;
}
public synchronized long getClaimedOverdueConnectionCount() {
return claimedOverdueConnectionCount;
}
public synchronized long getAverageOverdueCheckoutTime() {
return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount;
}
public synchronized long getAverageCheckoutTime() {
return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount;
}
public synchronized int getIdleConnectionCount() {
return idleConnections.size();
}
public synchronized int getActiveConnectionCount() {
return activeConnections.size();
}
@Override
public synchronized String toString() {
StringBuilder builder = new StringBuilder();
builder.append("\n=CONFINGURATION============================================");
builder.append("\n jdbcDriver “).append(dataSource.getDriver());
builder.append(”\n jdbcUrl “).append(dataSource.getUrl());
builder.append(”\n jdbcUsername “).append(dataSource.getUsername());
builder.append(”\n jdbcPassword “).append((dataSource.getPassword() == null ? “NULL” : “************”));
builder.append(”\n poolMaxActiveConnections “).append(dataSource.poolMaximumActiveConnections);
builder.append(”\n poolMaxIdleConnections “).append(dataSource.poolMaximumIdleConnections);
builder.append(”\n poolMaxCheckoutTime “).append(dataSource.poolMaximumCheckoutTime);
builder.append(”\n poolTimeToWait “).append(dataSource.poolTimeToWait);
builder.append(”\n poolPingEnabled “).append(dataSource.poolPingEnabled);
builder.append(”\n poolPingQuery “).append(dataSource.poolPingQuery);
builder.append(”\n poolPingConnectionsNotUsedFor “).append(dataSource.poolPingConnectionsNotUsedFor);
builder.append(”\n —STATUS-----------------------------------------------------");
builder.append("\n activeConnections “).append(getActiveConnectionCount());
builder.append(”\n idleConnections “).append(getIdleConnectionCount());
builder.append(”\n requestCount “).append(getRequestCount());
builder.append(”\n averageRequestTime “).append(getAverageRequestTime());
builder.append(”\n averageCheckoutTime “).append(getAverageCheckoutTime());
builder.append(”\n claimedOverdue “).append(getClaimedOverdueConnectionCount());
builder.append(”\n averageOverdueCheckoutTime “).append(getAverageOverdueCheckoutTime());
builder.append(”\n hadToWait “).append(getHadToWaitCount());
builder.append(”\n averageWaitTime “).append(getAverageWaitTime());
builder.append(”\n badConnectionCount “).append(getBadConnectionCount());
builder.append(”\n===============================================================");
return builder.toString();
}
}
接下来看下 PooledDataSource 这个类吧. 字面意思是池化的 DataSource. 也就是说每次调用 getConnection 可能不在是每次都要创建一个 connection 了.
public class PooledDataSource implements DataSource {
private static final Log log = LogFactory.getLog(PooledDataSource.class);
// 通过 PoolState 管理连接池的状态并统计相关信息.
private final PoolState state = new PoolState(this);
// 真正管理数据库连接的对象,用于生成真实的数据库连接对象
private final UnpooledDataSource dataSource;
// OPTIONAL CONFIGURATION FIELDS
// 最大活跃连接数
protected int poolMaximumActiveConnections = 10;
// 最大空闲连接数
protected int poolMaximumIdleConnections = 5;
// 最大 checkout 时长.
protected int poolMaximumCheckoutTime = 20000;
// 无法获取连接时,线程需要等待的时长
protected int poolTimeToWait = 20000;
// 每一个尝试从缓存池获取连接的线程. 如果这个线程获取到的是一个坏的连接,
// 那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超
// 过 poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。
protected int poolMaximumLocalBadConnectionTolerance = 3;
// 在检测一个数据库是否可用时,会给数据库发送一个测试SQL语句
protected String poolPingQuery = "NO PING QUERY SET”;
// 是否允许发送测试SQL语句
protected boolean poolPingEnabled;
// 当连接超过poolPingConnectionsNotUsedFor毫秒未使用时,会发送一次测试SQL语句,检测连接是否正常
protected int poolPingConnectionsNotUsedFor;
// 根据数据库的URL、用户名和密码生成的一个hash值,该哈希值用于标志着当前的连接池
private int expectedConnectionTypeCode;
// 构造 PooledDataSource.
public PooledDataSource() {
dataSource = new UnpooledDataSource();
}
public PooledDataSource(UnpooledDataSource dataSource) {
this.dataSource = dataSource;
}
public PooledDataSource(String driver, String url, String username, String password) {
dataSource = new UnpooledDataSource(driver, url, username, password);
// 根据 url,username,password 生成 hashcode.
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
}
public PooledDataSource(String driver, String url, Properties driverProperties) {
dataSource = new UnpooledDataSource(driver, url, driverProperties);
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
}
public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
}
public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
}
@Override
public Connection getConnection() throws SQLException {
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return popConnection(username, password).getProxyConnection();
}
@Override
public void setLoginTimeout(int loginTimeout) throws SQLException {
DriverManager.setLoginTimeout(loginTimeout);
}
@Override
public int getLoginTimeout() throws SQLException {
return DriverManager.getLoginTimeout();
}
@Override
public void setLogWriter(PrintWriter logWriter) throws SQLException {
DriverManager.setLogWriter(logWriter);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return DriverManager.getLogWriter();
}
public void setDriver(String driver) {
dataSource.setDriver(driver);
forceCloseAll();
}
public void setUrl(String url) {
dataSource.setUrl(url);
forceCloseAll();
}
public void setUsername(String username) {
dataSource.setUsername(username);
forceCloseAll();
}
public void setPassword(String password) {
dataSource.setPassword(password);
forceCloseAll();
}
public void setDefaultAutoCommit(boolean defaultAutoCommit) {
dataSource.setAutoCommit(defaultAutoCommit);
forceCloseAll();
}
public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
dataSource.setDefaultTransactionIsolationLevel(defaultTransactionIsolationLevel);
forceCloseAll();
}
public void setDriverProperties(Properties driverProps) {
dataSource.setDriverProperties(driverProps);
forceCloseAll();
}
/*
- The maximum number of active connections
- @param poolMaximumActiveConnections The maximum number of active connections
*/
public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
this.poolMaximumActiveConnections = poolMaximumActiveConnections;
forceCloseAll();
}
/*
- The maximum number of idle connections
- @param poolMaximumIdleConnections The maximum number of idle connections
*/
public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
this.poolMaximumIdleConnections = poolMaximumIdleConnections;
forceCloseAll();
}
/*
- The maximum number of tolerance for bad connection happens in one thread
- which are applying for new {@link PooledConnection}
- @param poolMaximumLocalBadConnectionTolerance
- max tolerance for bad connection happens in one thread
- @since 3.4.5
*/
public void setPoolMaximumLocalBadConnectionTolerance(
int poolMaximumLocalBadConnectionTolerance) {
this.poolMaximumLocalBadConnectionTolerance = poolMaximumLocalBadConnectionTolerance;
}
/*
- The maximum time a connection can be used before it may be
- given away again.
- @param poolMaximumCheckoutTime The maximum time
*/
public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
forceCloseAll();
}
/*
- The time to wait before retrying to get a connection
- @param poolTimeToWait The time to wait
*/
public void setPoolTimeToWait(int poolTimeToWait) {
this.poolTimeToWait = poolTimeToWait;
forceCloseAll();
}
/*
- The query to be used to check a connection
- @param poolPingQuery The query
*/
public void setPoolPingQuery(String poolPingQuery) {
this.poolPingQuery = poolPingQuery;
forceCloseAll();
}
/*
- Determines if the ping query should be used.
- @param poolPingEnabled True if we need to check a connection before using it
*/
public void setPoolPingEnabled(boolean poolPingEnabled) {
this.poolPingEnabled = poolPingEnabled;
forceCloseAll();
}
/*
- If a connection has not been used in this many milliseconds, ping the
- database to make sure the connection is still good.
- @param milliseconds the number of milliseconds of inactivity that will trigger a ping
*/
public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
this.poolPingConnectionsNotUsedFor = milliseconds;
forceCloseAll();
}
public String getDriver() {
return dataSource.getDriver();
}
public String getUrl() {
return dataSource.getUrl();
}
public String getUsername() {
return dataSource.getUsername();
}
public String getPassword() {
return dataSource.getPassword();
}
public boolean isAutoCommit() {
return dataSource.isAutoCommit();
}
public Integer getDefaultTransactionIsolationLevel() {
return dataSource.getDefaultTransactionIsolationLevel();
}
public Properties getDriverProperties() {
return dataSource.getDriverProperties();
}
public int getPoolMaximumActiveConnections() {
return poolMaximumActiveConnections;
}
public int getPoolMaximumIdleConnections() {
return poolMaximumIdleConnections;
}
public int getPoolMaximumLocalBadConnectionTolerance() {
return poolMaximumLocalBadConnectionTolerance;
}
public int getPoolMaximumCheckoutTime() {
return poolMaximumCheckoutTime;
}
public int getPoolTimeToWait() {
return poolTimeToWait;
}
public String getPoolPingQuery() {
return poolPingQuery;
}
public boolean isPoolPingEnabled() {
return poolPingEnabled;
}
public int getPoolPingConnectionsNotUsedFor() {
return poolPingConnectionsNotUsedFor;
}
/*
-
Closes all active and idle connections in the pool
-
关闭所有的活跃连接和非活跃连接.
*/
public void forceCloseAll() {
synchronized (state) {
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
for (int i = state.activeConnections.size(); i > 0; i–) {
try {
PooledConnection conn = state.activeConnections.remove(i - 1);
conn.invalidate();Connection realConn = conn.getRealConnection(); if (!realConn.getAutoCommit()) { realConn.rollback(); } realConn.close();
} catch (Exception e) {
// ignore
}
}
for (int i = state.idleConnections.size(); i > 0; i–) {
try {
PooledConnection conn = state.idleConnections.remove(i - 1);
conn.invalidate();Connection realConn = conn.getRealConnection(); if (!realConn.getAutoCommit()) { realConn.rollback(); } realConn.close();
} catch (Exception e) {
// ignore
}
}
}
if (log.isDebugEnabled()) {
log.debug(“PooledDataSource forcefully closed/removed all connections.”);
}
}
public PoolState getPoolState() {
return state;
}
private int assembleConnectionTypeCode(String url, String username, String password) {
return ("" + url + username + password).hashCode();
}
// 将连接归还到连接池中.
protected void pushConnection(PooledConnection conn) throws SQLException {
synchronized (state) {
state.activeConnections.remove(conn);
if (conn.isValid()) {
if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
state.accumulatedCheckoutTime += conn.getCheckoutTime();
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
state.idleConnections.add(newConn);
newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
conn.invalidate();
if (log.isDebugEnabled()) {
log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
}
state.notifyAll();
} else {
state.accumulatedCheckoutTime += conn.getCheckoutTime();
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
conn.getRealConnection().close();
if (log.isDebugEnabled()) {
log.debug("Closed connection " + conn.getRealHashCode() + ".");
}
conn.invalidate();
}
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
}
state.badConnectionCount++;
}
}
}
// 从池中取出一个 connection.
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
while (conn == null) {
synchronized (state) {
// 看是否有空闲线程.
if (!state.idleConnections.isEmpty()) {
// Pool has available connection
// 有的话就拿出一个 connection.
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
// 没有空闲线程,判断活跃线程数是否小于线程池中最大的活跃线程数.
// Pool does not have available connection
if (state.activeConnections.size() < poolMaximumActiveConnections) {
// Can create new connection
// 创建新 connection.
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// 如果活跃线程数不小于线程池最大活跃线程数了,则不能创建新 connection.
// Cannot create new connection
// 获取最早的活跃连接.
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
// 获取该连接的过期时间.
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
// 检查连接是否超时.
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// Can claim overdue connection
// 如果已超时,以下三条代码都是对超时连接信息进行统计
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
// 将该超时的连接移除.
state.activeConnections.remove(oldestActiveConnection);
// 如果该超时连接事务未提交,则进行回滚.
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
/*
Just log a message for debug and continue to execute the following
statement like nothing happend.
Wrap the bad connection with a new PooledConnection, this will help
to not intterupt current executing thread and give current thread a
chance to join the next competion for another valid/good database
connection. At the end of this loop, bad {@link @conn} will be set as null.
*/
log.debug("Bad connection. Could not roll back");
}
}
// 再次建立该超时连接的真实连接的新代理,因为该超时连接本身就是一个代理,需要拿到它的目标对象实例
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
// 将该超时连接置为无效.
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// Must wait
// 必须等待获取连接.
try {
if (!countedWait) {
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
break;
}
}
}
}
if (conn != null) {
// ping to server and check the connection is valid or not
// 检查 connection 是否有效.
if (conn.isValid()) {
// 如果有效且事务未提交则回滚.
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
// 配置连接代理的相关属性
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
state.activeConnections.add(conn);
state.requestCount++;
state.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
}
state.badConnectionCount++;
localBadConnectionCount++;
conn = null;
if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
}
}
if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
return conn;
}
/*
- Method to check to see if a connection is still usable
- 检查 connection 是否还可用.
- @param conn - the connection to check
- @return True if the connection is still usable
*/
protected boolean pingConnection(PooledConnection conn) {
boolean result = true;
try {
result = !conn.getRealConnection().isClosed();
} catch (SQLException e) {
if (log.isDebugEnabled()) {
log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
}
result = false;
}
if (result) {
if (poolPingEnabled) {
if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
try {
if (log.isDebugEnabled()) {
log.debug("Testing connection " + conn.getRealHashCode() + " ...");
}
Connection realConn = conn.getRealConnection();
Statement statement = realConn.createStatement();
ResultSet rs = statement.executeQuery(poolPingQuery);
rs.close();
statement.close();
if (!realConn.getAutoCommit()) {
realConn.rollback();
}
result = true;
if (log.isDebugEnabled()) {
log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
}
} catch (Exception e) {
log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
try {
conn.getRealConnection().close();
} catch (Exception e2) {
//ignore
}
result = false;
if (log.isDebugEnabled()) {
log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
}
}
}
}
}
return result;
}
/*
- Unwraps a pooled connection to get to the ‘real’ connection
- @param conn - the pooled connection to unwrap
- @return The ‘real’ connection
*/
public static Connection unwrapConnection(Connection conn) {
if (Proxy.isProxyClass(conn.getClass())) {
InvocationHandler handler = Proxy.getInvocationHandler(conn);
if (handler instanceof PooledConnection) {
return ((PooledConnection) handler).getRealConnection();
}
}
return conn;
}
protected void finalize() throws Throwable {
forceCloseAll();
super.finalize();
}
public T unwrap(Class iface) throws SQLException {
throw new SQLException(getClass().getName() + " is not a wrapper.");
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); // requires JDK version 1.6
}
}
PooledDataSourceFactory 类分析:
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
JndiDataSourceFactory 主要是通过 jndi 来获取 datasource.
public class JndiDataSourceFactory implements DataSourceFactory {
public static final String INITIAL_CONTEXT = “initial_context”;
public static final String DATA_SOURCE = “data_source”;
public static final String ENV_PREFIX = “env.”;
private DataSource dataSource;
@Override
public void setProperties(Properties properties) {
try {
InitialContext initCtx;
Properties env = getEnvProperties(properties);
if (env == null) {
initCtx = new InitialContext();
} else {
initCtx = new InitialContext(env);
}
if (properties.containsKey(INITIAL_CONTEXT)
&& properties.containsKey(DATA_SOURCE)) {
Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
} else if (properties.containsKey(DATA_SOURCE)) {
dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
}
} catch (NamingException e) {
throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
}
}
@Override
public DataSource getDataSource() {
return dataSource;
}
private static Properties getEnvProperties(Properties allProps) {
final String PREFIX = ENV_PREFIX;
Properties contextProperties = null;
for (Entry<Object, Object> entry : allProps.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (key.startsWith(PREFIX)) {
if (contextProperties == null) {
contextProperties = new Properties();
}
contextProperties.put(key.substring(PREFIX.length()), value);
}
}
return contextProperties;
}
}
参考:
① https://my.oschina.net/u/3768341/blog/3024560?from=timeline&isappinstalled=0