Mybatis datasource 包源码分析

和所有的框架一样,凡是功能性的,都会自定义异常.
所以就有了 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

分析 `openSessionFromDataSource` 源码之前,需要了解一些 Mybatis 的基本知识。 Mybatis 是一个持久层框架,用于简化数据库操作。它使用接口和 XML 文件来配置和映射数据库中的数据。 Mybatis 使用 SqlSession 对象来执行 SQL 语句,SqlSession 对象的实例可以通过 SqlSessionFactory 获得。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 来创建。 `openSessionFromDataSource` 方法位于 SqlSessionFactory 类中。它的作用是从数据源中获取 SqlSession 对象。这通常用于基于数据源的事务管理,因为数据源可以提供连接池来管理数据库连接。 以下是 `openSessionFromDataSource` 方法的源码: ```java public SqlSession openSessionFromDataSource(Interceptor... interceptors) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), null, false); final Executor executor = new ExecutorWrapper(configuration, tx.getConnection(), interceptors); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } ``` 在这段代码中,首先会从 Mybatis 的环境中获取事务工厂,然后使用事务工厂创建新的事务。接着,使用 ExecutorWrapper 装器创建 Executor 对象,并使用配置、事务连
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值