MyBatis源码解析(六)——DataSource数据源模块的池型数据源

原创作品,可以转载,但是要标注出处地址:http://www.cnblogs.com/V1haoge/p/6675674.html 

1 回顾

  及同温柔被解读了MyBatis中非池型数据源的源码,非池型也便是才具有纯数据连接的数据源,他只是管理在一个数连接,这种多少源现在老少用,一般还是用池型数据源,因为单个连接的场面下,为了保险操作的科学,针对这个连续的采取要拓展协同,这样的会延宕慢系统运行速度。

  而利用池型数据源,在池子中保存有差不多个数据库连接,可以供应多单数据库访问线程同时获现成的不同之数据库连接,既保证了数码访问的安全性,也克大的升级系统的运转速度。

2 池型数据源

  现在之Java项目中几近使用池型数据源,C3P0,DBCP之类的呢都提供了池型数据源,在MyBatis中为起定义了扳平栽池型数据源PooledDataSource,这个pooled正好和之前的Configuration配置文件被配置的数据源的项目“POOLED”对应。

<dataSource type="POOLED">

2.1 池型数据源工厂

  首先我们来探池型数据源的数据源工厂:PooledDataSourceFactory

1 package org.apache.ibatis.datasource.pooled;
2 import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
3 public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
4   public PooledDataSourceFactory() {
5     this.dataSource = new PooledDataSource();
6   }
7 }

  代码很简单,内部就来一个道,用于取吃型数据源的实例。果然好简单,你想错了……

  从类结构就可以看出,这个近乎继承了UnpooledDataSourceFactory工厂类,也就是说,PooledDataSourceFactory也装有UnpooledDataSourceFactory中位列的多主意效果,最要紧继承的意义就是安装属性的法力,这个力量可用被读取到外存中的数据源配置信息保存到数码源实例中。

  这个作用前已有了介绍,这里不再赘述。下面看看PooledDataSource数据源。

  这里以介绍PooledDataSource之前需要先对MyBatis自定义之池连接辅助类进行介绍。

2.2 池型连接:PooledConnedtion

class PooledConnection implements InvocationHandler {

  不扣无明白,一看即清楚,这是一个动态代理,采用的是JDK动态代理,说明PooledConnection池连接就是为着创建一个实在连接的代理,使用连接代理来调用真实连接来进行任何操作。

  同时是代理对连接的职能拓展了扩大,将那个转化为池型连接,即池型的概念以及落实有部分不怕当斯类似吃开展,这个看似将一个不足为奇的接连包装成一个池型化的连接,使该适用于MyBatis自定义的池型数据源。当然就并无是包装器模式,明显的代办模式(动态代理模式)。

  下面为咱们来精心看看哪些将一个简易的总是包装成一个池型连接。

  首先我们来看看池型连接有怎样性?

 1   private static final String CLOSE = "close";
 2   private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
 3   private int hashCode = 0;
 4   private PooledDataSource dataSource;
 5   //真正的连接
 6   private Connection realConnection;
 7   //代理的连接
 8   private Connection proxyConnection;
 9   private long checkoutTimestamp;
10   private long createdTimestamp;
11   private long lastUsedTimestamp;
12   private int connectionTypeCode;
13   private boolean valid;

  这里我们一一介绍:

    String CLOSE =
“close”
:这个大好理解,这仅是江湖代码中待以到之一个静态字符串常量而已,表示关闭。

    Class<?>[] IFACES = new Class<?>[]
{Connection.class}
:这个跟方类似,也是凡要运用的一个静态数组常量,表示连接路,这里肯定使及了多态的概念。Connection是有连接的祖类,

    int hashCode =
0
:这是数据库连接(真实连接)的HashCode值,默认为0,表示当真实连接不在即为null时的价。

    PooledDataSource
dataSource
:池型数据源,为什么当池型连接着见面需要池型数据源实例呢?在脚你见面相它们的以,它的要紧目的或为便于调用其内部定义之一部分方法来增援完成池型连接的一些功力判断。

