一,前言

​ 连接池有很多种,最为熟悉的比如c3p0,DBCP,druid等。

​ mybatis支持三种内置的数据源类型:

  • Pooled:实现dataSource接口,并且使用了池的思想。
  • UNPooled:同样也是实现了dataSource接口,但是该类型并没有使用池的思想。
  • JDNI:采用服务器提供的JDNI技术实现的,并且在不同服务器之间获取的连接池是不一样的。

注意:如果项目不是web或者maven的war工程,则是无法使用的。比如Tomcat服务器采用的就是DBCP连接池。

​ 那么在Mybatis种如何配置数据源,如下所示:

<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>

​ 在dataSource属性中,type标签可以指定所使用的数据源类型。

二,UnPooled

​ 1,首先从获取连接的源码开始。

@Override
public Connection getConnection() throws SQLException {
return doGetConnection(username, password);
}

​ 2,接着再进入到doGetConnection()方法中:

private Connection doGetConnection(String username, String password) throws SQLException {
// 实例化一个集合
Properties props = new Properties();
if (driverProperties != null) {
props.putAll(driverProperties);
}
// 判断用户名是否为空
if (username != null) {
props.setProperty("user", username);
}
// 判断密码是否为空
if (password != null) {
props.setProperty("password", password);
}
return doGetConnection(props);
}

​ 3,此时又返回一个doGetConnection(),这是重载的另一个方法。

private Connection doGetConnection(Properties properties) throws SQLException {
// 初始化
initializeDriver();
// 获取一个连接对象
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}

​ 4,第一行代码中,调用了initializeDriver()方法。

private synchronized void initializeDriver() throws SQLException {
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
if (driverClassLoader != null) {
// 使用反射获取到连接驱动
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
// 实例化连接驱动
Driver driverInstance = (Driver)driverType.newInstance();
// 注册驱动
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}

​ 大致流程:

  • 在获取连接对象时,调用initializeDriver()方法判断是否已经注册连接驱动。
  • 完成驱动注册,使用DriverManager.getConnection获取一个连接对象。
  • 将连接对象交给configureConnection()方法,并设置自动提交事务,及事务的隔离级别。
private void configureConnection(Connection conn) throws SQLException {
if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
conn.setAutoCommit(autoCommit);
}
if (defaultTransactionIsolationLevel != null) {
conn.setTransactionIsolation(defaultTransactionIsolationLevel);
}
}

这种方式是不具备连接池的思想,如果频繁的创建和销毁连接对象,会影响程序的运行效率。

三,Pooled

​ 再来看看使用连接池思想的数据源实现。

​ 在此之前先来说说什么是连接池,连接池就是用于存储连接对象的一个容器。而容器就是一个集合,且必须是线程安全的,即两个线程不能拿到同一个连接对象。同时还要具备队列的特性:先进先出原则。

使用连接池的好处:避免频繁创建和关闭数据库连接造成的开销,节省系统资源。

​ 1,先从获取连接源码开始。

@Override
public Connection getConnection() throws SQLException {
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}

​ 2,调用popConnection()方法(有点长)。

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 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) {
// ping to server and check the connection is valid or not
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 + 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;
}

​ 分析:

​ 1,判断连接池中是否有空闲的连接对象,有则直接返回。

​ 2,如果连接池没有空闲的连接,先判断活动连接池是否小于连接池承载的最大数量,小于则再创建新的连接对象。

​ 3,但是如果连接池已经达到最大承载数量,那么在连接池中就把最先进来的连接(oldest)返回出去。

四,总结

​ 关于JDNI的使用就不分享了,因为博客不太清楚,没有去研究里面的细节实现,不过后期会对这一点进行补充。

​ 那么关于Mybatis中另外两种数据源的使用,也总结完了,其最主要的就是关于池的思想,以及使用连接池带来的好处。

​ 最后以上内容均是自主学习总结,如有不适之处欢迎留言指正。

感谢阅读!

