在Mybatis的配置中,如果采用连接池的方式管理数据库连接池,那么就会开启数据库连接池。

采用连接池的数据源PooledDataSource实则是非连接池数据源UnpooledDtaSource的包装类。

public PooledDataSource() {dataSource = new UnpooledDataSource();
}

其中,数据库连接池的状态由PoolState来保管,其中最重要的是其存在两个存放数据库连接的数组。

protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();
protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();

顾名思义,一个则是空闲的数据库连接,另一个则是正在使用中的数据库连接,数据库连接池所管理的连接,就是保存在这两个数组当中。

通过getConnection()方法获取数据库连接,但在其get方法中,首先还是会通过popConnection()方法从连接池中获取连接。

@Override
public Connection getConnection() throws SQLException {return popConnection(dataSource.getUsername(), dataSource.getPassword()).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 connectionconn = state.idleConnections.remove(0);if (log.isDebugEnabled()) {log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");}} else {// Pool does not have available connectionif (state.activeConnections.size() < poolMaximumActiveConnections) {// Can create new connectionconn = new PooledConnection(dataSource.getConnection(), this);if (log.isDebugEnabled()) {log.debug("Created connection " + conn.getRealHashCode() + ".");}} else {// Cannot create new connectionPooledConnection oldestActiveConnection = state.activeConnections.get(0);long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();if (longestCheckoutTime > poolMaximumCheckoutTime) {// Can claim overdue connectionstate.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 followingstatement like nothing happend.Wrap the bad connection with a new PooledConnection, this will helpto not intterupt current executing thread and give current thread achance to join the next competion for another valid/good databaseconnection. 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 waittry {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 notif (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;
}

这个方法中,可以清楚看到整个数据库连接池的逻辑了。

首先,判断存放空闲数据库连接的数组是否为空,如果不为空 ,则说明此时已经存在闲置的数据库连接,那么可以直接返回该数组的第一个连接。

但是,如果此时的数据库连接池并没有空闲的连接,那么就需要重新建立一个新的数据库连接。但这样做的前提是此时数据库连接池中的连接数量小于规定的最大连接数量。

所以,一旦,因为此时的连接数量已经不小于当前规定的最大连接数量,那么就会从存放正在运行的数据库连接的数组中取第一个连接,根据当前时间计算得到已经运行时间,与固定的最长时间进行比较,如果超过,则将其移出,回滚其事务,建立一个新的数据库连接(但数据库的链接还是原来那个被判断超时的那个数据库连接),放入活跃的数据库连接池。

数据库连接池中的连接由PooledConnection来表示。

public PooledConnection(Connection connection, PooledDataSource dataSource) {this.hashCode = connection.hashCode();this.realConnection = connection;this.dataSource = dataSource;this.createdTimestamp = System.currentTimeMillis();this.lastUsedTimestamp = System.currentTimeMillis();this.valid = true;this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
}

可以看到,在其构造方法中除了一些常规的参数设置之外,还通过jdk生成了一个代理类来保存。

回到最一开始的getConnection(),也可以清楚地看到,在通过popConnection()得到PooledConnection之后,最后得到的还是其代理连接。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {dataSource.pushConnection(this);return null;} else {try {if (!Object.class.equals(method.getDeclaringClass())) {// issue #579 toString() should never fail// throw an SQLException instead of a RuntimecheckConnection();}return method.invoke(realConnection, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}
}

以上就是PooledConnection中实现的invoke()方法,这里会检查代理类所要调用的方法名。如果对应的方法是close方法,那么直接会调用连接池的pushConnection()方法。其他的方法,则会直接调用数据库连接来完成相应的操作。

protected void pushConnection(PooledConnection conn) throws SQLException {synchronized (state) {state.activeConnections.remove(conn);if (conn.isValid()) {if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {state.accumulatedCheckoutTime += conn.getCheckoutTime();if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);state.idleConnections.add(newConn);newConn.setCreatedTimestamp(conn.getCreatedTimestamp());newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());conn.invalidate();if (log.isDebugEnabled()) {log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");}state.notifyAll();} else {state.accumulatedCheckoutTime += conn.getCheckoutTime();if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}conn.getRealConnection().close();if (log.isDebugEnabled()) {log.debug("Closed connection " + conn.getRealHashCode() + ".");}conn.invalidate();}} else {if (log.isDebugEnabled()) {log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");}state.badConnectionCount++;}}
}

在关闭连接的时候,会先从活跃的数据库连接数组当中移出,之后根据规定的空闲数据库连接大小放入空闲数据库连接大小,这里重新生成了一个PooledConnection,但是其中数据库的连接还是原来的,并没有数据库连接发生变化。

Mybatis的数据库连接池相关推荐

  1. druid连接池初始化慢_从零开始手写 mybatis (三)jdbc pool 从零实现数据库连接池

    前景回顾 第一节 从零开始手写 mybatis(一)MVP 版本 中我们实现了一个最基本的可以运行的 mybatis. 第二节 从零开始手写 mybatis(二)mybatis interceptor ...

  2. MyBatis 简介、 环境搭建、数据库连接池、查询方式

    七.MyBatis 简介 Mybatis 开源免费框架.原名叫 iBatis,2010 在 google code,2013 年迁移到 github 作用: 数据访问层框架. 2.1 底层是对 JDB ...

  3. spring boot、mybatis集成druid数据库连接池,实现mysql cluster HA负载均衡访问

    spring boot.mybatis集成druid数据库连接池,实现mysql cluster HA负载均衡访问 1.原理实现介绍 本质来说使用连接池是为了节省创建.关闭数据库连接的资源消耗,从而提 ...

  4. 使用Druid数据库连接池整合MyBatis Plus时,出现Error attempting to get column 'startTime' from result set. 类似错误

    问题描述: 使用Druid数据库连接池整合MyBatis Plus3.2.0时,出现Error attempting to get column 'startTime' from result set ...

  5. ORM框架之Mybatis(二)数据库连接池、事务及动态SQL

    一.MybatisMybatis连接池与事务深入  1.1 Mybatis的连接池技术  在Mybatis中也有连接池技术,但是它采用的是自己的连接池技术.在Mybatis的SqlMapConfig. ...

  6. mybatis数据库连接池介绍和源码剖析

    mybatis数据库连接池分析介绍 一.数据库连接池 1.定义 数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时 ...

  7. mybatis数据库连接池配置

    配置可修改参数db.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/*XXX(数据库 ...

  8. mybatis数据库连接池

    mybatis作为一个ORM框架,在进行数据库操作时需要和数据库连接池连接,支持基于数据库连接池的连接创建方式. 目录 1.使用自带连接池 2.使用第三方数据库连接池 2.1添加依赖 2.2在util ...

  9. mybatis 配置 mysql连接池_mybatis数据库连接池配置

    mybatis学习笔记之学习目录(1) mybatis学习笔记之学习结构(1) 学习结构: 1.mybatis开发方法 原始dao开发方法(程序需要编写dao接口和dao实现类) mybatis的ma ...

最新文章

  1. android fragmentpageradapter切换不更新,关于android:在FragmentPagerAdapter中更新当前片段...
  2. [翻译] NMock 简介
  3. 关于数据科学,书上不曾提及的三点经验
  4. 如何避免fstab挂载故障问题
  5. StringBuilder的使用
  6. sql server 创建唯一性非聚集索引语句_数据库专题—索引原理
  7. QT+OpenCV综合示例:图像混合(滑动条)
  8. C/C++ _strlwr_s 函数 – 字符串大写转小写- C语言零基础入门教程
  9. java文件 linux_Linux执行Java文件
  10. java判断对象无数据_java 对象属性不能为空判断
  11. Java通过反射访问构造方法
  12. 数据库SQL语言类型(DQL.DML.DDL.DCL)
  13. 【zookeeper】Zookeeper:ZooInspector界面工具
  14. python项目代码总结
  15. 中等职业教育计算机,计算机应用基础:Windows7+Office2010(中等职业教育规划教材)...
  16. c语言马拉松试题,C语言马拉松_04.2_函数与指针
  17. php 操作xls,php中使用PHPExcel操作excel(xls)文件
  18. 鸿蒙如何连接电视,鸿蒙系统首秀,在自家设备上和普通电视大不相同
  19. Django-天天生鲜项目-用户登录
  20. java 规范 阿里巴巴_阿里巴巴 Java 代码规范

热门文章

  1. Python学习笔记之While循环(一)
  2. ProtoBuf3语法指南(Protocol Buffers)_下
  3. basler相机的触发线是那两脚_探究机器视觉领域线扫相机和面阵相机的区别
  4. python中的pyinstaller库_Python(00):PyInstaller库,打包成exe基本介绍
  5. argmax函数_Python科学计算库numpy——统计函数
  6. JavaSE——MD5、16位流
  7. npm install
  8. RestTemplate.getForObject返回List的时候处理方式
  9. SqlServer中使用Mysql中的limit分页功能
  10. 流程 - 发布【敏捷方法之Scrum v0.2.pdf】