Mybatis内置数据源

今天的文章稍微没有那么肝,但是还是拖到了晚上!!哈哈哈,其实昨天已经写好了,只不过一直想不到个人唠叨应该写什么,所以就拖了一下。

内置数据源初始化过程

在详细分析 UnpooledDataSource 和 PooledDataSource 两种数据源实现之前,我们先来了解一下数据源的配置与初始化过程。现在看数据源是如何配置的,如下:

<!--使用连接池-->
<dataSource type="POOLED"><!--这里会替换为local-mysql.properties中的对应字段的值--><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/>
</dataSource>

以UnpooledDataSource为例。UnpooledDataSource,从名称上即可知道,该种数据源不具有池化特性。该种数据源每次会返回一个新的数据库连接,而非复用旧的连接。由于 UnpooledDataSource 无需提供连接池功能,因此它的实现非常简单。核心的方法有三个,分别如下:

  1. initializeDriver - 初始化数据库驱动
  2. doGetConnection - 获取数据连接
  3. configureConnection - 配置数据库连接

初始化数据库驱动

​ 回顾我们一开始学习使用 JDBC 访问数据库时的情景,在执行 SQL 之前,通常都是先获取数据库连接。一般步骤都是加载数据库驱动,然后通过 DriverManager 获取数据库连接。UnpooledDataSource 也是使用 JDBC 访问数据库的,因此它获取数据库连接的过程也大致如此,只不过会稍有不同。先来看看这个调用栈吧,

// -☆- UnpooledDataSource
private synchronized void initializeDriver() throws SQLException {// 检测缓存中是否包含了与 driver 对应的驱动实例if (!registeredDrivers.containsKey(driver)) {Class<?> driverType;try {// 加载驱动类型if (driverClassLoader != null) {// 使用 driverClassLoader 加载驱动driverType = Class.forName(driver, true, driverClassLoader);} else {// 通过其他 ClassLoader 加载驱动driverType = Resources.classForName(driver);}// 通过反射创建驱动实例Driver driverInstance = (Driver) driverType.newInstance();/** 注册驱动,注意这里是将 Driver 代理类 DriverProxy 对象注册到 DriverManager 中的,* 而非 Driver 对象本身。DriverProxy 中并没什么特别的逻辑,就不分析。*/DriverManager.registerDriver(new DriverProxy(driverInstance));// 缓存驱动类名和实例registeredDrivers.put(driver, driverInstance);} catch (Exception e) {throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);}}
}// 之所以上面有缓存相关的逻辑,是用于便面重复注册驱动

获取数据库连接

这跟JDBC有点类似