    Connection
realConnection
:表示真实的数据库连接,属于为摄的对象

    Connection
proxyConnection
:表示用JDK动态代理创建的代理真实连接的代办连接实例

    long
checkoutTinmestamp
:表示数据库连接为查看出底时日戳,这个用于计算具体的检出时间

    long
createdTimestamp
:表示数据库连接于创造的光阴穿,用于计算数据库连接为创造的年华

    long
lastUsedTimestamp
:表示连接为最终使的流年穿,用于计算数据库连接于最终动用的岁月

    int
connectionTypeCode
:数据库连接的类别编码,格式为:url+username+password

    boolean valid:表示连接是否可用之逻辑值

  以上的性质大多就是当原来的数据库连接的根底及举行的又装进,为那个授予更多之属性,使其化一个不折不扣的池型连接,这虽像为一个人数越过上各种衣服,装饰,学习各种文化能力,最后用其包装成一个某一方面的专业人士。呵呵,这里是当真的专业人士,真正富有专业能力的人。当然最好基本之尚是其一人口了,在池型连接着也如出一辙,最核心之自然还是这个实际连接了。

  下面看看构造器:

1   public PooledConnection(Connection connection, PooledDataSource dataSource) {
2     this.hashCode = connection.hashCode();
3     this.realConnection = connection;
4     this.dataSource = dataSource;
5     this.createdTimestamp = System.currentTimeMillis();
6     this.lastUsedTimestamp = System.currentTimeMillis();
7     this.valid = true;
8     this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
9   }

  于此构造器中要传递两独参数,分别吗:数据库连接实例与池型数据源实例,将该个别赋值给相应的性能,同时初始化其余属性的值,最要紧的或者最后一件,调用Proxy的newProxyInstance()方法来扭转代理连接实例,其参数分别吗:Connection类的类似加载器、接口数组、当前相近的实例。(这是固定格式,套路,详情而参见《代办模式的动态代理》、《java静态代理及动态代理简单分析》)

  在这动态代理实现着极度要害的还是invoke方法的兑现:

 1   @Override
 2   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 3     String methodName = method.getName();
 4     //如果调用close的话,忽略它,反而将这个connection加入到池中
 5     if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
 6       dataSource.pushConnection(this);
 7       return null;
 8     } else {
 9       try {
10         if (!Object.class.equals(method.getDeclaringClass())) {
11           // issue #579 toString() should never fail
12           // throw an SQLException instead of a Runtime
13             //除了toString()方法,其他方法调用之前要检查connection是否还是合法的,不合法要抛出SQLException
14           checkConnection();
15         }
16         //其他的方法,则交给真正的connection去调用
17         return method.invoke(realConnection, args);
18       } catch (Throwable t) {
19         throw ExceptionUtil.unwrapThrowable(t);
20       }
21     }
22   }
23 
24   private void checkConnection() throws SQLException {
25     if (!valid) {
26       throw new SQLException("Error accessing PooledConnection. Connection is invalid.");
27     }
28   }

  我们一样词词来拘禁:

   首先获得要调用方法的称呼methodName,然后对之称谓进行认证,如果是close方法吧,那么忽略关闭的兑现,转而将此连续加到连年池中(推入pushConnection),这代表在池型了多少源中,当连接不再利用后是若回到池中备用的,而非是一直叫关门销毁。

   如果调用的措施是休是close时,则率先进行该法申明处的判定,如果这法子无是根源Object类(剔除toString()方法),那么对当前的连的可用性进行判断,如果不可用(valid值为false),则委来SqlException,否则继续执行下同样步,由实连接进行方式调用。

   原理也不行简单,总的来说就是贯彻方式调用(这也是代理的目的所在),外面看起是由代理类执行方,其实里面是出于实连接类来推行措施。

