Mybatis源码分析----Mybatis数据源与连接池(1)_mybatis 数据源 无限_天高任鸟飞-海阔凭鱼跃的博客-程序员秘密

技术标签: mybatis原理和源码解析  java  

对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题。本文将通过对MyBatis框架的数据源结构进行详尽的分析,并且深入解析MyBatis的连接池。

    本文首先会讲述MyBatis的数据源的分类,然后会介绍数据源是如何加载和使用的。紧接着将分类介绍UNPOOLED、POOLED和JNDI类型的数据源组织;期间我们会重点讲解POOLED类型的数据源和其实现的连接池原理。

以下是本章的组织结构:

 

  • 一、MyBatis数据源DataSource分类
  • 二、数据源DataSource的创建过程
  • 三、 DataSource什么时候创建Connection对象
  • 四、不使用连接池的UnpooledDataSource
  • 五、为什么要使用连接池?
  • 六、使用了连接池的PooledDataSource

 

一、MyBatis数据源DataSource分类

MyBatis数据源实现是在以下四个包中:

MyBatis把数据源DataSource分为三种:

        ž UNPOOLED    不使用连接池的数据源

        ž POOLED      使用连接池的数据源

        ž JNDI            使用JNDI实现的数据源

即:

相应地,MyBatis内部分别定义了实现了Java.sql.DataSource接口的UnpooledDataSource,PooledDataSource类来表示UNPOOLED、POOLED类型的数据源。 如下图所示:

对于JNDI类型的数据源DataSource,则是通过JNDI上下文中取值。

 

二、数据源DataSource的创建过程

 MyBatis数据源DataSource对象的创建发生在MyBatis初始化的过程中。下面让我们一步步地了解MyBatis是如何创建数据源DataSource的。

在mybatis的XML配置文件中,使用<dataSource>元素来配置数据源:

1.  MyBatis在初始化时,解析此文件,根据<dataSource>的type属性来创建相应类型的的数据源DataSource,即:

  • type=”POOLED”  :MyBatis会创建PooledDataSource实例
  • type=”UNPOOLED” :MyBatis会创建UnpooledDataSource实例
  • type=”JNDI”     :MyBatis会从JNDI服务上查找DataSource实例,然后返回使用

2.  顺便说一下,MyBatis是通过工厂模式来创建数据源DataSource对象的,MyBatis定义了抽象的工厂接口:org.apache.ibatis.datasource.DataSourceFactory,通过其getDataSource()方法返回数据源DataSource:

定义如下:

[java] view plain copy print?

派生到我的代码片

  1. public interface DataSourceFactory {  
  2.   
  3.   void setProperties(Properties props);  
  4.   //生产DataSource  
  5.   DataSource getDataSource();  
  6. }  

 

上述三种不同类型的type,则有对应的以下dataSource工厂:

  • POOLED        PooledDataSourceFactory
  • UNPOOLED     UnpooledDataSourceFactory
  • JNDI          JndiDataSourceFactory

   其类图如下所示:

 

3.  MyBatis创建了DataSource实例后,会将其放到Configuration对象内的Environment对象中, 供以后使用。

三、 DataSource什么时候创建Connection对象

当我们需要创建SqlSession对象并需要执行SQL语句时,这时候MyBatis才会去调用dataSource对象来创建java.sql.Connection对象。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句的时候。

比如,我们有如下方法执行一个简单的SQL语句:

[java] view plain copy print?

派生到我的代码片

  1. String resource = "mybatis-config.xml";  
  2. InputStream inputStream = Resources.getResourceAsStream(resource);  
  3. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
  4. SqlSession sqlSession = sqlSessionFactory.openSession();  
  5. sqlSession.selectList("SELECT * FROM STUDENTS");  

前4句都不会导致java.sql.Connection对象的创建,只有当第5句sqlSession.selectList("SELECT * FROM STUDENTS"),才会触发MyBatis在底层执行下面这个方法来创建java.sql.Connection对象:

[java] view plain copy print?