// -☆- UnpooledDataSource
public Connection getConnection() throws SQLException {return doGetConnection(username, password);
}private Connection doGetConnection(String username, String password) throws SQLException {Properties props = new Properties();if (driverProperties != null) {props.putAll(driverProperties);}if (username != null) {// 存储 user 配置props.setProperty("user", username);}if (password != null) {// 存储 password 配置props.setProperty("password", password);}// 调用重载方法return doGetConnection(props);
}private Connection doGetConnection(Properties properties) throws SQLException {// 初始化驱动initializeDriver();// 获取连接Connection connection = DriverManager.getConnection(url, properties);// 配置连接,包括自动提交以及事务等级configureConnection(connection);return connection;
}private void configureConnection(Connection conn) throws SQLException {if (autoCommit != null && autoCommit != conn.getAutoCommit()) {// 设置自动提交conn.setAutoCommit(autoCommit);}if (defaultTransactionIsolationLevel != null) {// 设置事务隔离级别conn.setTransactionIsolation(defaultTransactionIsolationLevel);}
}

上面方法将一些配置信息放入到 Properties 对象中,然后将数据库连接和 Properties 对象传给 DriverManager 的 getConnection 方法即可获取到数据库连接

PooledDataSource

内部实现了连接池技术

获取连接

先看看调用栈,入口方法和之前的UnPooled一样

public Connection getConnection() throws SQLException {// 返回 Connection 的代理对象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) {// 检测空闲连接集合(idleConnections)是否为空if (!state.idleConnections.isEmpty()) {// idleConnections 不为空,表示有空闲连接可以使用conn = state.idleConnections.remove(0);} else {/** 暂无空闲连接可用,但如果活跃连接数还未超出限制*(poolMaximumActiveConnections),则可创建新的连接*/if (state.activeConnections.size() < poolMaximumActiveConnections) {// 创建新连接conn = new PooledConnection(dataSource.getConnection(), this);} else {    // 连接池已满,不能创建新连接// 取出运行时间最长的连接PooledConnection oldestActiveConnection = state.activeConnections.get(0);// 获取运行时长long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();// 检测运行时长是否超出限制,即超时if (longestCheckoutTime > poolMaximumCheckoutTime) {// 累加超时相关的统计字段state.claimedOverdueConnectionCount++;state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;state.accumulatedCheckoutTime += longestCheckoutTime;// 从活跃连接集合中移除超时连接state.activeConnections.remove(oldestActiveConnection);// 若连接未设置自动提交,此处进行回滚操作if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {try {oldestActiveConnection.getRealConnection().rollback();} catch (SQLException e) {...}}/** 创建一个新的 PooledConnection,注意,* 此处复用 oldestActiveConnection 的 realConnection 变量*/conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);/** 复用 oldestActiveConnection 的一些信息,注意 PooledConnection 中的 * createdTimestamp 用于记录 Connection 的创建时间,而非 PooledConnection * 的创建时间。所以这里要复用原连接的时间信息。*/conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());// 设置连接为无效状态oldestActiveConnection.invalidate();} else {    // 运行时间最长的连接并未超时try {if (!countedWait) {state.hadToWaitCount++;countedWait = true;}long wt = System.currentTimeMillis();// 当前线程进入等待状态state.wait(poolTimeToWait);state.accumulatedWaitTime += System.currentTimeMillis() - wt;} catch (InterruptedException e) {break;}}}}if (conn != null) {/** 检测连接是否有效,isValid 方法除了会检测 valid 是否为 true,* 还会通过 PooledConnection 的 pingConnection 方法执行 SQL 语句,* 检测连接是否可用。pingConnection 方法的逻辑不复杂,大家可以自行分析。* 另外,官方文档在介绍 POOLED 类型数据源时,也介绍了连接有效性检测方面的* 属性,有三个:poolPingQuery,poolPingEnabled 和 * poolPingConnectionsNotUsedFor。关于这三个属性,大家可以查阅官方文档*/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 {// 连接无效,此时累加无效连接相关的统计字段state.badConnectionCount++;localBadConnectionCount++;conn = null;if (localBadConnectionCount > (poolMaximumIdleConnections+ poolMaximumLocalBadConnectionTolerance)) {throw new SQLException(...);}}}}}if (conn == null) {throw new SQLException(...);}return conn;
}

这里的代码有点复杂

稍微总结了一下:

回收连接

相比于获取连接,回收连接的逻辑要简单的多。回收连接成功与否只取决于空闲连接集合的状态,所需处理情况很少,因此比较简单。

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();}// 创建新的 PooledConnectionPooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);state.idleConnections.add(newConn);// 复用时间信息newConn.setCreatedTimestamp(conn.getCreatedTimestamp());newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());// 将原连接置为无效状态conn.invalidate();// 通知等待的线程state.notifyAll();} else {    // 空闲连接集合已满state.accumulatedCheckoutTime += conn.getCheckoutTime();// 回滚未提交的事务if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}// 关闭数据库连接conn.getRealConnection().close();conn.invalidate();}} else {state.badConnectionCount++;}}
}

回收连接的入口:

// -☆- PooledConnection
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 {if (!Object.class.equals(method.getDeclaringClass())) {checkConnection();}// 调用真实连接的目标方法return method.invoke(realConnection, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}
}

本篇文章大概就讲这么多,重点还是在对Pool和UnPool的理解,因为际场景下,我们基本不用 MyBatis 自带的数据库连接池的实现。所以,上面的流程了解即可!!!

个人唠叨

日常总结