2.3 池状态类:PoolState

  池状态,顾名思义,用于描述连接池的状态(或谓属性也行)的接近,这个特性不同让池连接的属性,这是由连接池的性能,这个特性是图为一切连接池的,而连续池中蕴含有限个池连接,一定要理解其中的关系。

  于这个近乎中定义了森特性,但是这些性多用于统计信息之出口,什么是统计信息吗,就是将池连接的一些音讯做一番统计并出口。所以里面要的性能并无多,如下:

 1   protected PooledDataSource dataSource;
 2   //空闲的连接
 3   protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();
 4   //活动的连接
 5   protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();
 6   //----------以下是一些统计信息----------
 7   //请求次数
 8   protected long requestCount = 0;
 9   //总请求时间
10   protected long accumulatedRequestTime = 0;
11   protected long accumulatedCheckoutTime = 0;
12   protected long claimedOverdueConnectionCount = 0;
13   protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
14   //总等待时间
15   protected long accumulatedWaitTime = 0;
16   //要等待的次数
17   protected long hadToWaitCount = 0;
18   //坏的连接次数
19   protected long badConnectionCount = 0;
20   //构造器
21   public PoolState(PooledDataSource dataSource) {
22     this.dataSource = dataSource;
23   }

  上面代码注释中讲的挺明白,第一单凡是数据源实例,这个实例在经构造器创建实例的早晚传出并赋值,然后随即两独ArrayList集合分别用于保存空闲之池连接实例与移动(使用)中之池连接实例。其余的便是一对统计信息,对于这些统计信息,不再详述,可自动翻看源码。

  这里开简单介绍:类中重新写了toString()方法,用于出口这些信,在近似中用来取这些消息的章程还是联合方法,保证线程安全性,保证得的凡没错的消息。

  下面就要:池型数据源

2.4 池型数据源:PooledDataSource

  这是一个协办的线程安全的数据库连接池。

  其实对于一个起连接池的数据源来说,针对池中数据连接的操作就是概念是池型数据源的关键所在,那么对池型连接的操作发生什么吧?

    获取池连接(推出池连接)

    收回池连接(推入池连接)

    关闭池连接

  于增长一个塘连接的可用性判断,而我们的要紧为就是集中在当下几碰。

  首先我们来瞧池型数据源拥有那些属性:

 1 public class PooledDataSource implements DataSource {
 2 
 3   private static final Log log = LogFactory.getLog(PooledDataSource.class);
 4 
 5   //有一个池状态
 6   private final PoolState state = new PoolState(this);
 7 
 8   //里面有一个UnpooledDataSource
 9   private final UnpooledDataSource dataSource;
10 
11   // OPTIONAL CONFIGURATION FIELDS
12   //正在使用连接的数量
13   protected int poolMaximumActiveConnections = 10;
14   //空闲连接数
15   protected int poolMaximumIdleConnections = 5;
16   //在被强制返回之前,池中连接被检查的时间
17   protected int poolMaximumCheckoutTime = 20000;
18   //这是给连接池一个打印日志状态机会的低层次设置,还有重新 尝试获得连接, 这些情况下往往需要很长时间 为了避免连接池没有配置时静默失 败)。
19   protected int poolTimeToWait = 20000;
20   //发送到数据的侦测查询,用来验证连接是否正常工作,并且准备 接受请求。默认是“NO PING QUERY SET” ,这会引起许多数据库驱动连接由一 个错误信息而导致失败
21   protected String poolPingQuery = "NO PING QUERY SET";
22   //开启或禁用侦测查询
23   protected boolean poolPingEnabled = false;
24   //用来配置 poolPingQuery 多次时间被用一次
25   protected int poolPingConnectionsNotUsedFor = 0;
26 
27   private int expectedConnectionTypeCode;
28     ......
29 }

  结合源码中的注释内容会:

    PoolState state = new
PoolState(this)
:拥有一个池状态属性,无可厚非,池状态正是用于描述整个连接池整体的,这里用眼前数据源实例作为参数赋予池状态形成一个免转换的实例(final修饰的作用),这里的无移指的凡是池子状态的实例是勿转移的,但无并意味着池状态被之各属性的价为未变换,这个要看池状态类中属性是哪定义之,查看源码发现,这片独集也是final修饰的,这表明这有限个集聚实例也是勿见面变换的,但就同样无法担保集合中属性值的不变性,所以,final修饰所针对的就算是不过外层,他连无见面指向那内部的概念来影响。

    UnpooledDataSource