派生到我的代码片

  1. protected void openConnection() throws SQLException {  
  2.     if (log.isDebugEnabled()) {  
  3.       log.debug("Opening JDBC Connection");  
  4.     }  
  5.     connection = dataSource.getConnection();  
  6.     if (level != null) {  
  7.       connection.setTransactionIsolation(level.getLevel());  
  8.     }  
  9.     setDesiredAutoCommit(autoCommmit);  
  10.   }  

 

而对于DataSource的UNPOOLED的类型的实现-UnpooledDataSource是怎样实现getConnection()方法的呢?请看下一节。

四、不使用连接池的UnpooledDataSource

 

当 <dataSource>的type属性被配置成了”UNPOOLED”,MyBatis首先会实例化一个UnpooledDataSourceFactory工厂实例,然后通过.getDataSource()方法返回一个UnpooledDataSource实例对象引用,我们假定为dataSource。

使用UnpooledDataSource的getConnection(),每调用一次就会产生一个新的Connection实例对象。

UnPooledDataSource的getConnection()方法实现如下:

[java] view plain copy print?

派生到我的代码片

  1. /* 
  2. UnpooledDataSource的getConnection()实现 
  3. */  
  4. public Connection getConnection() throws SQLException  
  5. {  
  6.     return doGetConnection(username, password);  
  7. }  
  8.   
  9. private Connection doGetConnection(String username, String password) throws SQLException  
  10. {  
  11.     //封装username和password成properties  
  12.     Properties props = new Properties();  
  13.     if (driverProperties != null)  
  14.     {  
  15.         props.putAll(driverProperties);  
  16.     }  
  17.     if (username != null)  
  18.     {  
  19.         props.setProperty("user", username);  
  20.     }  
  21.     if (password != null)  
  22.     {  
  23.         props.setProperty("password", password);  
  24.     }  
  25.     return doGetConnection(props);  
  26. }  
  27.   
  28. /* 
  29.  *  获取数据连接 
  30.  */  
  31. private Connection doGetConnection(Properties properties) throws SQLException  
  32. {  
  33.     //1.初始化驱动  
  34.     initializeDriver();  
  35.     //2.从DriverManager中获取连接,获取新的Connection对象  
  36.     Connection connection = DriverManager.getConnection(url, properties);  
  37.     //3.配置connection属性  
  38.     configureConnection(connection);  
  39.     return connection;  
  40. }  

 

如上代码所示,UnpooledDataSource会做以下事情:

1.  初始化驱动:    判断driver驱动是否已经加载到内存中,如果还没有加载,则会动态地加载driver类,并实例化一个Driver对象,使用DriverManager.registerDriver()方法将其注册到内存中,以供后续使用。

2.  创建Connection对象:    使用DriverManager.getConnection()方法创建连接。

3.  配置Connection对象:    设置是否自动提交autoCommit和隔离级别isolationLevel。

4.  返回Connection对象。

 

上述的序列图如下所示:

 

总结:从上述的代码中可以看到,我们每调用一次getConnection()方法,都会通过DriverManager.getConnection()返回新的java.sql.Connection实例。

五、为什么要使用连接池?

 

1. 创建一个java.sql.Connection实例对象的代价

首先让我们来看一下创建一个java.sql.Connection对象的资源消耗。我们通过连接Oracle数据库,创建创建Connection对象,来看创建一个Connection对象、执行SQL语句各消耗多长时间。代码如下:

[java] view plain copy print?

