常见的数据源组件都实现了java.sql.DataSource接口。MyBatis也是这个套路。

从上图可以看出:MyBatis使用不同的DataSourceFactory接口实现创建不同类型的DataSource。工厂模式

DataSourceFactory

public interface DataSourceFactory {
//设置DataSoutce相关属性,一般跟在初始化完成之后void setProperties(Properties props);
//获取DataSoutce对象DataSource getDataSource();}
public class UnpooledDataSourceFactory implements DataSourceFactory {private static final String DRIVER_PROPERTY_PREFIX = "driver.";private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();protected DataSource dataSource;
//构函初始化dataSourcepublic UnpooledDataSourceFactory() {this.dataSource = new UnpooledDataSource();}@Overridepublic void setProperties(Properties properties) {Properties driverProperties = new Properties();//创建DataSource相应的MetaObjectMetaObject metaDataSource = SystemMetaObject.forObject(dataSource);//遍历for (Object key : properties.keySet()) {String propertyName = (String) key;if (propertyName.startsWith("driver.")) {//以"driver."开始的配置是对DataSource的配置保存到driverPropertiesString value = properties.getProperty(propertyName);driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);} else if (metaDataSource.hasSetter(propertyName)) {//是否有该属性的setter方法String value = (String) properties.get(propertyName);Object convertedValue = convertValue(metaDataSource, propertyName, value);metaDataSource.setValue(propertyName, convertedValue);} else {throw new DataSourceException("Unknown DataSource property: " + propertyName);}}if (driverProperties.size() > 0) {//设置datasource.driverProperties相关属性值metaDataSource.setValue("driverProperties", driverProperties);}}@Overridepublic DataSource getDataSource() {return dataSource;}private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {Object convertedValue = value;Class<?> targetType = metaDataSource.getSetterType(propertyName);if (targetType == Integer.class || targetType == int.class) {convertedValue = Integer.valueOf(value);} else if (targetType == Long.class || targetType == long.class) {convertedValue = Long.valueOf(value);} else if (targetType == Boolean.class || targetType == boolean.class) {convertedValue = Boolean.valueOf(value);}return convertedValue;}}
复制代码

UnpooledDataSource

public class UnpooledDataSource implements DataSource {private ClassLoader driverClassLoader;//加载Driver类的加载器private Properties driverProperties;//数据库连接驱动的相关配置//缓存所有已注册的数据库连接驱动private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();private String driver;//驱动名称private String url;private String username;private String password;private Boolean autoCommit;private Integer defaultTransactionIsolationLevel;//事务隔离级别static {Enumeration<Driver> drivers = DriverManager.getDrivers();while (drivers.hasMoreElements()) {Driver driver = drivers.nextElement();registeredDrivers.put(driver.getClass().getName(), driver);}}public UnpooledDataSource() {}public UnpooledDataSource(String driver, String url, String username, String password) {this.driver = driver;this.url = url;this.username = username;this.password = password;}public UnpooledDataSource(String driver, String url, Properties driverProperties) {this.driver = driver;this.url = url;this.driverProperties = driverProperties;}public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {this.driverClassLoader = driverClassLoader;this.driver = driver;this.url = url;this.username = username;this.password = password;}public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {this.driverClassLoader = driverClassLoader;this.driver = driver;this.url = url;this.driverProperties = driverProperties;}@Overridepublic Connection getConnection() throws SQLException {return doGetConnection(username, password);}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return doGetConnection(username, password);}@Overridepublic void setLoginTimeout(int loginTimeout) throws SQLException {DriverManager.setLoginTimeout(loginTimeout);}@Overridepublic int getLoginTimeout() throws SQLException {return DriverManager.getLoginTimeout();}@Overridepublic void setLogWriter(PrintWriter logWriter) throws SQLException {DriverManager.setLogWriter(logWriter);}@Overridepublic PrintWriter getLogWriter() throws SQLException {return DriverManager.getLogWriter();}public ClassLoader getDriverClassLoader() {return driverClassLoader;}public void setDriverClassLoader(ClassLoader driverClassLoader) {this.driverClassLoader = driverClassLoader;}public Properties getDriverProperties() {return driverProperties;}public void setDriverProperties(Properties driverProperties) {this.driverProperties = driverProperties;}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);}private Connection doGetConnection(Properties properties) throws SQLException {initializeDriver(); //1.初始化驱动//2.从DriverManager中获取连接,获取新的Connection对象Connection connection = DriverManager.getConnection(url, properties);configureConnection(connection);//3.配置autoCommit和隔离级别return connection;}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);}//创建Driver对象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);}}}private void configureConnection(Connection conn) throws SQLException {if (autoCommit != null && autoCommit != conn.getAutoCommit()) {conn.setAutoCommit(autoCommit);//设置事务}if (defaultTransactionIsolationLevel != null) {//设置隔离级别conn.setTransactionIsolation(defaultTransactionIsolationLevel);}}private static class DriverProxy implements Driver {private Driver driver;DriverProxy(Driver d) {this.driver = d;}@Overridepublic boolean acceptsURL(String u) throws SQLException {return this.driver.acceptsURL(u);}@Overridepublic Connection connect(String u, Properties p) throws SQLException {return this.driver.connect(u, p);}@Overridepublic int getMajorVersion() {return this.driver.getMajorVersion();}@Overridepublic int getMinorVersion() {return this.driver.getMinorVersion();}@Overridepublic DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {return this.driver.getPropertyInfo(u, p);}@Overridepublic boolean jdbcCompliant() {return this.driver.jdbcCompliant();}// @Override only valid jdk7+public Logger getParentLogger() {return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {throw new SQLException(getClass().getName() + " is not a wrapper.");}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}// @Override only valid jdk7+public Logger getParentLogger() {// requires JDK version 1.6return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}}
复制代码

PooledDataSource

依赖的组件

PooledDataSource并不会直接管理connection对象,而是管理PooledConnection对象。PooledConnection中封装真正的连接对象以及其代理对象(通过jdk代理)

# PooledDataSource
//通过PoolState管理池状态并记录统计信息private final PoolState state = new PoolState(this);
//生成真正的连接对象,构造函数中初始化该字段private final UnpooledDataSource dataSource;protected int poolMaximumActiveConnections = 10;//最大活跃连接protected int poolMaximumIdleConnections = 5;//最大空闲连接protected int poolMaximumCheckoutTime = 20000;//最大checkout时长protected int poolTimeToWait = 20000;//无法获取链接时,线程要等待的时间protected int poolMaximumLocalBadConnectionTolerance = 3;
//检测连接是否可用,会发送一个测试SQL语句protected String poolPingQuery = "NO PING QUERY SET";protected boolean poolPingEnabled;//是否允许发送测试SQL语句
//当连接超过poolPingConnectionsNotUsedFor毫秒未用时,发送一次测试SQL,检测连接是否正常protected int poolPingConnectionsNotUsedFor;private int expectedConnectionTypeCode;
//获取链接过程public Connection getConnection() throws SQLException {return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();//得到连接后,返回代理。(这个是在初始化PooledConnection时,生成的代理)}// 返回可用的PooledConnectionprivate 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()) {// 连接池中有空闲连接,取出第一个conn = state.idleConnections.remove(0);} else {//  // 连接池中没有空闲连接,则取当前正在使用的连接数小于最大限定值if (state.activeConnections.size() < poolMaximumActiveConnections) {//  // 创建一个新的connection对象conn = new PooledConnection(dataSource.getConnection(), this);if (log.isDebugEnabled()) {log.debug("Created connection " + conn.getRealHashCode() + ".");}} else {//  当活动连接池已满,不能创建时,取出活动连接池的第一个,即最先进入连接池的PooledConnection对象// 计算它的校验时间,如果校验时间大于连接池规定的最大校验时间,则认为它已经过期了,// 利用这个PoolConnection内部的realConnection重新生成一个PooledConnectionPooledConnection oldestActiveConnection = state.activeConnections.get(0);long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();if (longestCheckoutTime > poolMaximumCheckoutTime) {// Can claim overdue connectionstate.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 {//如果不能释放,则必须等待有try {if (!countedWait) {state.hadToWaitCount++;countedWait = true;}long wt = System.currentTimeMillis();state.wait(poolTimeToWait);state.accumulatedWaitTime += System.currentTimeMillis() - wt;} catch (InterruptedException e) {break;}}}}  //如果获取PooledConnection成功,则更新其信息if (conn != null) {// ping to server and check the connection is valid or notif (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("PooledDataSource: Could not get a good connection to the database.");}}}}}
####返回连接//通过PooledConnection.invoke()方法我们知道,调用连接的代理的close方法时,调用的是//PooledDataSource.pushConnectioin方法,将PooledConnction归还给连接池,供之后重用。protected void pushConnection(PooledConnection conn) throws SQLException {synchronized (state) {//同步state.activeConnections.remove(conn);//从活跃集合中移除该PooledConnection对象if (conn.isValid()) {//检测PooledConnection是否有效if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {//空闲连接是否达到上限,以及连接是否为该连接池连接state.accumulatedCheckoutTime += conn.getCheckoutTime();//累积checkout时长if (!conn.getRealConnection().getAutoCommit()) {//回滚未提交的事务conn.getRealConnection().rollback();}//为返还的连接创建新的PooledConnection对象PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);state.idleConnections.add(newConn);//添加到idle集合newConn.setCreatedTimestamp(conn.getCreatedTimestamp());newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());conn.invalidate();//将原pooledConnecion设为无效state.notifyAll();//唤醒阻塞等待的线程} else {//空闲连接到达上限或pooledConnection对象不属于该池state.accumulatedCheckoutTime += conn.getCheckoutTime();//累积checkout时长if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}conn.getRealConnection().close();//关闭真正的连接conn.invalidate();//将PooledConncetion设为无效}} else {state.badConnectionCount++;//统计无效PooledConnection对象个数}}}//检测有效性的方法public boolean isValid() {return valid && realConnection != null&& dataSource.pingConnection(this);//检测真正连接是否有用}//执行测试SQLprotected boolean pingConnection(PooledConnection conn) {boolean result = true;//记录ping操作是否成功try {result = !conn.getRealConnection().isClosed();//检测真正的连接是否关闭} catch (SQLException e) {result = false;}if (result) {if (poolPingEnabled) {//是否执行SQL语句if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {//长时间未使用的连接,才需要ping来检测是否正常try {//执行测试语句Connection realConn = conn.getRealConnection();try (Statement statement = realConn.createStatement()) {statement.executeQuery(poolPingQuery).close();}if (!realConn.getAutoCommit()) {realConn.rollback();}result = true;} catch (Exception e) {try {conn.getRealConnection().close();} catch (Exception e2) {//ignore}result = false;}}}}return result;}
复制代码

PooledConnection核心字段:

 //当前PooledConnection所属的PooledDataSource。private final PooledDataSource dataSource;private final Connection realConnection;//真正的数据连接private final Connection proxyConnection;//连接的代理private long checkoutTimestamp;//从池中取出该连接的时间private long createdTimestamp;//该连接的创建时间private long lastUsedTimestamp;//最后一次使用时间private int connectionTypeCode;//url 用户名密码得到的hash,标示连接所在的连接池private boolean valid;//检测当前连接是否有效。主要是防止程序通过close()方法将连接归还池后,依然通过该连接操作数据库
//当调用关闭的时候,回收此Connection到PooledDataSource中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())) {//通过valid字段检测连接是否有效checkConnection();}//调用真正数据库对象的对应方法return method.invoke(realConnection, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}}//池和连接的理解https://www.cnblogs.com/panxuejun/p/6403760.html
//http://shift-alt-ctrl.iteye.com/blog/1967020
//数据库连接池在初始化时,会创建一定数量的连接放在池中备用。
//当池中连接全部用完,请求就会进入阻塞队列等待。
复制代码

PoolState是用于管理PooledConnection对象状态的组件,通过两个ArrayList<>管理空闲和活跃两种状态。

protected final List<PooledConnection> idleConnections = new ArrayList<>();//空闲protected final List<PooledConnection> activeConnections = new ArrayList<>();//活跃//统计的字段protected long requestCount = 0;//请求连接次数protected long accumulatedRequestTime = 0;//获取连接的累积时间protected long accumulatedCheckoutTime = 0;//所有连接累积CheckoutTime时长protected long claimedOverdueConnectionCount = 0;//超时连接个数protected long accumulatedCheckoutTimeOfOverdueConnections = 0;//累积超时时间protected long accumulatedWaitTime = 0;//累积等待时间protected long hadToWaitCount = 0;//等待次数protected long badConnectionCount = 0;//无效的连接数
复制代码

转载于:https://juejin.im/post/5bdbf5c56fb9a049bc4c1626

MyBatis--连接池模块相关推荐