dataSource
:拥有一个非池型数据源。可以如此说,一个池型数据源就是一个非池型数据源加上一个连接池,也就是说,池型数据源是当非池型数据源基础及腾飞使来,是因非池型为根基的。

    int poolMaximumActiveConnections =
10
:连接池中最多但有的运动总是数,这是极度充分价值,池中保留之位移总是数不克跳此价值(10单),当要跳时,在尚未空闲连接的基本功下,不克以新建连接,而是于走链接中取最老的十分连接进行动。(这个起在盛产连续时)

    int poolMaximumIdleConnections =
5
:连接池中不过多而是具有的空连接数,这是极其特别价值,池中保留的空闲连接数不可知跨越这个价(5只),当要跨时,将多来底实事求是连接直接关门,池连接置为无用。(这个产生在推入连接时)

      int poolMaximumCheckoutTime =
20000
:连接池最充分检出时间(可以知晓啊证时),如果一个一连验证时越设定值,则用以此连续装置为过(发生在产连续时)

    int poolTimeToWait =
20000
:池等待时,当用从池中获一个老是时,如果空闲连接数量为0,而移动连续的数也达成了无限老价值,那么就算对老最早得到来的连进行检讨证明(check
out),如果证明成功(即于点poolMaximumCheckoutTime限定的辰外证通过),说明是连续还地处采用状态,这时取出操作停顿,线程等待限定时间,这个范围时间纵是这个参数的运位置。

    String poolPingQuery = “NO PING QUERY
SET”
:在印证连接是否有效之时节,对数据库执行查询,查询内容吧该装置情节。整个目的就是为着摸清此数据库连接还是否能够利用(未关,并处在正常状态),这是一个侦测查询。

    boolean poolPingEnabled =
false
:这是一个开关,表示是否打开侦测查询功能,默认为false,表示关闭该功能。

    int poolPingConnectionsNotUsedFor =
0
:如果一个连连于界定的工夫内一直无给运用,那么就如指向拖欠连进行求证,以确定此连续是否处在可用状态(即开展侦测查询),这个界定的流年就是使poolPingConnectionsNotUsedFor来设定,默认值为0。

    int expectedConnectionTypeCode:连接的门类编码,这个类别编码在创造池型数据源实例的当儿会给组装,他的组装需要从数据源中获取连接的url、username、password三单价值,将那以顺序组合在一起,这个类型编码可用于分别连接路。

  上面说交了性能字段,下面就就是说说对属性之操作方法:

    state:它仅出get方法,用于获取池状态值,因为他是final的,所以无设置set方法

    dataSource:它的价值当构造器中开创并进行赋值,是final的,不设有get与set方法。

    poolMaximumActiveConnections:拥有set与get方法,可装新值,也能够取得其价值。

    poolMaximumCheckoutTime:拥有set与getMyBatis方法,可装新值,也克博得其价。

    poolTimeToWait:拥有set与get方法,可以装新值,也会博取其价值。

    poolPingQuery:拥有set与get方法,可以设置新值,也能取其价。

    poolPingEnabled:拥有set与get方法,可以设置新值,也能够得到其价值。

    poolPingConnectionsNotUsedFor:拥有set与get方法,可以设置新值,也克赢得其价。

  这里发出只注意点,在上述的每个set方法被,其中尚包针对UnpooledDataSource中的性质之set方法重复写之中,都以结尾有如此一个计:forceCloseAll()

 