今天下午已经把Mybatis系列全部写完!!!哈哈哈,后面会陆续发的。感觉现在的自己阅读源码越来越牛逼了(日常吹牛逼),本来计划是再阅读一遍Spring源码的,因为其实每一次阅读源码,你都会有不同的收获!!但是基于计划的原因,如果再阅读一遍的话,恐怕又要花一周的时间了。所以,今晚就特意提前进入算法的刷题了,不为啥,因为之前一直害怕算法,所以刷了一次之后,就停了一段时间了,然后又忘了,然后6月份又刷了一下链表,因为项目和期末考的原因就先放下了,现在又把算法捡起来了,这次是抱着刷刷刷的决心来的,这次一定要坚持刷,一定要坚持,坚持,坚持!!!

说说今晚刷算法的感受

今晚应该不算刷吧,算复习一下6月份刷的链表题目,8点开始复习,重新刷了一遍,才刷了8道题了,用了两个小时,而且都是在6月份做过的,仅仅用了一个月,就把做过的题目忘光光了,那平时的那些知识点更不用说了唉!所以,今晚是一个好的开始,之后一直到找到实习offer之前都要一直刷下去,先计划一下这个暑假刷算法的时间吧!每天晚上8点开始(最迟)开始刷,刷到10点左右吧,每天差不多这个时间段,题量具体而定,然后每周划出周日的晚上不刷新题目,只做复习,如果当天晚上复习不完,就留给明天晚上!!!在此立下Flag,之后的个人唠叨中,我都会稍微提一下这个Flag的情况,哈哈哈,希望大家和我一样能尽早克服对算法的恐惧!!(大佬勿理)

谈谈我大学遇到的那个她(这里先说结论,我们最后是没有在一起的)上篇(哈哈哈,留个悬念下篇再继续)

哈哈哈哈,怎么突然今晚会说这个呢?嗯,就是爸妈今晚吃饭的时候,我在跟我爸妈提到了,我的同学都准备要结婚了,哈哈哈哈哈,才20岁耶,就结婚了,卧槽,我。。。(语塞)!!我其实也一点都不羡慕啦,我跟我爸妈说,我喜欢在这段时间好好奋斗,因为这是我精力最旺盛的时候啊,真的不能留下遗憾哎,拍拖会不会有点浪费时间呢?这时候,我爸妈就说,你大学能找到合适的,当然最好,他怕我出来工作之后找不到女朋友,好像我三叔一样,虽然工资很高,也挺有钱的,但是现在30多岁还没找到老婆,所以我爸妈一直催我,在大学就赶紧找个女朋友啦!!我:我都想找啊,但我无人问津,失礼啊!!哈哈哈哈,开玩笑的

接下来就进入主题了,大学的上半阶段结束了,我在大二上确实遇到过这么一个女孩,她超可爱的,她是我部门的一个师姐!那我是什么时候开始喜欢她的呢?是在三下乡的时候,短短8天时间,回忆真的好多,先挑几张经典的图出来压压惊

哈哈哈 这个是跟大头的工作照,跟着大头哥学到了很多很多,虽然现在也不怎么拿起相机继续拍照了,但是偶尔也会拿起手机拍几张,然后修一下,留在相册里面吃灰

还有上面那位小姐姐,看看时间,哈哈哈哈,凌晨一点多的我还在工作,那时的我是兼任排版+拍照+剪视频,卧槽,现在想想都恐怖,天天排版排到凌晨2点多3点,然后早上7点多又起床了,真的太恐怖了,现在都没试过这种工作模式了!

正经合照就不发了,因为好像找不到,就随便找了一张那时候去吃东西拍的合照,嗯,本文的主角就是我(戴眼镜,男,在右边)前面那位带着白色帽子的女生!

今天就先说到这,哈哈哈,因为也快11点了,有点困了,要早点休息了!!!

欲知详情,请继续关注下篇文章(狗头。。)

晚安!!!