Mybatis之连接池的更多相关文章

  1. Mybatis 的连接池技术

    我们在前面的 WEB 课程中也学习过类似的连接池技术,而在 Mybatis 中也有连接池技术,但是它采用的是自 己的连接池技术.在 Mybatis 的 SqlMapConfig.xml 配置文件中,通 ...

  2. Mybatis 打开连接池和关闭连接池性能对比

    1  创建数据库表 -- phpMyAdmin SQL Dump -- version 4.2.11 -- http://www.phpmyadmin.net -- -- Host: localhos ...

  3. Mybatis的连接池

    先总结一个原则:mytatis的连接池最大值poolMaximumActiveConnections尽量跟服务器的并发访问量持平以至于大于并发访问量. 原因:在org.apache.ibatis.da ...

  4. spring 5.x 系列第6篇 —— 整合 mybatis + druid 连接池 (代码配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...

  5. spring 5.x 系列第5篇 —— 整合 mybatis + druid 连接池 (xml配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...

  6. 阶段3 1.Mybatis_07.Mybatis的连接池及事务_6 mybatis中的事务原理和自动提交设置

    在实际的开发中,建议使用连接池的形式. JNDI的资料 H:\BaiDu\黑马传智JavaEE57期 2019最新基础+就业+在职加薪\讲义+笔记+资料\主流框架\31.会员版(2.0)-就业课(2. ...

  7. 阶段3 1.Mybatis_07.Mybatis的连接池及事务_3 mybatis连接池的分类

    2.mybatis中的连接池     mybatis连接池提供了3种方式的配置:         配置的位置:             主配置文件SqlMapConfig.xml中的dataSourc ...

  8. Mybatis数据连接池的配置---增删改查(以及遇见的问题)

    1.首先创建项目和各个文件,如图所示: 2.配置相关数据库连接 在jdbc.properties中加入 1 db.driver=com.mysql.jdbc.Driver 2 db.url=jdbc: ...

  9. mybatis 配置连接池

    <!-- 配置数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidData ...

随机推荐

  1. linux中shell内置命令和外置命令

    shell内置命令 无法通过which或者whereis去查找命令的位置 例如cd,cp这些命令是shell解释器内置的命令 当shell内置命令传入shell解释器,shell解释器通过内核获取相关 ...

  2. IoU-aware Single-stage Object Detector for Accurate Localization

    网络的结构如下: 采用FPN结构,Backbone是RetinalNet,分成了P3~P7共5个Layer,分别训练不同尺寸的Box.每个Layer对应的Head有2个分支,包括一个单独的分支用来预测 ...

  3. PMP--1.7 项目治理

    治理凌驾于管理之上 组织治理用于影响项目治理. 组织治理需要组织根据组织文化.项目类型和组织需求裁剪治理框架,适用于当前组织. 其实组织治理的内容,在项目管理初期不需要详细了解,组织治理的内容都是高层 ...

  4. Java软件工程师技能图谱

    原文链接:Java软件工程师技能图谱 最近在考虑"拥有怎样的技能才能算一名合格的java软件工程师呢?"这个问题.碰巧在github发现一个很棒的开源项目--程序员技能图谱.@Zh ...

  5. mysql必知必会--排序检索数据

    排序数据 其实,检索出的数据并不是以纯粹的随机顺序显示的.如果不排 序,数据一般将以它在底层表中出现的顺序显示.这可以是数据最初 添加到表中的顺序.但是,如果数据后来进行过更新或删除,则此顺 序将会受 ...

  6. 《手把手教你构建自己的 Linux 系统》学习笔记(5)

    交叉编译是什么? 交叉编译就是在一个系统上,编译生成另外一个系统运行的程序文件. 「硬件体系结构」和「操作系统」的关系是什么? 硬件体系结构也可以称为架构,主要是通过 CPU 的指令集来进行区分的,操 ...

  7. 逻辑卷管理(LVM)-迁移

    逻辑卷管理(LVM)-迁移 更换卷组中逻辑卷中的一块硬盘流程:1确保卷组剩余空间大于需要更换的空间(缩减或添加添加新空间)-2迁移-3从卷组删除-4删除物理卷 #移除sdc1 1.查看卷组可用空间是否 ...

  8. logback日志的基本使用

    logback的日志使用,有两种方式,可以在application.yml文件中配置,不过最常见的还是用一个单独的xml配置文件进行配置: 一.application.yml配置方式 logging: ...

  9. 安装MYSQL到Ubuntu(APT)

    运行环境 系统版本:Ubuntu 16.04.6 LTS 软件版本:MYSQL-5.7 硬件要求:无 安装过程 1.安装APT-MYSQL存储库 APT-MYSQL存储库由MYSQL官网提供.选择安装 ...

  10. 在服务器上安装并配置JDK1.8

    参考链接:https://blog.csdn.net/qq_40958000/article/details/83996912