1   public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
2     this.poolPingConnectionsNotUsedFor = milliseconds;
3     forceCloseAll();
4   }

 

  这是啊意思啊?让咱们来看看forceCloseAll()这个点子就是了解了:

 1   /*
 2    * Closes all active and idle connections in the pool
 3    */
 4   public void forceCloseAll() {
 5     synchronized (state) {
 6       expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
 7       //关闭所有的activeConnections和idleConnections
 8       for (int i = state.activeConnections.size(); i > 0; i--) {
 9         try {
10           PooledConnection conn = state.activeConnections.remove(i - 1);
11           conn.invalidate();
12 
13           Connection realConn = conn.getRealConnection();
14           if (!realConn.getAutoCommit()) {
15             realConn.rollback();
16           }
17           realConn.close();
18         } catch (Exception e) {
19           // ignore
20         }
21       }
22       for (int i = state.idleConnections.size(); i > 0; i--) {
23         try {
24           PooledConnection conn = state.idleConnections.remove(i - 1);
25           conn.invalidate();
26 
27           Connection realConn = conn.getRealConnection();
28           if (!realConn.getAutoCommit()) {
29             realConn.rollback();
30           }
31           realConn.close();
32         } catch (Exception e) {
33           // ignore
34         }
35       }
36     }
37     if (log.isDebugEnabled()) {
38       log.debug("PooledDataSource forcefully closed/removed all connections.");
39     }
40   }

  注释很知:关闭池中享有活动之以及空之连续。代码也老简短,就是将池状态中之有数个聚众中保存的池塘连接一切价值吗无效,所有的真是连接一切关闭。但是关键之不是这个做法,而是为什么要这样做?当我们为重新设置一个参数时,就待拿所有的连一切关闭。

  其实为格外好明:连接是在数据源完全设置总体的动静下才转的,数据源就是连生成的底子,是接二连三有的地步,当我们如果修改数据源的基础属性之时光,原有设置上发出的连年一定不再符合新的设置,需要全体推倒重来,这里就是是意思。

  下面我们来探望构造器:

 1   public PooledDataSource() {
 2     dataSource = new UnpooledDataSource();
 3   }
 4 
 5   public PooledDataSource(String driver, String url, String username, String password) {
 6     dataSource = new UnpooledDataSource(driver, url, username, password);
 7     expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
 8   }
 9 