[Professor麦]浅谈Mybatis内置数据源相关推荐

  1. 浅谈Mybatis内置数据源

    Mybatis内置数据源 今天的文章稍微没有那么肝,但是还是拖到了晚上!!哈哈哈,其实昨天已经写好了,只不过一直想不到个人唠叨应该写什么,所以就拖了一下. 内置数据源初始化过程 在详细分析 Unpoo ...

  2. 浅谈Vue内置component组件的应用场景

    官方的说明 渲染一个"元组件"为动态组件.依 is 的值,来决定哪个组件被渲染. <!-- 动态组件由 vm 实例的属性值 `componentId` 控制 --> & ...

  3. MyBatis 源码分析 - 内置数据源

    1.简介 本篇文章将向大家介绍 MyBatis 内置数据源的实现逻辑.搞懂这些数据源的实现,可使大家对数据源有更深入的认识.同时在配置这些数据源时,也会更清楚每种属性的意义和用途.因此,如果大家想知其 ...

  4. mybatis与php,浅谈mybatis中的#和$的区别

    浅谈mybatis中的#和$的区别 发布于 2016-07-30 11:14:47 | 236 次阅读 | 评论: 0 | 来源: 网友投递 MyBatis 基于Java的持久层框架MyBatis 本 ...

  5. 「Spark从入门到精通系列」4.Spark SQL和DataFrames:内置数据源简介

    来源 |  Learning Spark Lightning-Fast Data Analytics,Second Edition 作者 | Damji,et al. 翻译 | 吴邪 大数据4年从业经 ...

  6. boolean mybatis_浅谈mybatis中SQL语句给boolean类型赋值问题

    我就废话不多说了,大家还是直接看代码吧~ SELECT m.menu_id , m.parent_id , m.`name` , 1 opens FROM menu m WHERE m.is_vali ...

  7. 浅谈mybatis优缺点

    通过上篇介绍mybatis与hibernate区别,我们已经能得出一些mybatis的优缺点,但那只是相对于hibernate的,并不全面,我来继续总结mybatis的优缺点,以便大家对于mybati ...

  8. 浅谈MyBatis中的resultMap(个人总结)

    官方文档:          mybatis – MyBatis 3 | XML 映射器https://mybatis.org/mybatis-3/zh/sqlmap-xml.html"re ...

  9. 浅谈 “ 站内信 ” 的实现

    最近一直在写站内信模块,现在想好好总结一下.和大家一起学习交流. 之前看过,AlphaThink 写的 "两年后 再议站内信".个人感觉写的很不错的.但是我们的网站没有那么多的访问 ...

最新文章

  1. 2021年春季学期-信号与系统-第八次作业参考答案-第九小题
  2. 某计算机系大二级共有5个班,2016年计算机二级考试MS Office题库及答案
  3. MCtalk对话抱抱星英语:从Diss在线英语教学乱象到回归教育本原
  4. express中get和post的区别
  5. jsoup html转义处理,jsoup解析网页出现转义符问题
  6. 苹果6系统怎么更新不了_苹果App Store登录不了怎么办_登录不了App Store的解决办法?...
  7. 顺丰不行了吗?对快递行业的深度理解
  8. 阿里启动“Buy+”计划,正式成立 VR 实验室
  9. 变速齿轮”再研究-----(转帖)
  10. 阿里云服务器租用费用
  11. 80C51单片机:3.数码管显示、封装与消隐
  12. php 生成ics文件
  13. 移远BC28_opencpu方案_pin脚分配
  14. 电竞游戏电脑推荐,台式机R13性能破界
  15. 计算机内部信号表现形式,信号是数据在传输过程中的什么的表现形式
  16. 查看是否安装了VC++ 2015-2019 Redistributeable
  17. Linux kernel: USB driver编写入门(二)
  18. scp复制文件到指定端口
  19. tensorflow RNN实例
  20. 单细胞论文记录(part9)--Spatial charting of single-cell transcriptomes in tissues

热门文章

  1. Winamp头上动土
  2. 利用selenuim以及无头浏览器爬取9酷网音乐
  3. openstack环境中安装mistral
  4. 百度公司创始人李彦宏
  5. 基于Google Maps API的地图应用开发
  6. win8服务器防火墙配置文件,win8系统更改防火墙设置的方法
  7. 如何五分钟内在Mac上安装consolas字体
  8. 江民公司网络版问题汇总二
  9. 手机横竖屏相关设置:动态 关闭横竖屏切换、指定横竖屏切换
  10. 10种网络安全攻击方式详解!