派生到我的代码片

  1. public static void main(String[] args) throws Exception  
  2. {  
  3.   
  4.     String sql = "select * from hr.employees where employee_id < ? and employee_id >= ?";  
  5.     PreparedStatement st = null;  
  6.     ResultSet rs = null;  
  7.   
  8.     long beforeTimeOffset = -1L; //创建Connection对象前时间  
  9.     long afterTimeOffset = -1L; //创建Connection对象后时间  
  10.     long executeTimeOffset = -1L; //创建Connection对象后时间  
  11.   
  12.     Connection con = null;  
  13.     Class.forName("oracle.jdbc.driver.OracleDriver");  
  14.   
  15.     beforeTimeOffset = new Date().getTime();  
  16.     System.out.println("before:\t" + beforeTimeOffset);  
  17.   
  18.     con = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:xe", "louluan", "123456");  
  19.   
  20.     afterTimeOffset = new Date().getTime();  
  21.     System.out.println("after:\t\t" + afterTimeOffset);  
  22.     System.out.println("Create Costs:\t\t" + (afterTimeOffset - beforeTimeOffset) + " ms");  
  23.   
  24.     st = con.prepareStatement(sql);  
  25.     //设置参数  
  26.     st.setInt(1, 101);  
  27.     st.setInt(2, 0);  
  28.     //查询,得出结果集  
  29.     rs = st.executeQuery();  
  30.     executeTimeOffset = new Date().getTime();  
  31.     System.out.println("Exec Costs:\t\t" + (executeTimeOffset - afterTimeOffset) + " ms");  
  32.   
  33. }  

 

上述程序在我笔记本上的执行结果为:

从此结果可以清楚地看出,创建一个Connection对象,用了250 毫秒;而执行SQL的时间用了170毫秒

创建一个Connection对象用了250毫秒!这个时间对计算机来说可以说是一个非常奢侈的!

这仅仅是一个Connection对象就有这么大的代价,设想一下另外一种情况:如果我们在Web应用程序中,为用户的每一个请求就操作一次数据库,当有10000个在线用户并发操作的话,对计算机而言,仅仅创建Connection对象不包括做业务的时间就要损耗10000×250ms= 250 0000 ms = 2500 s = 41.6667 min,竟然要41分钟!!!如果对高用户群体使用这样的系统,简直就是开玩笑!

2. 问题分析:

 

创建一个java.sql.Connection对象的代价是如此巨大,是因为创建一个Connection对象的过程,在底层就相当于和数据库建立的通信连接,在建立通信连接的过程,消耗了这么多的时间,而往往我们建立连接后(即创建Connection对象后),就执行一个简单的SQL语句,然后就要抛弃掉,这是一个非常大的资源浪费!

 

3.解决方案:

对于需要频繁地跟数据库交互的应用程序,可以在创建了Connection对象,并操作完数据库后,可以不释放掉资源,而是将它放到内存中,当下次需要操作数据库时,可以直接从内存中取出Connection对象,不需要再创建了,这样就极大地节省了创建Connection对象的资源消耗。由于内存也是有限和宝贵的,这又对我们对内存中的Connection对象怎么有效地维护提出了很高的要求。我们将在内存中存放Connection对象的容器称之为 连接池(Connection Pool)。下面让我们来看一下MyBatis的线程池是怎样实现的。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/asdasdasd123123123/article/details/81034131

智能推荐

Caffe均值文件mean.binaryproto与mean.npy互转_zhw864680355的博客-程序员秘密

原文链接: https://blog.csdn.net/hyman_yx/article/details/51732656 https://blog.csdn.net/ZhikangFu/article/details/53260329 使用Caffe的C++接口进行操作时,需要的图像均值文件是pb格式,例如常见的均值文件名为mean...

欧拉函数模板及其性质_几许情愁的博客-程序员秘密

欧拉函数定义:对于一个整数N,小于 N 且与 N 互质的数的个数;列入 φ(8)=4,因为1,3,5,7与它互质,模板有两个,一个是直接求N的欧拉函数,一个是求1~N中所有数的欧拉函数;欧拉函数的一些性质:① N是不为0的整数。φ(1)=1(本身)。② 除了N&amp;gt;2时,φ(N)都是偶数。③ 小于N且与N互质的所有数的和是φ(n)*n/2。④ 欧拉函数是积性函数——若m,n互质,...

MSTAR的内存分配情况的介绍与查看_杭州山不高的博客-程序员秘密

2012-02-03 15:14MSTAR的内存分配情况的介绍MSTAR平台中,用于MALLOC的内存分为2块,一块叫POOL,一块叫HEAP。POOL总共有6个,POOL0~POOL5(后面简写为P0,P1,P2,P3,P4,P5),每个POOL对应的大小和数量是固定的。POOL是有很多个小块内存组合起来的大内存池,POOL的存在,可以很大限度的减少内存碎片产生。当需要分配小

