本来准备看下DruidDatasource的,发现太复杂了,还是研究一下mybatis的datasource吧
mybatis的datasource实现:无连接池实现的UnpooledDataSource,连接池实现的PooledDataSource
一、DataSource
DataSource是JDK中提供数据库库连接的顶级接口。DataSource类图
主要实现:
getConnection():获取一个数据库连接。
getLogWriter():日志
setLoginTimeout:数据源在尝试连接数据库时的最大等待时间
wrapper:包装器,例如BeanWrapper包装Bean,允许实例包装datasource。
二、UnpooledDataSource
UnpooledDataSource是一个无连接池实现的DataSource,JDBC的简单封装,开启一个连接,执行完关闭连接。
1、变量与构造方法
private ClassLoader driverClassLoader;//Driver的类加载器 private Properties driverProperties;//启动属性 private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();//驱动程序容器 private String driver;//数据库连接驱动程序 private String url;//数据库连接url private String username;//用户名 private String password;//密码 private Boolean autoCommit;//自动提交 private Integer defaultTransactionIsolationLevel;//默认事务隔离级别 static { //类加载时,扫描DriverMananger中所有的数据库连接驱动程序 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; }
2、主要实现方法
getConnection():获取数据库连接,JDBC的封装使用。
/* org.apache.ibatis.datasource.unpooled.UnpooledDataSource*/ public Connection getConnection() throws SQLException { return doGetConnection(username, password); } public Connection getConnection(String username, String password) throws SQLException { return doGetConnection(username, password); } private Connection doGetConnection(String username, String password) throws SQLException { Properties props = new Properties(); if (driverProperties != null) { //设置driverProperties文件到属性对象中 props.putAll(driverProperties); } if (username != null) { //设置username到属性对象中,注意显式配置username会覆盖driverPropertie文件中的 props.setProperty("user", username); } if (password != null) { //设置password到属性对象中中,注意显式配置password会覆盖driverPropertie文件中的 props.setProperty("password", password); } return doGetConnection(props); } private Connection doGetConnection(Properties properties) throws SQLException { //启动驱动程序 //若在registerDrivers中(已启动),直接返回 //若不在registerDrivers中,使用driverClassLoader(不为空)加载driver类,启动驱动程序 //若driverClassLoader为空,所有的类加载器,遍历加载一次,直到能加载为止 initializeDriver(); //JDBC获取连接的语句 Connection connection = DriverManager.getConnection(url, properties); //设置连接属性 //自动提交autocommit //默认事务隔离级别:defaultTransactionIsolationLevel configureConnection(connection); return connection; }
3、总结
UnpooledDataSource就是JDBC的简单封装,没有其他逻辑。
① 类似工厂方法提供一个连接方法,getConnection().
② 启动数据库连接驱动程序driver,类加载器不是我以为的线程上下文类加载器,而是遍历所有的类加载器,直到找到合适的类加载器加载成功(cathc异常)。
三、PooledDataSource
PooledDataSource是数据库连接的连接池实现。前面研究过线程池ThreadPoolExecutor实现,多个线程+队列。这里猜测应该也是用的实现差不多,使用队列实现,不同在于这里队列里的元素时connection。
1、变量与构造方法
private final PoolState state = new PoolState(this);//连接池中连接存放的容器(两个队列) private final UnpooledDataSource dataSource;//装饰者模式,PooledDataSource是一个增强的UnpooledDataSource // OPTIONAL CONFIGURATION FIELDS protected int poolMaximumActiveConnections = 10;//连接池中最大活跃连接数 protected int poolMaximumIdleConnections = 5;//连接池中最大闲置连接数 protected int poolMaximumCheckoutTime = 20000;//连接池中闲置连接的过期时间 protected int poolTimeToWait = 20000;//连接池获取连接的超时时间 protected int poolMaximumLocalBadConnectionTolerance = 3;//连接池中坏连接容忍数 protected String poolPingQuery = "NO PING QUERY SET";//暂不清 protected boolean poolPingEnabled;//暂不清 protected int poolPingConnectionsNotUsedFor;//暂不清 private int expectedConnectionTypeCode;//连接池属性集合的hashCode,("" + url + username + password).hashCode(); 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()); }
2、主要实现
① 连接池connection的容器PoolState
public class PoolState { protected PooledDataSource dataSource; protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();//闲置连接的容器 protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();//活跃连接的容器 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() { ... } }
② 队列实现
出队:popConnection()
public Connection getConnection(String username, String password) throws SQLException { return popConnection(username, password).getProxyConnection(); } 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 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; 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 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; }
入队:pushConnection()