在描述mybatis数据源以前,先抛出几个问题,这几个问题都能在本文获得解答java
1.mybatis是如何获取到mysql链接的? 2.mybatis的Connection是怎么被建立的?
咱们已一段mybatis的配置文件为例mysql
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
datasource的type共有三个选项sql
UNPOOLED 不使用链接池的数据源
POOLED 使用链接池的数据源
JNDI 使用JNDI实现的数据源
mybatis在项目启动阶段会加载配置文件,读取xml中的配置信息到Configuration中。咱们看下datasource是怎么加载进来的。这段代码在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration数据库
点进去,找到这个方法。apache
这里经过工厂模式,反射生成一个DatasourceFactory。数组
这里DataSourceFactory有三个子类,也就是上述三个datasource的类型。mybatis
当Datasource的配置加载到configuration后。每一次执行sql都须要Datasource对象建立Connection去链接数据库。并发
咱们以一次查询为例,看下这个connection究竟是怎么生成的。ui
transaction对象持有connection和datasource,咱们再点进去this
终于看到了真正获取connection是datasource,那datasource是怎么获取connection的呢?
datasource有三种类型,相对应的每种datasource建立connection各不相同。常见的是POOLEDA和UNPOOLED两种类型。
UNPOOLED顾名思义就是不用链接池的方式,每次用到一个就生产一个
POOLEDA类型采用了链接池的方式,内部经过Poolstate对象来维护链接池对象
Poolstate内部有两个数组,idleConnections用来存放空闲的connection,activeConnections用来存放链接中的connection。
具体的获取一个Connection链接以下
private final PoolState state = new PoolState(this); 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) { //这个对象锁锁的范围有点大,不过这也是由于PoolState内部的两个集合是ArrayList会产生并发问题 synchronized (state) { //有空闲链接 if (state.idleConnections.size() > 0) { // 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); @SuppressWarnings("unused") //used in logging, if enabled Connection realConn = conn.getRealConnection(); 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()) { oldestActiveConnection.getRealConnection().rollback(); } conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this); 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()) { 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 + 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; }
1. 先看是否有空闲(idle)状态下的PooledConnection对象,若是有,就直接返回一个可用的PooledConnection对象;不然进行第2步。
2. 查看活动状态的PooledConnection池activeConnections是否已满;若是没有满,则建立一个新的PooledConnection对象,而后放到activeConnections池中,而后返回此PooledConnection对象;不然进行第三步;
3. 看最早进入activeConnections池中的PooledConnection对象是否已通过期:若是已通过期,从activeConnections池中移除此对象,而后建立一个新的PooledConnection对象,添加到activeConnections中,而后将此对象返回;不然进行第4步。
4. 线程等待,循环2步
以上描述的是经过链接池获取connection,那在关闭connection后,参照获取链接的步骤,首先从activeConnections中移除,再判断idle数组是否已经满了,若是满了再判断下数组中第一个链接是否已经任然可用,若是可用再把这个这个链接销毁。
上文咱们提到了PoolState
两个集合里面存放着PooledConnection对象,PooledConnection对象是Connection的代理类
PooledConnection相比Connection多了什么呢,答案就在invoke方法中,当connection对象调用close方法时,会调用datasource的pushConnection方法,pushConnection的方法大体和上面的猜测一致