package org.apache.ibatis.datasource; import javax.sql.DataSource; import java.util.Properties; /** * MyBatis使用不同的DataSourceFactory接口创建不同类型的DataSource * 这是工厂方法模式的一个典型应用。 * 工厂方法模式定义了一个创建对象的工厂接口,并根据工厂接口的具体是实例化哪一个具体产品类 *UnpooledDataSourceFactory-具体工厂类ConcreteFactory*
* 工厂方法模式四个角色构成: * (1)工厂接口(Factory):工厂接口是工厂方法模式的核心接口,调用者会直接与工厂接口交互用户获取具体的产品实现类; * (2)具体工厂类(ConcreteFactory):具体工厂类是工厂接口的实现类,用于实例化产品对象,不同的具体工厂类会根据 * 需求实例化不同的产品实现类; * (3)产品接口(Product):产品接口用于定义产品类的功能,具体工厂类产生的所有产品对象都必须实例化该接口。 * 调用者一般会面向产品接口进行编程,所有产品接口会与调用者直接交互,活着也是调用者最为关心的接口; * (4)具体产品类(ConcreteProduct):实现产品接口的实现类,具体产品类中定义了具体的业务逻辑; *
*
* 这里可以看出在datasource包下的职责划分为: * (1)DataSourceFactory是工厂接口; * (2)PooledDataSourceFactory、UnpooledDataSourceFactory以及JndiDataSourceFactory是具体工厂类实现了工厂接口; * (3)javax.sql.DataSource是产品接口; * (4)PooledDataSource和UnpooledDataSource是具体产品类实现了产品接口; *
*
* 当有新的数据源模块需要进行添加的时候,只需要添加对应的工厂实现类(具体工厂类(ConcreteFactory))即可,而不用修改其他代码 * @author Clinton Begin */ public interface DataSourceFactory { /** * 设置DataSource的相关属性,一般紧跟在初始化完成之后 * @param props 属性参数 */ void setProperties(Properties props); /** * 获取DataSource对象 */ DataSource getDataSource(); }
package org.apache.ibatis.datasource.unpooled; import org.apache.ibatis.datasource.DataSourceException; import org.apache.ibatis.datasource.DataSourceFactory; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import javax.sql.DataSource; import java.util.Properties; /** * 工厂方法模式中的具体工厂类(工厂接口实现类ConcreteFactory) * @author Clinton Begin */ 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(); /*** 工厂方法模式中的产品接口Product*/ protected DataSource dataSource; /** * 在构造函数中直接创建UnpooledDataSource具体产品类(ConcreteProduct产品实现类) */ public UnpooledDataSourceFactory() { //通过工厂方法模式创建UnpooledDataSource数据源(具体产品类) this.dataSource = new UnpooledDataSource(); } /** * 完成对UnpooledDataSource对象的配置 */ @Override public void setProperties(Properties properties) { Properties driverProperties = new Properties(); //创建DataSource相应的MetaObject MetaObject metaDataSource = SystemMetaObject.forObject(dataSource); //遍历properties集合,该集合中配置了数据源需要的信息 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); //根据属性类型进行类型转换,主要是Integer、Long、Boolean三种类型的转换 Object convertedValue = convertValue(metaDataSource, propertyName, value); //设置DataSource的相关属性值 metaDataSource.setValue(propertyName, convertedValue); } else { throw new DataSourceException("Unknown DataSource property: " + propertyName); } } //设置DataSource.driverProperties属性值 if (driverProperties.size() > 0) { metaDataSource.setValue("driverProperties", driverProperties); } } @Override public DataSource getDataSource() { return dataSource; } 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; } }UnpooledDataSource-具体产品类ConcreteProduct
package org.apache.ibatis.datasource.unpooled; import org.apache.ibatis.io.Resources; import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.*; import java.util.Enumeration; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; /** * MyBatis提供了两个javax.sql.DataSource接口实现,一个是PooledDataSource另一个是UnpooledDataSource * @author Clinton Begin * @author Eduardo Macarron */ public class UnpooledDataSource implements DataSource { /*** 缓存所有已经注册的数据库连接驱动*/ private static MapPooledDataSource-具体产品类ConcreteProductregisteredDrivers = new ConcurrentHashMap (); static { //向DriverManager注册JDBC驱动 Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); registeredDrivers.put(driver.getClass().getName(), driver); } } /*** 加载Driver类的类加载器*/ private ClassLoader driverClassLoader; /*** 数据库连接驱动的相关配置*/ private Properties driverProperties; /*** 数据库连接驱动名称*/ private String driver; private String url; private String username; private String password; /*** 是否自动提交*/ private Boolean autoCommit; /*** 事务隔离级别*/ private Integer defaultTransactionIsolationLevel; 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 int getLoginTimeout() throws SQLException { return DriverManager.getLoginTimeout(); } @Override public void setLoginTimeout(int loginTimeout) throws SQLException { DriverManager.setLoginTimeout(loginTimeout); } @Override public PrintWriter getLogWriter() throws SQLException { return DriverManager.getLogWriter(); } @Override public void setLogWriter(PrintWriter logWriter) throws SQLException { DriverManager.setLogWriter(logWriter); } 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); //配置数据库连接的autoCommit和实物隔离级别 configureConnection(connection); return connection; } /** * 数据库驱动的初始化 */ 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对象 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); } } } /** * 配置数据库连接的autoCommit和实物隔离级别 * @param conn * @throws SQLException */ private void configureConnection(Connection conn) throws SQLException { if (autoCommit != null && autoCommit != conn.getAutoCommit()) { conn.setAutoCommit(autoCommit); } if (defaultTransactionIsolationLevel != null) { conn.setTransactionIsolation(defaultTransactionIsolationLevel); } } @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); } 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); } } }
package org.apache.ibatis.datasource.pooled; import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import javax.sql.DataSource; import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.sql.*; import java.util.Properties; import java.util.logging.Logger; /** * MyBatis提供了两个javax.sql.DataSource接口实现,一个是PooledDataSource另一个是UnpooledDataSource * This is a simple, synchronous, thread-safe database connection pool. *PooledDataSourceFactory-具体工厂类ConcreteFactory* PooledDataSource不会直接管理java.sql.Connection对象,而是管理PooledConnection对象,在PooledConnection * 中封装了真正的数据库连接对象以及其代理对象,这里的代理对象是通过JDK动态代理产生的。 * @author Clinton Begin */ public class PooledDataSource implements DataSource { private static final Log log = LogFactory.getLog(PooledDataSource.class); /*** 通过PoolState管理连接池的状态并记录统计信息--final修饰的对象,值可改变但引用不变*/ private final PoolState state = new PoolState(this); /** * PooledDataSource创建数据库连接的功能是依赖其中封装的UnpooledDataSource对象实现的 * 记录UnpooledDataSource对象,用于生成真是的数据库连接对象,构造函数中会初始化该字段 */ private final UnpooledDataSource dataSource; // OPTIONAL CONFIGURATION FIELDS protected int poolMaximumActiveConnections = 10; //最大活跃连接 protected int poolMaximumIdleConnections = 5; //最大空闲连接 protected int poolMaximumCheckoutTime = 20000; //最长checkout时长 protected int poolTimeToWait = 20000; //在无法获取连接是,线程需要等待的时间 protected String poolPingQuery = "NO PING QUERY SET"; //在检测一个数据库连接是否可用是,会给数据库发送一个测试SQL语句 protected boolean poolPingEnabled; //是否允许发送测试SQL语句 //当连接超过poolPingConnectionsNotUsedFor毫秒未使用时,会发送一次测试SQL语句,检测连接是否正常 protected int poolPingConnectionsNotUsedFor; //根据数据库URL、用户名和密码生成的一个hash值,该hash值用于标志着当前的连接池,在构造函数中初始化 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()); } /** * 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; } @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 int getLoginTimeout() throws SQLException { return DriverManager.getLoginTimeout(); } @Override public void setLoginTimeout(int loginTimeout) throws SQLException { DriverManager.setLoginTimeout(loginTimeout); } @Override public PrintWriter getLogWriter() throws SQLException { return DriverManager.getLogWriter(); } @Override public void setLogWriter(PrintWriter logWriter) throws SQLException { DriverManager.setLogWriter(logWriter); } public void setDefaultAutoCommit(boolean defaultAutoCommit) { dataSource.setAutoCommit(defaultAutoCommit); forceCloseAll(); } public String getDriver() { return dataSource.getDriver(); } public void setDriver(String driver) { dataSource.setDriver(driver); forceCloseAll(); } public String getUrl() { return dataSource.getUrl(); } public void setUrl(String url) { dataSource.setUrl(url); forceCloseAll(); } public String getUsername() { return dataSource.getUsername(); } public void setUsername(String username) { dataSource.setUsername(username); forceCloseAll(); } public String getPassword() { return dataSource.getPassword(); } public void setPassword(String password) { dataSource.setPassword(password); forceCloseAll(); } public boolean isAutoCommit() { return dataSource.isAutoCommit(); } public Integer getDefaultTransactionIsolationLevel() { return dataSource.getDefaultTransactionIsolationLevel(); } public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) { dataSource.setDefaultTransactionIsolationLevel(defaultTransactionIsolationLevel); forceCloseAll(); } public Properties getDriverProperties() { return dataSource.getDriverProperties(); } public void setDriverProperties(Properties driverProps) { dataSource.setDriverProperties(driverProps); forceCloseAll(); } public int getPoolMaximumActiveConnections() { return poolMaximumActiveConnections; } /** * The maximum number of active connections * @param poolMaximumActiveConnections The maximum number of active connections */ public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) { this.poolMaximumActiveConnections = poolMaximumActiveConnections; forceCloseAll(); } public int getPoolMaximumIdleConnections() { return poolMaximumIdleConnections; } /** * The maximum number of idle connections * @param poolMaximumIdleConnections The maximum number of idle connections */ public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) { this.poolMaximumIdleConnections = poolMaximumIdleConnections; forceCloseAll(); } public int getPoolMaximumCheckoutTime() { return poolMaximumCheckoutTime; } /** * 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(); } public int getPoolTimeToWait() { return poolTimeToWait; } /** * 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(); } public String getPoolPingQuery() { return poolPingQuery; } /** * The query to be used to check a connection * @param poolPingQuery The query */ public void setPoolPingQuery(String poolPingQuery) { this.poolPingQuery = poolPingQuery; forceCloseAll(); } public boolean isPoolPingEnabled() { return poolPingEnabled; } /** * 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(); } public int getPoolPingConnectionsNotUsedFor() { return poolPingConnectionsNotUsedFor; } /** * 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(); } /** * 当修改PooledDataSource的字段信息时,如用户名和密码,都会调用forceCloseAll * 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) { //同步 //从activeConnections集合中移除该PooledConnection对象 state.activeConnections.remove(conn); if (conn.isValid()) { //检测PooledConnection对象是否有效 //检测空闲连接数是否以达到上限,以及PooledConnection是否为该连接池的连接 if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) { state.accumulatedCheckoutTime += conn.getCheckoutTime(); //累积checkout时长 if (!conn.getRealConnection().getAutoCommit()) { //回滚提交事务 conn.getRealConnection().rollback(); } //为返还连接创建新的PooledConnection对象,然后添加到idleConnections集合中 PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this); state.idleConnections.add(newConn); newConn.setCreatedTimestamp(conn.getCreatedTimestamp()); newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp()); conn.invalidate(); //将原PooledConnection对象设置为无效 if (log.isDebugEnabled()) { log.debug("Returned connection " + newConn.getRealHashCode() + " to pool."); } state.notifyAll(); } else { //空闲连接数以达到上限或PooledConnection对象并不属于该连接池 state.accumulatedCheckoutTime += conn.getCheckoutTime(); //累积checkout时长 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++; } } } /** * PooledDataSource.getConnection()方法首先会调用PooledDataSource.popConnection()方法获取PooledConnection对象, * 然后通过PooledConnection.getProxyConnection()方法获取数据库连接的代理 * @param username * @param password * @return * @throws SQLException */ 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 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 创建数据库连接,并封装成PooledConnection对象 conn = new PooledConnection(dataSource.getConnection(), this); if (log.isDebugEnabled()) { log.debug("Created connection " + conn.getRealHashCode() + "."); } } else { //活跃连接数达到最大值,不可以创建新连接 // 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; //将超时连接移除activeConnections集合 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()); //将超时的PooledConnection设置为无效 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) { if (conn.isValid()) { //检测PooledConnection是否有效 if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } //配置PooledConnection的相关属性、设置connectionTypeCode、checkoutTimestamp、lastUsedTimestamp字段的属性 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 + 3)) { 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 * @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; } 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 } }
package org.apache.ibatis.datasource.pooled; import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory; /** * @author Clinton Begin */ public class PooledDataSourceFactory extends UnpooledDataSourceFactory { public PooledDataSourceFactory() { this.dataSource = new PooledDataSource(); } }PooledConnection-代理类
package org.apache.ibatis.datasource.pooled; import org.apache.ibatis.reflection.ExceptionUtil; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; /** * PooledDataSource不会直接管理java.sql.Connection对象,而是管理PooledConnection对象,在PooledConnection * 中封装了真正的数据库连接对象以及其代理对象,这里的代理对象是通过JDK动态代理产生的。 *PoolState* PooledConnection继承了InvocationHandler接口 * @author Clinton Begin */ class PooledConnection implements InvocationHandler { private static final String CLOSE = "close"; private static final Class [] IFACES = new Class []{Connection.class}; private int hashCode = 0; /** * 记录当前PooledConnection对象所在的PooledDataSource对象。该PooledConnection是从该PooledDataSource中获取的; * 当调用close()方法时会将PooledConnection放回该PooledDataSource中 */ private PooledDataSource dataSource; /*** 真正的数据库连接*/ private Connection realConnection; /*** 数据库连接的代理对象*/ private 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. * 是proxyConnection这个连接代理对象的真正代理逻辑,他会对close()方法的调用进行代理,并且在调用真正数据库连接的方法之前进行检测; *
* 也就是说,首先判断是不是close方法,如果是的话就会根据代理的逻辑将连接放入到连接池中,而不是关闭连接,如果是其他方法就直接使用 * realConnection调用具体的方法即可。 * @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(); //通过valid字段检测是否有效 } //调用真正数据库连接对象的方法 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."); } } }
package org.apache.ibatis.datasource.pooled; import java.util.ArrayList; import java.util.List; /** * 用于管理PooledConnection对象状态的组件; ** PooledDataSource中国管理的真正的数据库连接对象是由PooledDataSource中封装的UnpooledDataSource * 对象创建的,并由PoolState管理所有连接的状态。 * @author Clinton Begin */ public class PoolState { /*** 空闲的PooledConnection集合*/ protected final List
idleConnections = new ArrayList (); /*** 活跃的PooledConnection集合*/ protected final List activeConnections = new ArrayList (); protected PooledDataSource dataSource; /* * 一下是定义的一系列统计的字段 */ protected long requestCount = 0; //请求数据库连接的次数 protected long accumulatedRequestTime = 0; //获取连接的累积时间 /** * checkoutTime表示应用从连接池中取出连接,到归还连接这段时间, * accumulatedCheckoutTime记录了所有练级累积的checkoutTime时间 */ protected long accumulatedCheckoutTime = 0; /** * 当前连接长时间为归还给连接池时,会被认为该连接超时, * claimedOverdueConnectionCount记录了超时连接的个数 */ 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(); } }