10   public PooledDataSource(String driver, String url, Properties driverProperties) {
11     dataSource = new UnpooledDataSource(driver, url, driverProperties);
12     expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
13   }
14 
15   public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
16     dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
17     expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
18   }
19 
20   public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
21     dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
22     expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
23   }

  从者列有底构造器中可以看到,池型数据源的从来要非池型数据源,池型就是于非池型的底蕴及加上池型的概念和实现。我们以开立池型数据源实例的时候首先会创一个非池型数据源的实例并以其赋值给参数。五种植构造器也是因非池型数据源的五栽构造器而来,一一对应,只是于继四只构造器中加进了连路编码的组建。这个连续路编码适用于分别连接路的。

  下面介绍PooledDataSource中最好紧要的法门,推入(popConnection)与推出(pushConnection)方法。

  推出方法是出于getConnection()方法调用的。

 1   @Override
 2   public Connection getConnection() throws SQLException {
 3       //覆盖了DataSource.getConnection方法,每次都是pop一个Connection,即从池中取出一个来
 4     return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
 5   }
 6 
 7   @Override
 8   public Connection getConnection(String username, String password) throws SQLException {
 9     return popConnection(username, password).getProxyConnection();
10   }

  1   private PooledConnection popConnection(String username, String password) throws SQLException {
  2     boolean countedWait = false;
  3     PooledConnection conn = null;
  4     long t = System.currentTimeMillis();
  5     int localBadConnectionCount = 0;
  6 
  7     //最外面是while死循环,如果一直拿不到connection,则不断尝试
  8     while (conn == null) {
  9       synchronized (state) {
 10         if (!state.idleConnections.isEmpty()) {
 11           //如果有空闲的连接的话
 12           // Pool has available connection
 13           //删除空闲列表里第一个,返回
 14           conn = state.idleConnections.remove(0);
 15           if (log.isDebugEnabled()) {
 16             log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
 17           }
 18         } else {
 19             //如果没有空闲的连接
 20           // Pool does not have available connection
 21           if (state.activeConnections.size() < poolMaximumActiveConnections) {
 22               //如果activeConnections太少,那就new一个PooledConnection
 23             // Can create new connection
 24             conn = new PooledConnection(dataSource.getConnection(), this);
 25             if (log.isDebugEnabled()) {
 26               log.debug("Created connection " + conn.getRealHashCode() + ".");
 27             }
 28           } else {
 29               //如果activeConnections已经很多了,那不能再new了
 30             // Cannot create new connection
 31               //取得activeConnections列表的第一个(最老的)
 32             PooledConnection oldestActiveConnection = state.activeConnections.get(0);
 33             long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
 34             if (longestCheckoutTime > poolMaximumCheckoutTime) {
 35                 //如果checkout时间过长,则这个connection标记为overdue(过期)
 36               // Can claim overdue connection
 37               state.claimedOverdueConnectionCount++;
 38               state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
 39               state.accumulatedCheckoutTime += longestCheckoutTime;
 40               state.activeConnections.remove(oldestActiveConnection);
 41               if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
 42                 oldestActiveConnection.getRealConnection().rollback();
 43               }
 44               //删掉最老的连接,然后再new一个新连接
 45               conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
 46               oldestActiveConnection.invalidate();
 47               if (log.isDebugEnabled()) {
 48                 log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
 49               }
 50             } else {
 51                 //如果checkout时间不够长,等待吧
 52               // Must wait
 53               try {
 54                 if (!countedWait) {
 55                     //统计信息:等待+1
 56                   state.hadToWaitCount++;
 57                   countedWait = true;
 58                 }
 59                 if (log.isDebugEnabled()) {
 60                   log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
 61                 }
 62                 long wt = System.currentTimeMillis();
 63                 //睡一会儿吧
 64                 state.wait(poolTimeToWait);
 65                 state.accumulatedWaitTime += System.currentTimeMillis() - wt;
 66               } catch (InterruptedException e) {
 67                 break;
 68               }
 69             }
 70           }
 71         }
 72         if (conn != null) {
 73             //如果已经拿到connection,则返回
 74           if (conn.isValid()) {
 75             if (!conn.getRealConnection().getAutoCommit()) {
 76               conn.getRealConnection().rollback();
 77             }
 78             conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
 79             //记录checkout时间
 80             conn.setCheckoutTimestamp(System.currentTimeMillis());
 81             conn.setLastUsedTimestamp(System.currentTimeMillis());
 82             state.activeConnections.add(conn);
 83             state.requestCount++;
 84             state.accumulatedRequestTime += System.currentTimeMillis() - t;
 85           } else {
 86             if (log.isDebugEnabled()) {
 87               log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
 88             }
 89             //如果没拿到,统计信息:坏连接+1
 90             state.badConnectionCount++;
 91             localBadConnectionCount++;
 92             conn = null;
 93             if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
 94                 //如果好几次都拿不到,就放弃了,抛出异常
 95               if (log.isDebugEnabled()) {
 96                 log.debug("PooledDataSource: Could not get a good connection to the database.");
 97               }
 98               throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
 99             }
100           }
101         }
102       }
103 
104     }
105 
106     if (conn == null) {
107       if (log.isDebugEnabled()) {
108         log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
109       }
110       throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
111     }
112 
113     return conn;
114   }

  结合注释内容,我本着端的实施过程进行简单的叙述:

    (1)
这是个联合方法,线程安全。但是以synchronized锁同步放置到循环之中,而不是循环外的原由是以:如果将合锁放置在循环外,当多单线程执行及锁之职务,其中一个线程获得锁然后开执行循环,如果起问题造成极端循环,那么这个锁将凡直接给这线程所享有,导致其他线程永久处于等待锁的状态,程序无法推行下。而以锁放置到循环中,当多单线程来到锁之前,其中一个线程获得锁,执行循环里代码,当尽到位同样潮巡回,无论成功失败,都见面释放锁,而别线程就足以得锁进而实行。

    (2)
首先验证空闲连接集合是否也空(验证是否还有空闲连接备用),如果有空闲连接,那么直接获取之空闲连接,将之连续起闲暇连接集合中删去。

    (3)
