mysql数据库连接池使用(二)实现自己的数据库连接池
上一个章节,我们讲了xml文件的解析框架XMLConfiguration的使用,不懂的可以参考
Apache Commons Configuration读取xml配置具体使用。
这个章节主要实现自己的数据库连接池,封装自己的BasicDataSource类。实现自己业务的数据池。下面开始我们的项目构建。
1.1.1. maven依赖。
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.1.1</version> <exclusions> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>commons-configuration</groupId> <artifactId>commons-configuration</artifactId> <version>1.8</version> </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>commons-jxpath</groupId> <artifactId>commons-jxpath</artifactId> <version>1.3</version> </dependency>
1.1.2. 配置文件
数据库采用读写分离,所以定义了2个数据源配置,配置文件da2s.xml存放在src根目录下具体的配置如下:
<?xml version="1.0" encoding="UTF-8"?> <da2s-configuration> <DefaultConnectionPool>3000</DefaultConnectionPool> <connectionPool name="3000"> <dbtype>MYSQL</dbtype> <driverClassName>com.mysql.jdbc.Driver</driverClassName> <url>jdbc:mysql://localhost:3306/springok</url> <username>root</username> <password></password> <datasourceProperty> <defaultAutoCommit>false</defaultAutoCommit> <initialSize>10</initialSize> <maxActive>10</maxActive> <maxIdle>5</maxIdle> <minIdle>5</minIdle> <maxWait>3000</maxWait> <validationQuery>select 1</validationQuery> <testOnBorrow>true</testOnBorrow> <removeAbandoned>true</removeAbandoned> <removeAbandonedTimeout>180</removeAbandonedTimeout> <logAbandoned>true</logAbandoned> </datasourceProperty> </connectionPool> <connectionPool name="5000"> <dbtype>MYSQL</dbtype> <driverClassName>com.mysql.jdbc.Driver</driverClassName> <url>jdbc:mysql://localhost:3306/springok</url> <username>root</username> <password></password> <datasourceProperty> <defaultAutoCommit>false</defaultAutoCommit> <initialSize>10</initialSize> <maxActive>10</maxActive> <maxIdle>5</maxIdle> <minIdle>5</minIdle> <maxWait>3000</maxWait> <validationQuery>select 1</validationQuery> <testOnBorrow>true</testOnBorrow> <removeAbandoned>true</removeAbandoned> <removeAbandonedTimeout>180</removeAbandonedTimeout> <logAbandoned>true</logAbandoned> </datasourceProperty> </connectionPool> </da2s-configuration>
1.1.3. DataSourceManager的实现(核心部分)
package cn.xhgg.test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.util.Enumeration; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.sql.DataSource; import org.apache.commons.configuration.XMLConfiguration; import org.apache.commons.dbcp2.BasicDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; public class DataSourceManager { private final static Logger log = LoggerFactory.getLogger(DataSourceManager.class); static private ConcurrentHashMap<String, DataSource> pools = new ConcurrentHashMap<>(); static private ConcurrentHashMap<String, String> dbTypes = new ConcurrentHashMap<>(); static private String catalina_base = System.getProperty("catalina.base"); static private String log_file_name = "dbcp2_exception.log"; static private String DefaultConnectionPoolName = null; /** * 建构函数私有以防止其它对象创建本类实例 */ private DataSourceManager() { initDataSource(); } /** * 返回唯一实例.如果是第一次调用此方法,则创建实例 * * @return DBConnectionManager 唯一实例 */ static public DataSourceManager getInstance() { return DataSourceManager2Holder.instance; } /** 该类的一个对象,整个系统公用这一个对象。 */ private static class DataSourceManager2Holder { private static DataSourceManager instance = new DataSourceManager(); } /** * 根据指定属性创建连接池实例. * * @param props * 连接池属性 */ private void initDataSource() { XMLConfiguration config; try { config = new XMLConfiguration("da2s.xml"); config.setThrowExceptionOnMissing(false); } catch (org.apache.commons.configuration.ConfigurationException exc) { log.error("GlobalConfigurationException", exc); throw new RuntimeException(exc); } DefaultConnectionPoolName = config.getString("DefaultConnectionPool"); // 该项未配置,则值为null log.debug("DefaultConnectionPoolName is " + DefaultConnectionPoolName + "..."); List<?> poolList = config.getList("connectionPool.dbtype"); String connPoolName = new String(); String dbtype = new String(); String driverClassName = new String(); String url = new String(); String username = new String(); String password = new String(); boolean defaultAutoCommit = false; boolean defaultReadOnly = false; int initialSize = 0; int maxActive = 0; int maxIdle = 0; int minIdle = 0; long maxWait = 0; String validationQuery = new String(); boolean testOnBorrow = true; boolean removeAbandoned = true; int removeAbandonedTimeout = 0; boolean logAbandoned = true; long maxConnLifetimeMillis = 0; try { for (int i = 0, j = poolList.size(); i < j; i++) { connPoolName = config.getString("connectionPool(" + i + ")[@name]"); dbtype = config.getString("connectionPool(" + i + ").dbtype"); driverClassName = config.getString("connectionPool(" + i + ").driverClassName"); url = config.getString("connectionPool(" + i + ").url"); username = config.getString("connectionPool(" + i + ").username"); password = config.getString("connectionPool(" + i + ").password"); defaultAutoCommit = config.getBoolean("connectionPool(" + i + ").datasourceProperty.defaultAutoCommit", false); defaultReadOnly = config.getBoolean("connectionPool(" + i + ").datasourceProperty.defaultReadOnly", false); initialSize = config.getInt("connectionPool(" + i + ").datasourceProperty.initialSize", 3); maxActive = config.getInt("connectionPool(" + i + ").datasourceProperty.maxActive", 50); maxIdle = config.getInt("connectionPool(" + i + ").datasourceProperty.maxIdle", 20); minIdle = config.getInt("connectionPool(" + i + ").datasourceProperty.minIdle", 5); maxWait = config.getLong("connectionPool(" + i + ").datasourceProperty.maxWait", 3000); maxConnLifetimeMillis = config.getLong("connectionPool(" + i + ").datasourceProperty.maxLifetime", 600000);// 10分钟 validationQuery = config.getString("connectionPool(" + i + ").datasourceProperty.validationQuery", "select 1"); testOnBorrow = config.getBoolean("connectionPool(" + i + ").datasourceProperty.testOnBorrow", true); removeAbandoned = config.getBoolean("connectionPool(" + i + ").datasourceProperty.removeAbandoned", true); removeAbandonedTimeout = config.getInt("connectionPool(" + i + ").datasourceProperty.removeAbandonedTimeout", 180); logAbandoned = config.getBoolean("connectionPool(" + i + ").datasourceProperty.logAbandoned", true); BasicDataSource bds2 = new BasicDataSource(); bds2.setDriverClassName(driverClassName); bds2.setUrl(url); bds2.setUsername(username); bds2.setPassword(password); bds2.setDefaultAutoCommit(defaultAutoCommit); bds2.setDefaultReadOnly(defaultReadOnly); bds2.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 初始化连接数 bds2.setInitialSize(initialSize); // 最小空闲连接 bds2.setMinIdle(minIdle); // 最大空闲连接 bds2.setMaxIdle(maxIdle); // 超时回收时间(以毫秒为单位) bds2.setMaxWaitMillis(maxWait); // 最大连接数 bds2.setMaxTotal(maxActive); bds2.setTestOnBorrow(testOnBorrow); bds2.setValidationQuery(validationQuery); // 一个连接的最大存活毫秒数。如果超过这个时间,则连接在下次激活、钝化、校验时都将会失败。如果设置为0或小于0的值,则连接的存活时间是无限的。 bds2.setMaxConnLifetimeMillis(maxConnLifetimeMillis); // 空闲对象驱逐线程运行时的休眠毫秒数,如果设置为非正数,则不运行空闲对象驱逐线程。 long timeBetweenEvictionRunsMillis = 1000; bds2.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 超时取回 bds2.setLogAbandoned(logAbandoned); bds2.setAbandonedUsageTracking(logAbandoned); bds2.setRemoveAbandonedOnMaintenance(removeAbandoned); bds2.setRemoveAbandonedOnBorrow(removeAbandoned); bds2.setRemoveAbandonedTimeout(removeAbandonedTimeout); pools.put(connPoolName, bds2); dbTypes.put(connPoolName, dbtype); log.debug("Init DataSource " + connPoolName + "..."); } } catch (Exception e) { log.error("Init DataSource " + connPoolName + "...ERROR", e); throw new RuntimeException(e); } } /** * 动态添加连接池 */ public boolean addDataSource(String key, DataSource datasource) { pools.put(key, datasource); return true; } /** * 动态删除连接池 */ public void removeDataSource(String key) { if (key == null) return; BasicDataSource cds = (BasicDataSource) pools.remove(key); try { cds.close(); } catch (SQLException e) { log.error("Close DS Error key={}", key, e); } cds = null; } /** * 获取一个默认的可用连接. * * @return DataSource */ public DataSource getDataSource() { if (DefaultConnectionPoolName == null || DefaultConnectionPoolName.trim().isEmpty()) return null; else return (DataSource) pools.get(DefaultConnectionPoolName); } /** * 获取一个可用连接. * * @param name * 连接池名字 * @return DataSource */ public DataSource getDataSource(String name) { return (DataSource) pools.get(name); } /** * close all connection. <br> * 关闭所有闲置连接. */ public synchronized void shutdown() { Enumeration<String> allkeys = pools.keys(); while (allkeys.hasMoreElements()) { String poolName = (String) allkeys.nextElement(); log.warn("DataSourceManager shutdown pool[{}]...", poolName); BasicDataSource cpds = (BasicDataSource) pools.remove(poolName); try { cpds.close(); } catch (SQLException e) { log.error("Close DS Error key={}", poolName, e); } cpds = null; dbTypes.remove(poolName); } } public String getDefaultDataSourceName() { return DefaultConnectionPoolName; } public String getDBType(String name) { return (String) dbTypes.get(name); } public Set<String> getPoolNames() { return pools.keySet(); } /** * 获取一个默认的可用连接. * * @return DataSource */ public Connection getConnection() { return getConnection(DefaultConnectionPoolName); } /** * 获取一个可用连接. * * @param name * 连接池名字 * @return DataSource */ public Connection getConnection(String name) { // Objects.requireNonNull(name, "PoolName should not be null"); Preconditions.checkNotNull(name, "PoolName should not be null"); Preconditions.checkArgument(!name.trim().isEmpty(), "PoolName should not be empty"); BasicDataSource thisDS = (BasicDataSource)pools.get(name); //String dbType = dbTypes.get(name); Preconditions.checkState(thisDS != null, "DataSource " + name + " is null"); Preconditions.checkState(!thisDS.isClosed(), "DataSource " + name + " has closed"); try { Connection conn = thisDS.getConnection(); //setCallerInfo(conn); return conn; } catch (SQLException e) { throw new RuntimeException(e); } } /** * 获取直接的数据库conn * * @param driverClassName * @param dbUrl * @param userName * @param password * @return * @throws InstantiationException * @throws IllegalAccessException * @throws ClassNotFoundException * @throws SQLException */ public static Connection getDirectJDBCConnection(String driverClassName, String dbUrl, String userName, String password) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException { Class.forName(driverClassName).newInstance(); Connection conn = DriverManager.getConnection(dbUrl, userName, password); return conn; } }
1.1.4. JdbcUtils工具类实现
package cn.xhgg.test; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; public class JdbcUtils { // 使用ThreadLocal存储当前线程中的Connection对象 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); /** * 获取读数据源 * @return * @throws SQLException */ public static DataSource getReadDataSource(){ return DataSourceManager.getInstance().getDataSource("3000"); } /** * 获取写数据源 * @return */ public static DataSource getWriteDataSource(){ return DataSourceManager.getInstance().getDataSource("3000"); } public static Connection getConnection() throws SQLException { // 从当前线程中获取Connection Connection conn = threadLocal.get(); if (conn == null) { throw new SQLException("no connection init"); } return conn; } public static Connection getConnection(boolean isCreate) throws SQLException { // 从当前线程中获取Connection Connection conn = threadLocal.get(); if (conn == null&&isCreate) { loadReadConnection(); } return getConnection(); } /** * @Method: startTransaction * @Description: 开启事务 * */ public static void loadReadConnection() { try { Connection conn = threadLocal.get(); if (conn == null) { conn = getReadDataSource().getConnection(); // 把 conn绑定到当前线程上 threadLocal.set(conn); } // 开启事务 conn.setAutoCommit(false); } catch (Exception e) { throw new RuntimeException(e); } } /** * @Method: startTransaction * @Description: 开启事务 * */ public static void startTransaction() { try { Connection conn = threadLocal.get(); if (conn == null) { conn = getWriteDataSource().getConnection(); // 把 conn绑定到当前线程上 threadLocal.set(conn); } // 开启事务 conn.setAutoCommit(false); } catch (Exception e) { throw new RuntimeException(e); } } /** * @Method: rollback * @Description:回滚事务 * */ public static void rollback() { try { // 从当前线程中获取Connection Connection conn = threadLocal.get(); if (conn != null) { // 回滚事务 conn.rollback(); } } catch (Exception e) { throw new RuntimeException(e); } } /** * @Method: commit * @Description:提交事务 * */ public static void commit() { try { // 从当前线程中获取Connection Connection conn = threadLocal.get(); if (conn != null) { conn.commit(); } } catch (Exception e) { throw new RuntimeException(e); } } /** * @Method: close * @Description:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池) * */ public static void close() { try { // 从当前线程中获取Connection Connection conn = threadLocal.get(); if (conn != null) { conn.close(); // 解除当前线程上绑定conn threadLocal.remove(); } } catch (Exception e) { throw new RuntimeException(e); } } public static boolean isClosed(){ try { // 从当前线程中获取Connection Connection conn = threadLocal.get(); if (conn != null) { return false; } } catch (Exception e) { throw new RuntimeException(e); } return true; } }
1.1.5. 测试
Connection connection = JdbcUtils.getConnection(true); System.out.println(connection);
输出如下图:
测试OK。
1.1.6. 思考获取数据库连接与ThreadLocal
用户的请求处理过程: controller层-->>service层-->>dao层。
所以先实例化controller层并初始化service实例对象,调用service方法,service实例化依赖的dao层。并进行数据库的操作。然后依次返回。
OpenSessionInView模式不就是解决session关闭,不能在层之间传递的问题。
逻辑如下图:
mysql数据库连接池使用(二)实现自己的数据库连接池的更多相关文章
- ADO.Net 之 数据库连接池(二)
连接到数据库服务器通常由几个需要很长时间的步骤组成.必须建立物理通道(例如套接字或命名管道),必须与服务器进行初次握手,必须分析连接字符串信息,必须由服务器对连接进行身份验证,必须运行检查以便在当前事 ...
- Druid连接池(二)
DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0.DBCP.PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB ...
- 【Java】java数据库连接中C3P、DBCP、Druid连接池的使用
使用JDBC的步骤:1.加载数据库驱动2.通过DriverManager获得数据库连接3.通过Connection获得Statement对象4.使用Statement执行SQL语句.5.操作结果集合6 ...
- Swoole 实战:MySQL 查询器的实现(协程连接池版)
目录 需求分析 使用示例 模块设计 UML 类图 入口 事务 连接池 连接 查询器的组装 总结 需求分析 本篇我们将通过 Swoole 实现一个自带连接池的 MySQL 查询器: 支持通过链式调用构造 ...
- 我的MYSQL学习心得(二) 数据类型宽度
我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...
- mysql 同一IP 产生太多终端的数据库连接导致阻塞
问题:null, message from server: "Host 'ip' is blocked because of many connection errors; unblock ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- 屌炸天实战 MySQL 系列教程(二) 史上最屌、你不知道的数据库操作
此篇写MySQL中最基础,也是最重要的操作! 第一篇:屌炸天实战 MySQL 系列教程(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:屌炸天实战 MySQL 系列教程(二) 史上最屌.你不 ...
- Mysql学习笔记(二)数据类型 补充
原文:Mysql学习笔记(二)数据类型 补充 PS:简单的补充一下数据类型里的String类型以及列类型... 学习内容: 1.String类型 2.列类型存储需求 String类型: i.char与 ...
随机推荐
- ●BZOJ 4698 Sdoi2008 Sandy的卡片
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4698 题解: 后缀数组,二分这个题还是比较套路的.首先依据题意,把各个串差分以后,用分割符号 ...
- hdu 4670 Cube number on a tree(点分治)
Cube number on a tree Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/ ...
- UVA 1146 Now or later
The Terminal Radar Approach CONtrol (TRACON) controls aircraft approaching and departing when they a ...
- [BZOJ]1047 理想的正方形(HAOI2007)
真·水题.小C本来是不想贴出来的,但是有一股来自东方的神秘力量催促小C发出来. Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和 ...
- 中断API之setup_irq【转】
转自:https://blog.csdn.net/tiantao2012/article/details/78957472 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blo ...
- 笔记7 AOP练习<有疑问>
场景描述: 核心业务:举行一场古典音乐会. 周边功能:观众入场,关闭手机.落座,觉得音乐好听时鼓掌,觉都不好听则退票.(切面) 1.编写切点(切点用于准确定位应该在什么地方应用切面的通 知)----即 ...
- Fashion-MNIST:A MNIST-like fashion product database. Benchmark
Zalando的文章图像的一个数据集包括一个训练集6万个例子和一个10,000个例子的测试集. 每个示例是一个28x28灰度图像,与10个类别的标签相关联. 时尚MNIST旨在作为用于基准机器学习算法 ...
- Qone 正式开源,使 javascript 支持 .NET LINQ
Qone 下一代 Web 查询语言,使 javascript 支持 LINQ Github: https://github.com/dntzhang/qone 缘由 最近刚好修改了腾讯文档 Excel ...
- mysql 合并left join 数据条目
查询两张关联表信息,原sql: SELECT * FROM car_life_info c left JOIN shop_label sl ON c.shop_id = sl.shop_id 出现 ...
- Mysql优化--Show Profile
Mysql 系列文章主页 =============== 是Mysql提供可以用来分析当前会话中语句执行的资源消耗情况.可以用于Sql的调优的测量.默认情况下处于关闭状态,并保存最近 15 次的运行结 ...