grails 环境找不到java_home_weixin_34032792的博客-程序员秘密

为什么80%的码农都做不了架构师?&gt;&gt;&gt; ...

用劳斯判据计算系统几个精确的虚轴交点_劳斯判据求与虚轴交点_Ethan__D的博客-程序员秘密

用劳斯判据计算系统几个精确的虚轴交点vpa函数控制精度format longsyms Ks51=1;s52=39;s53=2*K+24;s41=11.4;s42=K+43.6;s43=4*K;s31=-det([s51,s52;s41,s42])/s41;s32=-det([s51,s53;s41,s43])/s41;s21=-det([s41,s42;s31,s32])/s31;s22=-det([s41,s43;s31,0])/s31;s11=-det([s31,s32;s

linux点亮硬盘locat,Linux中常用的查询指令(which、whereis、find、locatae)_南城北忆的博客-程序员秘密

我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令来搜索:Which 查看可执行文件的位置。whereis查看文件的位置。locate 配合数据库查看文件位置。Find 实际搜寻硬盘查询文件名称whichwhich命令的作用是,在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用which 命令,就可以看到某个系统命令...

随便推点

中山大学H3C客户端下载_JohnLanbow的博客-程序员秘密

自行转往中山大学IT服务帮助台处下载

vue-resource跨域问题_飞翔在蓝天下的蜂鸟的博客-程序员秘密

今天在项目中使用vue-resource的时候,我的vue项目开发地址是http://localhost:8080/set,但是我想获取http://localhost:80/ 域名下的数据,于是遇到了跨域问题: 我在使用vue-resource请求数据的时候是这样写的this.$http.get('http://localhost:80/picasso-wan/governance...

shape文件导入mdb文件_shape转mdb_默而识之者的博客-程序员秘密

构造mdb,并在其中构造要素数据集,将shape文件都导入该要素数据集中using System;using System.Collections.Generic;using System.IO;using System.Threading;using ESRI.ArcGIS.Geodatabase;using ESRI.ArcGIS.DataSourcesGDB;usin

Golang爬虫全攻略_空山老师的博客-程序员秘密

一、简介Golang诞生已经超过十个年头了,发展得愈发完善,其简单方便的协程并发机制使得其在爬虫领域有着一定的天赋。首先我们来看一看,Golang相对于Python这个爬虫领域的传统强者,有哪些优点和缺点。优点:完善简便的协程并发机制并发数量大占用资源少运行速度更快部署方便缺点:数据处理比较繁琐成熟工具不是很多资料较少实现相...

基于STM32F103单片机智能安全门禁人体感应报警方案设计_hcsr505连接图_jianghuxiaoge的博客-程序员秘密

硬件电路的设计(文末附资料)系统总体结构本系统具体框图如下图所示:STM32单片机核心电路设计STM32系列处理器是意法半导体ST公司生产的一种基于ARM 7架构的32位、支持实时仿真和跟踪的微控制器。选择此款控制芯片是因为本系统设计并非追求成本的最低或更小的功耗,而是在实现本设计功能的前提下能够提供更丰富的接口和功能以便于设计实验系统各实验项目所需的外围扩展电路。此款控制芯片在完成单片机课程的学习后上手较为容易,在医疗器械中应用广泛,具有很好的学习、实验研究价值。一、STM32的主要优点:

redis存储java对象_在Redis中存储大型复杂java类对象的最佳方法是什么?_weixin_39711441的博客-程序员秘密

在Redis中存储嵌套且复杂的Java对象的最佳方式是什么?我们的做法如下。在Redis中存储大型复杂java类对象的最佳方法是什么?我们使用Redisson用于Redis交互的java客户端库。请参阅下面的代码:不是放try{Config conf = new Config();conf.useSingleServer().setTimeout(3600000);conf.useSingleSe...

推荐文章

热门文章

相关标签