如果无空余连接,那么就是说明活动总是集合中总是的数目是否上最特别价值(poolMaximumActiveConnections),如果不达成极端充分价值,这时,我们得一直开立一个初的池型连接(需要一个实际连接为同一个池型数据源实例作为参数)

    (4)
如果运动连续集合中之总是数目已经高达至极酷价值(poolMaximumActiveConnections),那么即使对准极早的十分活动连续(即当汇聚中排在0位的不胜连接实例)进行认证。并获其证实时间隔值(该连上等同浅记录作证时戳到当前光阴的间距),将该和池连接的极度特别说明期(poolMaximumCheckoutTime)进行比,如果前者大,说明对此连续距上同一不好记录作证时穿的时空过了限制时限,这时用这个老连接起运动总是集合中去除,并新建一个池塘连接,还以直连接所摄的真人真事连接为实在连接(实际上就是创立一个初的代办),并以一直的池连接设为无效。

    (5)
如果证明时以及展示时间较结实也说明时低于限定时限(这个范围时限的装置需要基于项目实在状况来安,或通过经历来装,确保于是时空内连接的数据库操作实施完毕,不然贸然将连接关闭会招原先的数据库操作失败),说明是连续还可能处于采用状态,这时候只有拭目以待一途,这里拿线程设置等限定秒数(poolTimeToWait),线程进入待状态,那么就是会见释放并锁,此时外线程就能得到锁来拓展实施。当前线程在等待N秒之后自动进入准备状态准备还得到锁。

    (6)
然后即便得到的连接进行判断,如果总是不也空,那么证明连接是否可用(isValid),如果连接可用则装连接路编码,并记下证明时戳(setCheckoutTimestamp)与终极一次等下时穿(setLastUsedTimestamp),这点儿单日子戳可用于计算该连的辨证时与最终一坏采用时,在前头会下到这些价值进行判定。再然后用该链接添加到活动连续集合中。

    (7)如果获之连为空,或者说并未收获到连年,则坏连接数加1,将连接置null,并验证坏连接数价,如果比当下空闲连接数量+3且非常的语,那么即使放弃获取连接,并弃来SqlException,(抛出异常与否不怕代表执行之已,这个线程将不再实行循环操作)

  下面看看推入连接方式:

 1   protected void pushConnection(PooledConnection conn) throws SQLException {
 2 
 3     synchronized (state) {
 4       //先从activeConnections中删除此connection
 5       state.activeConnections.remove(conn);
 6       if (conn.isValid()) {
 7         if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
 8             //如果空闲的连接太少,
 9           state.accumulatedCheckoutTime += conn.getCheckoutTime();
10           if (!conn.getRealConnection().getAutoCommit()) {
11             conn.getRealConnection().rollback();
12           }
13           //new一个新的Connection,加入到idle列表
14           PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
15           state.idleConnections.add(newConn);
16           newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
17           newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
18           conn.invalidate();
19           if (log.isDebugEnabled()) {
20             log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
21           }
22           //通知其他线程可以来抢connection了
23           state.notifyAll();
24         } else {
25             //否则,即空闲的连接已经足够了
26           state.accumulatedCheckoutTime += conn.getCheckoutTime();
27           if (!conn.getRealConnection().getAutoCommit()) {
28             conn.getRealConnection().rollback();
29           }
30           //那就将connection关闭就可以了
31           conn.getRealConnection().close();
32           if (log.isDebugEnabled()) {
33             log.debug("Closed connection " + conn.getRealHashCode() + ".");
34           }
35           conn.invalidate();
36         }
37       } else {
38         if (log.isDebugEnabled()) {
39           log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
40         }
41         state.badConnectionCount++;
42       }
43     }
44   }

   同样的,我们成注释内容开展解析:

    (1) 这个点子一致是一个联手方法,拥有并锁,以池状态实例为锁。

    (2)
首先我们拿手上若是推入的连年实例从走总是着除去,表示其不再处于采用状态。

    (3)