  1. java day55【 Mybatis 连接池与事务深入 、 Mybatis 的动态 SQL 语句、 Mybatis 多表查询之一对多 、 Mybatis 多表查询之多对多】...

    第1章 Mybatis 连接池与事务深入 1.1 Mybatis 的连接池技术 1.1.1 Mybatis 连接池的分类 1.1.2 Mybatis 中数据源的配置 1.1.3 Mybatis 中 D ...

  2. Mybatis 连接池POOLED

    1.连接池: 我们在实际开发中都会使用连接池. 因为它可以减少我们获取连接所消耗的时间. 2.mybatis中的连接池 在 Mybatis 中也有连接池技术,但是它采用的是自己的连接池技术. 在 My ...

  3. Mybatis连接池介绍与分类 Mybatis使用POOLED UNPOOLED配置连接池的原理分析

    一.连接池 1.概念:其实就是一个容器(集合),存放数据库连接的容器. 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象 ...

  4. mybatis连接池的3种配置方式(POOLED,UNPOOLED,JNDI)

    mybatis连接池的3种配置方式 1,POOLED 采用传统的javax. sql. DataSource规范中的连接池.一旦数据库操作完成,mybaties会将此连接返回给连接池.mybatis有 ...

  5. mybatis 连接池_应用框架之Mybatis数据源和连接池

