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; }
阅读全文
1 0
- MyBatis连接池源码分析
- mybatis源码分析——连接池
- mybatis源码分析----数据库连接池----连接池概述
- Mybatis源码分析之SqlSessionFactory,SqlSession和连接池
- 【Mybatis源码分析】Mybatis源码分析-数据库连接池
- XAPool连接池源码分析
- HTablePool 连接池源码分析
- XAPool连接池源码分析
- 【Mybatis】mybatis插件源码分析
- MyBatis 3源码分析
- MyBatis源码分析
- mybatis源码分析
- Mybatis原理源码分析
- Mybatis源码 select分析
- Mybatis源码分析
- MyBatis源码分析
- Mybatis源码分析--MapperScannerConfigurer
- Mybatis源码分析系列
- oracle中DBMS_RANDOM各函数使用
- linux i2c驱动讲解
- 一、玩转物联网--Nginx服务器搭建
- python fabric 库的学习使用
- Android自定义View:MeasureSpec的真正意义与View大小控制
- MyBatis连接池源码分析
- java字符串查找算法
- charles弱网测试
- [学习笔记] 拓展欧几里得算法
- L1-019. 谁先倒
- [线代小记] 树形图求和
- Linux中环境变量的作用
- solr6的学习与使用(五):联想词功能
- win10环境下实现python3.5与python2.7共存