MyBatis连接池源码分析

来源:互联网 发布:mac 无线网卡 编辑:程序博客网 时间:2024/05/29 16:05

0 概述

访问数据库之前需要先建立一个Connection,然后再执行相应sql操作。然而创建一个新的连接是比较耗时间的,因此可以需要使用连接池来复用已经创建好的连接。本文主要分析MyBatis自带的连接池源码。

1 源码分析PooledConnection

执行过程:

 SqlSession sqlSession=sqlSessionFactory.openSession();   UserInfo userInfo=  sqlSession.selectOne("test.findUserInfoById",id);   sqlSession.close();  
SqlSessionFactory打开一个连接的时候,将具体DataSource中传入,在执行具体Sql时候会通过DataSource获取连接对象。
  public SqlSession openSession() {    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);  }  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {    Transaction tx = null;    try {      final Environment environment = configuration.getEnvironment();      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);      //这里面传入DataSource      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);      final Executor executor = configuration.newExecutor(tx, execType);      return new DefaultSqlSession(configuration, executor, autoCommit);    } catch (Exception e) {      closeTransaction(tx); // may have fetched a connection so lets call close()      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }

在使用Connection 希望,在执行colse()方法的时候并不会真正的关闭掉这个连接而是存放起来复用。MyBatis采用代理模式实现的(PooledConnection 类)在invoke方法类进行了拦截。这里只给贴出了部分核心代码。从下面代码可以看到。

class PooledConnection implements InvocationHandler {  private static final String CLOSE = "close";  private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };  private int hashCode = 0;  private PooledDataSource dataSource;  private Connection realConnection;  private Connection proxyConnection;  private long checkoutTimestamp;  private long createdTimestamp;  private long lastUsedTimestamp;  private int connectionTypeCode;  private boolean valid;  /*   * Required for InvocationHandler implementation.   *   * @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 {       //如果执行的不是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);      }    }  }

2 源码分析PoolState

PoolState 类主要用于维护连接对象情况即:idleConnections以及activeConnections。

public class PoolState {    protected PooledDataSource dataSource;    /**     * 空闲的连接集合     */    protected final List<PooledConnection> idleConnections = new ArrayList<>();    /**     * 激活的连接集合     */    protected final List<PooledConnection> activeConnections = new ArrayList<>();}

3 源码分析PooledDataSource

在获取连接时候,最终会执行PooledDataSource#popConnection的方法
1.判断有没有闲置的连接对象
2.如果有闲置连接获取闲置连接,从闲置连接列表中获取连接对象,直接到9
3.如果没有闲置连接,则判断已经激活的连接数是不是小于设定的最大连接激活数
4.如果已经激活的连接数小于设定的最大连接激活数,创建一个新的连接,直接到9
5.如果已经激活的连接数不小于设定的最大连接激活数,此时不能创建新的连接
6.取出最老的连接,判断其执行的时间是否超时
7.如果超时,释放改老的的代理连接,生成新的代理连接,直接到9
8.如果没有超时,则此时只能等待(在等待时候可能会被唤醒pushConnection时候)
9.获取到连接对象后,对该连接进行校验以及一些初始化。

  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) {            //state 是PoolState,多线程环境需要同步            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) {                                    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) {                    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;    }