本文需要配合代码demo一起观看更佳,源码地址。
本源码中对 mybatis代码做了详尽的注释。对mybatis源码进行了详尽的注释,且可以对项目进行install,然后在ron-man-mybatis1项目中 src/main/java/iron/man/lyf/ironmanmybatis1/run_test/MybatisQuickStart.java 进行运行 对mybatis源码进行debug查看运行过程,欢迎大家下载指正。如果您觉得帮助到您麻烦给个赞
一、概述
-
datasource下的两个包一个是不使用连接池、一个是使用mybatis提供的连接池
- 连接池:src/main/java/org/apache/ibatis/datasource/pooled
- 非连接池:src/main/java/org/apache/ibatis/datasource/unpooled
-
此dataSource是使用的工厂模式创建的。
-
抽象工厂: DataSourceFactory是抽象工厂
-
具体工厂:
pooled
和unpooled
两个包中的UnpooledDataSourceFactory
和PooledDataSourceFactory
是抽象工厂的两个实现类。PooledDataSourceFactory继承了UnPooledDataSourceFactory
。两个类基本一模一样,只不过PooledDataSourceFactory中实例化的是PooledDataSource
,UnPooledDataSourceFactory
中实例化的是UnPooledDataSource
。所以会看到在使用mybatis默认连接池的时候,却会时常调用UnPooledDataSourceFactory
的方法。实际是由于UnPooledDataSourceFactory继承了UnPooledDataSourceFactory
全部方法的缘故 -
抽象产品:工厂要生产的对象的抽象类是jdk 的 /javax/sql/DataSource
-
具体产品:
pooled
和unpooled
两个包中 的UnpooledDataSource、PooledDataSource
是jdk 的DataSource
的两个实现类
-
-
可以把DataSource 的实现类看做一个手写的jdbc连接数据库的实现类(iron-man-mybatis/iron-man-mybatis1/src/main/java/iron/man/lyf/ironmanmybatis1/run_test/JdbcDemo.java)
- 尤其
UnpooledDataSource
中,只不过是把driver、url、username、password…这些连接参数提成了成员变量,由程序从配置文件读取然后给这些变量赋值。然后就是构造器多了一点,还有一些get、set方法干扰。最后就是initializeDriver加载驱动、doGetConnection
从jdk拿去数据库链接Connection
,与JdbcDemo
的数据库连接无异。 -
PooledDataSource
会把UnpooledDataSource
放到属性中,是为了重复使用UnpooledDataSource
中的driver、url、username、password…这些字段。PooledDataSource
会存一些最大活跃连接数
、最大闲置连接数
等这些固定的值,而 PoolState 属性会存程序运行起来的一些值,譬如:空闲的连接池资源集合
、活跃的连接池资源集合
等。值得注意的是PoolState.toString()会返回当前连接池的一堆关键参数
- 尤其
二、执行流程(以mybatis的PooledDataSource连接池为例)
-
每创建一个sqlSession就会进行一次数据库连接操作。由:iron-man-mybatis/iron-man-mybatis1/src/main/java/iron/man/lyf/ironmanmybatis1/run_test/CacheTest.java#Test1LevelCache 可得知。此demo创建了两个sqlsession,进行了两次数据库连接。
-
在src/main/java/org/apache/ibatis/datasource/pooled/PooledDataSource.java 类中的
popConnection
、pushConnection
、pingConnection
方法点断点,结合demo:CacheTest.java#Test1LevelCache 即可看到连接池运行的全貌 -
popConnection
是负责在开启一个新的sqlsession需要一个数据库连接的时候弹出来一个数据库连接。其中还会操作idleConnections(空闲的连接池资源集合)
、activeConnections(活跃的连接池资源集合)
等状态。 -
pushConnection
是在关闭sqlsession,需要把这个数据库连接归还连接池时候进行调用。期间也会操作idleConnections(空闲的连接池资源集合)
、activeConnections(活跃的连接池资源集合)
等状态。 -
pingConnection
是在popConnection
时需要有一个验证当前连接是否有效,如果在mybatis 的配置文件environments
属性中配置了poolPingEnabled
为true,那么就会在pingConnection
进行一个数据库查询的操作,查询的sql是在environments
属性中配置的poolPingQuery
的sql。 -
/datasource/pooled/PooledConnection.java 是用对Connection做的一个动态代理增强。
其主要作用是:
- 保存了 jdk给的 realConnection的这个连接。
- 在执行 sqlsession.close时 在invoke方法中 增强,来在close前执行
pushConnection
方法。
三、popConnection执行流程(从连接池中拿去一个连接)
注意点:
- 第一次执行popConnection的时候空闲连接肯定为0,所以会直接创建一个连接
-
popConnection
内创建的PooledConnection
只是一个外壳,真正的connection 是PooledConnection
内部的realConnection
- 不管是怎么得到的一个连接,都会重新设置
checkoutTimestamp
为当前时间,这个值在超时判断中起决定作用 - 当一个连接超时后,需要移除一个连接时,移除的只是
PooledConnection
外壳,真正的realConnection
不会移除只是放到新创建的PooledConnection
新的外壳中。
四、pushConnection执行流程(归还连接池一个连接)
五、连接池总结:
-
当来一个连接请求会先看连接池有没有空闲的连接,有的话直接返回空闲连接。
-
如果没有空闲连接。
-
判断当前活跃的连接数是否到了最大连接数。
- 未达到则直接创建一个连接(此连接是从数据库拿一个真正的连接)
- 已达到则判断现在的活跃的连接是否有超时的连接
- 如果有超时连接。则把以前的事务回滚,把超时的连接扒掉旧的PooledConnection外壳,拿到真正的realConnection,创建一个新的PooledConnection,把realConnection放进去。
- 如果没有超时连接,那么调用wait方法阻塞等待
-
-
验证连接
-
如果验证通过,那么进一步初始化PooledConnection。并修改一些状态值
2. 如果验证不通过,即连接无效。那么badConnectionCount(累计的获取无效连接次数+1)- 如果重试次数超过阈值,则报错。