同步操作将从 icanci/Java-Review 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
datasource
包,如下org.apache.ibatis.datasource.DataSourceFactory
,javax.sql.DataSource
工厂接口public interface DataSourceFactory {
// 设置 DataSource 对象的属性
void setProperties(Properties props);
// 获得 DataSource对象
DataSource getDataSource();
}
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory
,实现 DataSourceFactory 接口,非池化的 DataSourceFactory 实现类
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
driver
– 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
url
– 这是数据库的 JDBC URL 地址。
username
– 登录数据库的用户名。
password
– 登录数据库的密码。
defaultTransactionIsolationLevel
– 默认的连接事务隔离级别。
defaultNetworkTimeout
– 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout()
的 API 文档以获取更多信息。
作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:
driver.encoding=UTF8
这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8
的 encoding
属性给数据库驱动。
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();
// DataSource 对象
protected DataSource dataSource;
// 构造方法
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
// 根据配置文件对象,赋予属性
@Override
public void setProperties(Properties properties) {
// 创建一个对象
Properties driverProperties = new Properties();
// 创建一个 MetaObject 对象
MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
// 遍历 properties 对象
for (Object key : properties.keySet()) {
// 强转key
String propertyName = (String) key;
// 把以 driver. 打头的配置文件的信息初始化到driverProperties对象
if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
String value = properties.getProperty(propertyName);
driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
// 初始化到 MetaObject对象中
} 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 MetaObject中
if (driverProperties.size() > 0) {
metaDataSource.setValue("driverProperties", driverProperties);
}
}
// 返回datasource
@Override
public DataSource getDataSource() {
return dataSource;
}
private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
Object convertedValue = value;
// 获得该属性的 set 方法的参数类型
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;
}
}
org.apache.ibatis.datasource.pooled.PooledDataSourceFactory
,继承 UnpooledDataSourceFactory 类,池化的 DataSourceFactory 实现类。
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
poolMaximumActiveConnections
– 在任意时间可存在的活动(正在使用)连接数量,默认值:10poolMaximumIdleConnections
– 任意时间可能存在的空闲连接数。poolMaximumCheckoutTime
– 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)poolTimeToWait
– 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。poolMaximumLocalBadConnectionTolerance
– 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections
与 poolMaximumLocalBadConnectionTolerance
之和。 默认值:3(新增于 3.4.5)poolPingQuery
– 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。poolPingEnabled
– 是否启用侦测查询。若开启,需要设置 poolPingQuery
属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。poolPingConnectionsNotUsedFor
– 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
// 默认创建了 PooledDataSource 对象
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
org.apache.ibatis.datasource.jndi.JndiDataSourceFactory
,实现 DataSourceFactory 接口,基于 JNDI 的 DataSourceFactory 实现类
JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
initial_context
– 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。data_source
– 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:
env.encoding=UTF8
UTF8
的 encoding
属性。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对象
Properties env = getEnvProperties(properties);
// 创建 InitialContext对象
if (env == null) {
initCtx = new InitialContext();
} else {
initCtx = new InitialContext(env);
}
// 从InitialContext上下文,获取 DataSource 对象
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;
}
}
javax.sql.DataSource
是个神奇的接口,在其上可以衍生出数据连接池、分库分表、读写分离等等功能。public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password)
throws SQLException;
}
org.apache.ibatis.datasource.unpooled.UnpooledDataSource
,实现 DataSource 接口,非池化的 DataSource 对象public class UnpooledDataSource implements DataSource {
// ClassLoader 对象
private ClassLoader driverClassLoader;
// Properties对象
private Properties driverProperties;
// 已经注册的 Driver 映射
// KEY:Driver类名
// VALUE:Driver对象
private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();
// 驱动名称
private String driver;
// 数据库 url
private String url;
// 数据库用户名
private String username;
// 数据库密码
private String password;
// 是否自动提交事务
private Boolean autoCommit;
// 默认的事务隔离级别
private Integer defaultTransactionIsolationLevel;
// 默认的网络超时时间
private Integer defaultNetworkTimeout;
static {
// 初始化 drivers
Enumeration<Driver> 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) {
DriverManager.setLoginTimeout(loginTimeout);
}
@Override
public int getLoginTimeout() {
return DriverManager.getLoginTimeout();
}
@Override
public void setLogWriter(PrintWriter logWriter) {
DriverManager.setLogWriter(logWriter);
}
@Override
public PrintWriter getLogWriter() {
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 synchronized 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;
}
/**
* Gets the default network timeout.
*
* @return the default network timeout
* @since 3.5.2
*/
public Integer getDefaultNetworkTimeout() {
return defaultNetworkTimeout;
}
/**
* Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
*
* @param defaultNetworkTimeout
* The time in milliseconds to wait for the database operation to complete.
* @since 3.5.2
*/
public void setDefaultNetworkTimeout(Integer defaultNetworkTimeout) {
this.defaultNetworkTimeout = defaultNetworkTimeout;
}
// 获取连接
private Connection doGetConnection(String username, String password) throws SQLException {
// 创建Properties对象
Properties props = new Properties();
// 设置 driverProperties 到 props中
if (driverProperties != null) {
props.putAll(driverProperties);
}
// 设置 username 和 password
if (username != null) {
props.setProperty("user", username);
}
if (password != null) {
props.setProperty("password", password);
}
// 获得连接
return doGetConnection(props);
}
private Connection doGetConnection(Properties properties) throws SQLException {
// 初始化 Driver
initializeDriver();
// 获得 Connection 对象
Connection connection = DriverManager.getConnection(url, properties);
// 配置 Connection 对象
configureConnection(connection);
return connection;
}
// 初始化Driver方法
private synchronized void initializeDriver() throws SQLException {
// 判断 registeredDrivers 是否存在这个driver。如果不存在,进行初始化
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
// 获得driver类
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对象
Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
// 创建 DriverProxy 对象
DriverManager.registerDriver(new DriverProxy(driverInstance));
// 添加到 registeredDrivers
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 (defaultNetworkTimeout != null) {
conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
}
// 设置自动提交
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
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException(getClass().getName() + " is not a wrapper.");
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public Logger getParentLogger() {
// requires JDK version 1.6
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
org.apache.ibatis.datasource.pooled.PooledDataSource
,实现 DataSource 接口,池化的 DataSource 实现类。public class PooledDataSource implements DataSource {
private static final Log log = LogFactory.getLog(PooledDataSource.class);
// PoolState 对象,记录池化的状态
private final PoolState state = new PoolState(this);
// UnpooledDataSource 对象
private final UnpooledDataSource dataSource;
// OPTIONAL CONFIGURATION FIELDS
// 池的可以存在的连接数量
protected int poolMaximumActiveConnections = 10;
// 池的任意时间的空闲连接数
protected int poolMaximumIdleConnections = 5;
// 在被强制返回之前,池中连接(check out) 单位:毫秒
protected int poolMaximumCheckoutTime = 20000;
// 如果连接花费相当长的时间,连接池会打印状态日志并重新尝试获取一个连接
protected int poolTimeToWait = 20000;
// 这是一个关于坏连接容忍度的底层设置,作用于每一个尝试从缓存池获取连接的线程.
// 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接
protected int poolMaximumLocalBadConnectionTolerance = 3;
// 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。
protected String poolPingQuery = "NO PING QUERY SET";
// 是否开启侦测查询。
protected boolean poolPingEnabled;
// 配置 poolPingQuery 的频率,
protected int poolPingConnectionsNotUsedFor;
// 期望的 Connection 的类型编码
private int expectedConnectionTypeCode;
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);
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) {
DriverManager.setLoginTimeout(loginTimeout);
}
// 获取时间
@Override
public int getLoginTimeout() {
return DriverManager.getLoginTimeout();
}
@Override
public void setLogWriter(PrintWriter logWriter) {
DriverManager.setLogWriter(logWriter);
}
@Override
public PrintWriter getLogWriter() {
return DriverManager.getLogWriter();
}
// 设置驱动
public void setDriver(String driver) {
dataSource.setDriver(driver);
forceCloseAll();
}
// 设置url
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();
}
/**
* Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
*
* @param milliseconds
* The time in milliseconds to wait for the database operation to complete.
* @since 3.5.2
*/
public void setDefaultNetworkTimeout(Integer milliseconds) {
dataSource.setDefaultNetworkTimeout(milliseconds);
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();
}
/**
* Gets the default network timeout.
*
* @return the default network timeout
* @since 3.5.2
*/
public Integer getDefaultNetworkTimeout() {
return dataSource.getDefaultNetworkTimeout();
}
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;
}
// 关闭所有的 activeConnections和idleConnections的连接
public void forceCloseAll() {
synchronized (state) {
// 计算 expectedConnectionTypeCode
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
// 遍历activeConnections关闭
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
}
}
// 遍历 idleConnections 关闭
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);
// 通过ping来判断连接是否有效
if (conn.isValid()) {
// 判断是否超过空闲连接上限,并且和当前连接池的标识匹配
if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
// 统计连接使用时长
state.accumulatedCheckoutTime += conn.getCheckoutTime();
// 回滚事务
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
// 创建 PooledConnection 对象,并且添加到空闲的连接集合中
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
state.idleConnections.add(newConn);
newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
// 设置原来的连接失效
// 为什么这里需要创建新的 PooledConnection 对象呢?
// 避免使用方还在使用 conn ,通过将它设置为失效,万一再次调用,会抛出异常
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++;
}
}
}
// 获取PooledConnection对象
private PooledConnection popConnection(String username, String password) throws SQLException {
// 标记,获取连接的时候,是否进行了等待
boolean countedWait = false;
// 最终获取到的连接的对象
PooledConnection conn = null;
// 记录当前时间
long t = System.currentTimeMillis();
// 记录当前方法,获取到连接的次数
int localBadConnectionCount = 0;
// 循环,获取可用的Connection连接
while (conn == null) {
synchronized (state) {
// 空闲连接非空
if (!state.idleConnections.isEmpty()) {
// Pool has available 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
// 创建一个连接
conn = new PooledConnection(dataSource.getConnection(), this);
// 如果是debug级别的,打印日志
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// Cannot create new connection
// 获得首个 PooledConnection对象
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) {
log.debug("Bad connection. Could not roll back");
}
}
// 创建新的 PooledConnection对象
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
// 对old设置为无效
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// 检查到未超时
// Must wait
try {
// 对等待连接进行统计,通过 countedWait 标识,在这个循环中,值记录一次
if (!countedWait) {
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
// 记录当前时间
long wt = System.currentTimeMillis();
// 等待,直到超时,或者 pingConnection 方法中归还连接时候唤醒
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
// 通过 ping 检测是否有效
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 置空,那么可以继续获取
conn = null;
// 如果超过最大次数,抛出 SQLException异常
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.");
}
}
}
}
}
// 获取不到连接,抛出SQLException异常
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
*
* @param conn
* - the connection to check
* @return True if the connection is still usable
*/
// 通过向数据库发起 poolPingQuery 语句来发起 ping 操作,以判断数据库连接是否有效
protected boolean pingConnection(PooledConnection conn) {
// 用来记录ping是否成功
boolean result = true;
// 判断真实的连接是否已经关闭,若已经关闭,就意味着 ping肯定是失败的
try {
result = !conn.getRealConnection().isClosed();
} catch (SQLException e) {
if (log.isDebugEnabled()) {
log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
}
result = false;
}
// 是否启用侦测查询
if (result && poolPingEnabled && poolPingConnectionsNotUsedFor >= 0
&& conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
try {
if (log.isDebugEnabled()) {
log.debug("Testing connection " + conn.getRealHashCode() + " ...");
}
Connection realConn = conn.getRealConnection();
// 发起 ping
try (Statement statement = realConn.createStatement()) {
statement.executeQuery(poolPingQuery).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;
}
// 获取真实数据库的连接
public static Connection unwrapConnection(Connection conn) {
// 如果传入的是被代理连接
if (Proxy.isProxyClass(conn.getClass())) {
// 获取 InvocationHandler 对象
InvocationHandler handler = Proxy.getInvocationHandler(conn);
// 如果是 PooledConnection 就是真实的连接
if (handler instanceof PooledConnection) {
return ((PooledConnection) handler).getRealConnection();
}
}
return conn;
}
@Override
protected void finalize() throws Throwable {
// 关闭所有的连接
forceCloseAll();
// 执行对象销毁
super.finalize();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException(getClass().getName() + " is not a wrapper.");
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
org.apache.ibatis.datasource.pooled.PoolState
,连接池状态,记录空闲和激活的 PooledConnection 集合,以及相关的数据统计。public class PoolState {
// 所属的 PooledDataSource对象
protected PooledDataSource dataSource;
// 空闲的 PooledConnection 集合 记录 idleConnections
protected final List<PooledConnection> idleConnections = new ArrayList<>();
// 空闲的 PooledConnection 集合 记录 activeConnections
protected final List<PooledConnection> activeConnections = new ArrayList<>();
// 全局统计 - 获取连接的次数
protected long requestCount = 0;
// 全局统计 - 获取连接的时间
protected long accumulatedRequestTime = 0;
// 全局统计 - 获取连接非超时+超时的占用时长
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();
}
}
org.apache.ibatis.datasource.pooled.PooledConnection
,实现 InvocationHandler 接口,池化的 Connection 对象class PooledConnection implements InvocationHandler {
// 关闭 Connection 方法名
private static final String CLOSE = "close";
// JDK Proxy 的接口
private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
// 对象的标识,基于 {@link #realConnection} 求 hashCode
private final int hashCode;
// 所属的 PooledDataSource对象
private final PooledDataSource dataSource;
// 真实的Connection连接
private final Connection realConnection;
// 代理连接
private final Connection proxyConnection;
// 从连接池中,获取走的时间戳
private long checkoutTimestamp;
// 对象创建时间
private long createdTimestamp;
// 最后更新时间
private long lastUsedTimestamp;
// 连接的标识
private int connectionTypeCode;
// 是否有效
private boolean valid;
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;
}
}
// 代理调用方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判断是不是 close 方法,则将连接放回到连接池中,避免连接关闭
String methodName = method.getName();
if (CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
}
try {
// 判断非 Object 方法,则先检查连接是否可用
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.");
}
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。