    本文将从以下几个方面介绍Mybatis的数据源和连接池: MyBatis数据源DataSource分类 数据源DataSource的创建过程 DataSource什么时候创建Connection对象 ...

  6. 【Java从0到架构师】MyBatis - 连接池、分页_PageHelper

    连接池.分页 集成 druid 连接池 读取外部的配置文件 分页查询 - PageHelper PageHelper 环境配置 PageHelper 使用方法 Java 从 0 到架构师目录:[Jav ...

  7. mybatis 连接池POOLED分析

    mybatis提供了三种连接池的配置方式: 配置的位置:主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式. type属性的取值: POOL ...

  8. Mybatis——连接池

    一.概述 在实际开发中都会使用连接池,因为连接池可以减少获取连接所消耗的时间. 队列特性:先进先出,正好可以确保拿到的连接对象就是刚刚自己创建的 二.mybatis中的连接池 配置的位置 主配置文件中 ...

  9. Mybatis中连接池介绍

    连接池:我们在实际开发中都会使用连接池.以为它可以减少我们获取连接所消耗的时间mybatis中的连接池mybatis连接池提供了三种方式的配置,配置位置主配置文件SqlMapConfig.xml中的d ...

  10. mybatis 配置 mysql连接池_mybatis数据库连接池配置

    mybatis学习笔记之学习目录(1) mybatis学习笔记之学习结构(1) 学习结构: 1.mybatis开发方法 原始dao开发方法(程序需要编写dao接口和dao实现类) mybatis的ma ...

最新文章

  1. 2020年AI将会如何发展?吴恩达、周志华、Yann LeCun等大神对2020年 AI 发展趋势的预测的预测...
  2. 浅谈 JSON.stringify 方法
  3. element表格实现树形全选_vue+element UI实现树形表格带复选框的示例代码
  4. 快递下单后取消订单_网约车定位地点不动,男子别的平台下单,没取消订单要付6.6元...
  5. RTUILabel+正则表达式
  6. python程序实现excel排序_python初学—-实现excel里面读数据进行排序(改进算法)
  7. npm收录了哪些包_手把手教你制作一个小而美丽的 npm 包并发布
  8. javascript回调函数及推论
  9. ctf xor题_CTF下的命令执行
  10. 第三次作业_201731062533
  11. Hyper-V Windows 8.1 Windows Server 2012 R2 QA
  12. 联想集团大裁员:“公司不是家” 和 “柳传志的回应”(
  13. 【论文】Learning by Abstraction: The Neural State Machine
  14. pose_subscriber.cpp
  15. 戴维解惑——用iTestin云测做测试怎样拿大奖
  16. ListView 实现阻尼回弹效果 并去除边缘阴影
  17. BZOJ4598: [Sdoi2016]模式字符串
  18. 洛谷 P1008 三连击 题解
  19. 重庆卫生副高考试成绩查询2021,2021年重庆卫生资格成绩查询时间及查分入口【6月4日起】...
  20. 如何理解goto语句

热门文章

  1. webpack入门--前端必备
  2. LaTeX 笔记:NFSS 那点事儿
  3. OpenCV-Python教程(6)(7)(8): Sobel算子 Laplacian算子 Canny边缘检测
  4. 条件随机场(Conditional random fields,CRFs)文献阅读指南
  5. 计算机视觉Computer Vision的尴尬---by林达华
  6. boost源码剖析之:多重回调机制signal(下)
  7. Machine Learning week 3 quiz : Regularization
  8. JavaScript 设计模式之观察者模式与发布订阅模式
  9. Kali Linux
  10. 《数据结构与抽象:Java语言描述(原书第4版)》一2.1.4 让实现安全