Mybatis数据源
在描述mybatis数据源之前,先抛出几个问题,这几个问题都能在本文得到解答
1.mybatis是如何获取到mysql连接的? 2.mybatis的Connection是怎么被创建的?
1.Datasource的分类
我们已一段mybatis的配置文件为例
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
datasource的type共有三个选项
UNPOOLED 不使用连接池的数据源 POOLED 使用连接池的数据源 JNDI 使用JNDI实现的数据源
2.Datasource的配置加载与创建
mybatis在项目启动阶段会加载配置文件,读取xml中的配置信息到Configuration中。我们看下datasource是怎么加载进来的。这段代码在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration

点进去,找到这个方法。


这里通过工厂模式,反射生成一个DatasourceFactory。
这里DataSourceFactory有三个子类,也就是上述三个datasource的类型。

3.Connection的创建
当Datasource的配置加载到configuration后。每一次执行sql都需要Datasource对象创建Connection去连接数据库。
我们以一次查询为例,看下这个connection到底是怎么生成的。



transaction对象持有connection和datasource,我们再点进去



终于看到了真正获取connection是datasource,那datasource是怎么获取connection的呢?
4.连接池
datasource有三种类型,相对应的每种datasource创建connection各不相同。常见的是POOLEDA和UNPOOLED两种类型。
UNPOOLED顾名思义就是不用连接池的方式,每次用到一个就生产一个

POOLEDA类型采用了连接池的方式,内部通过Poolstate对象来维护连接池对象

Poolstate内部有两个数组,idleConnections用来存放空闲的connection,activeConnections用来存放连接中的connection。

具体的获取一个Connection连接如下
private final PoolState state = new PoolState(this);
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) {
//这个对象锁锁的范围有点大,不过这也是因为PoolState内部的两个集合是ArrayList会产生并发问题
synchronized (state) {
//有空闲连接
if (state.idleConnections.size() > 0) {
// 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);
@SuppressWarnings("unused")
//used in logging, if enabled
Connection realConn = conn.getRealConnection();
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()) {
oldestActiveConnection.getRealConnection().rollback();
}
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
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. 先看是否有空闲(idle)状态下的PooledConnection对象,如果有,就直接返回一个可用的PooledConnection对象;否则进行第2步。
2. 查看活动状态的PooledConnection池activeConnections是否已满;如果没有满,则创建一个新的PooledConnection对象,然后放到activeConnections池中,然后返回此PooledConnection对象;否则进行第三步;
3. 看最先进入activeConnections池中的PooledConnection对象是否已经过期:如果已经过期,从activeConnections池中移除此对象,然后创建一个新的PooledConnection对象,添加到activeConnections中,然后将此对象返回;否则进行第4步。
4. 线程等待,循环2步
以上描述的是通过连接池获取connection,那在关闭connection后,参照获取连接的步骤,首先从activeConnections中移除,再判断idle数组是否已经满了,如果满了再判断下数组中第一个连接是否已经任然可用,如果可用再把这个这个连接销毁。
上文我们提到了PoolState

两个集合里面存放着PooledConnection对象,PooledConnection对象是Connection的代理类

PooledConnection相比Connection多了什么呢,答案就在invoke方法中,当connection对象调用close方法时,会调用datasource的pushConnection方法,pushConnection的方法大致和上面的猜想一致

Mybatis数据源的更多相关文章
- 《深入理解mybatis原理》 Mybatis数据源与连接池
对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文将通过对MyBatis框架的数据源结构进行详尽的分析,并且深入解析MyBatis的连接池. 本文首先会讲述MyB ...
- 《深入理解mybatis原理3》 Mybatis数据源与连接池
<深入理解mybatis原理> Mybatis数据源与连接池 对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文将通过对MyBatis框架的数据源结构 ...
- mybatis深入理解(二)-----Mybatis数据源与连接池
对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文将通过对MyBatis框架的数据源结构进行详尽的分析,并且深入解析MyBatis的连接池.本文首先会讲述MyBa ...
- mybatis数据源与连接池
1.概念介绍1.1 数据源:顾名思义,数据的来源,它包含了数据库类型信息,位置和数据等信息,一个数据源对应一个数据库. 1.2 连接池:在做持久化操作时,需要通过数据库连接对象来连接数据库,而连接池就 ...
- MyBatis 数据源的原理和机制
回顾JDBC JDBC访问数据库流程 加载驱动 获取Connection连接对象(消耗性能) 获取PrepareStatement对象 执行SQL语句 获取结果集 关闭Connection连接对象 存 ...
- 浩哥解析MyBatis源码(四)——DataSource数据源模块
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6634880.html 1.回顾 上一文中解读了MyBatis中的事务模块,其实事务操作无非 ...
- 浩哥解析MyBatis源码(五)——DataSource数据源模块之非池型数据源
1 回顾 上一篇中我解说了数据源接口DataSource与数据源工厂接口DataSourceFactory,这二者是MyBatis数据源模块的基础,包括本文中的非池型非池型数据源(UnpooledDa ...
- MyBatis源码解析(五)——DataSource数据源模块之非池型数据源
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6675633.html 1 回顾 上一篇中我解说了数据源接口DataSource与数据源工厂 ...
- MyBatis源码解析(四)——DataSource数据源模块
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6634880.html 1.回顾 上一文中解读了MyBatis中的事务模块,其实事务操作无非 ...
随机推荐
- flag:用心学习的第一天
目标是:加油学习,尽早改变世界
- 2019.03.26 bzoj4447: [Scoi2015]小凸解密码(线段树)
传送门 题意简述:咕咕咕 思路:考虑预处理出bbb数组,然后每次改动aaa都只会对第iii和i+1i+1i+1这两个位置产生影响,于是可以用线段树来维护bbb数组. 现在求答案的方法是断环为链,倍增整 ...
- js 对象与json的转化
1.将对象转换为JSON格式字符串 JSON.stringify(object) 2.将JSON字符串转换为对象 JSON.parse(jsonString);
- ORACLE 快速刷新物化视图的方法(11g)
1.on demand:用户需要刷新的时候刷新,这里就要求用户自己动手去刷新数据了(也可以使用job定时刷新) refresh [fast|complete|force] 视图刷新的方式: compl ...
- 再探haproxy
一 设置haproxy输出log 1.1 调整配置文件 默认haproxy是不会输出log到文件的,这样很大程度在查询问题时会很不方便,haproxy是可以输出日志到文件的,配置文档类似于如下: ]# ...
- Ubuntu 离线安装 docker
1.下载离线包,网址:https://download.docker.com/linux/ubuntu/dists/xenial/pool/stable/amd64/ 离线安装docker需要下载3个 ...
- mysql-exporter
[1] https://blog.frognew.com/2017/08/use-prometheus-monitoring-mysql.html [2] http://www.ywnds.com/? ...
- HDU5813 Elegant Construction
Elegant Construction Time Li ...
- 你不知道的 #include
1.#include 指令 C++的程序中带 “#” 号的语句被称为宏定义或编译指令.#include在代码中是包含和引用的意思,例如:"#include <iostream>& ...
- 调用支付宝支付(C#)
//支付宝支付 public string AliPay(string OrderID, string Total) //OrderID订单号,Total订单总金额 { // 支付宝网关 string ...