然后针对连接额可用性进行(valid)判断,如果还处在可用状态,则印证空闲连接集合中之空连接数量是否低于设置的限定值(poolMaximumIdleConnections)和时接连实例的品种编码是否跟眼前池型数据源中之连路编码一致,如果地方两接触还满足,则展开下一致步:

    (4)
新建一个池型连接实例并将该加加至空闲连接集合中,这个池型连接实例是以之前若推入的连年为根基还创设的,也就是说是本着大使推入的池型连接的实在连接重新创设一个池型连接代理(只改变他包装,实质不改动),并拿原池型连接的辰戳设置统统设置到新的连日着,保持连续的连绵,然后拿原池型连接置为无用。

    (5) 然后唤醒所有沉睡线程notifyAll()。

    (6)
如果程序(3)点吃的判定中发出一个休立(空闲连接数量上极致酷价值或者连续的花色编码不均等)那么直接以拖欠连的实连接关闭,池连接置为无用即可。

   这个方式比较简单,条理也格外亮。

下讨论池型数据源中之最终一个办法:pingConnection()

 1   protected boolean pingConnection(PooledConnection conn) {
 2     boolean result = true;
 3 
 4     try {
 5       result = !conn.getRealConnection().isClosed();
 6     } catch (SQLException e) {
 7       if (log.isDebugEnabled()) {
 8         log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
 9       }
10       result = false;
11     }
12 
13     if (result) {
14       if (poolPingEnabled) {
15         if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
16           try {
17             if (log.isDebugEnabled()) {
18               log.debug("Testing connection " + conn.getRealHashCode() + " ...");
19             }
20             Connection realConn = conn.getRealConnection();
21             Statement statement = realConn.createStatement();
22             ResultSet rs = statement.executeQuery(poolPingQuery);
23             rs.close();
24             statement.close();
25             if (!realConn.getAutoCommit()) {
26               realConn.rollback();
27             }
28             result = true;
29             if (log.isDebugEnabled()) {
30               log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
31             }
32           } catch (Exception e) {
33             log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
34             try {
35               conn.getRealConnection().close();
36             } catch (Exception e2) {
37               //ignore
38             }
39             result = false;
40             if (log.isDebugEnabled()) {
41               log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
42             }
43           }
44         }
45       }
46     }
47     return result;
48   }

  这个法吗很是概括,它的目的就是是为证实某一个连连是否任然可用,它见面吃池连接PooledConnection类中之isValid()方法调用,用于判断一个总是是否还可用。这个办法就是的确用于判断连可用与否的功能性方法。

  (1) 首先创建一个有变量result用于保存判断结果,默认为true

  (2)
然后将眼前池型连接包裹的真连接的开闭状态值的非值赋值给result(当真实连接处倒闭状态时,result值为false,当真正连接处打开状态时,result值为true),如果赋值过程出现了老大,则直拿result置false

  (3)
判断result的值,如果result值为true,则判断poolPingEnabled的价,这是侦测查询的开关,如果这个价值也true,表示被侦测查询,那么就是可履以下内容。

  (4)
判断poolPingConnectionsNotUsedFor的值是否超越等于0(这个论断的意是判定是否设置了科学的poolPingConnectionsNotUsedFor值),并且判断该连的从最后一软采取以来的辰间隔是否超越设定的poolPingConnectionsNotUsedFor值(验证该连是否到了亟需开展侦测查询的时刻,如果低于设置时间尽管无进行侦测查询)

  (5)
如果上述原则都满足,则进行相同蹩脚侦测查询,这个侦测查询就是对准是连续的一个测试查询,看看整个查询过程是否通畅,若通(没有外异常出现),则将result置为true,一旦测试过程出现了异常,则用该连的真实性连接关闭,并将result置为false

2.5 总结

  MyBatis自行实现的池型数据源简单容易亮,代码简洁明快,单功能较为简单,无法与那些比较规范的如dbcp和c3p0相抗衡,但是满足普通需要还是十足了,这里面提到到之关于池型的定义更是促进我们确实了解Java中池的兑现方式。

网站地图xml地图