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与 ...
随机推荐
- 【bzoj4009 hnoi2015】接水果
题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本. 首先有 ...
- WebStorm配置node.js调试
最近因为工作关系,一直在做node.js的开发,学习了koa框架,orm框架sequelize,以及swagger文档的配置.但是,最近因为swagger文档使用了es6的修饰器那么个东西(在java ...
- Git与Github的基本概念
git git是一个分布式版本控制系统,在这里就要介绍一下什么是版本控制:参考至维基百科 版本控制(Revision control)是维护工程蓝图的标准作法,能追踪工程蓝图从诞生一直到定案的过程.此 ...
- 垃圾回收机制(GC)
垃圾收集器(GC)与内存分配策略 GC需要完成的三件事: 判断哪些内存需要回收 什么时候回收 如何回收 在java内存运行时区域的各个部分中,程序计数器.虚拟机栈.本地方法栈3个区域随线程而生,随线程 ...
- 在Linux系统上获取命令帮助信息和划分man文档
使用历史命令history 打完以后前面会有顺序号的比如1 cd2 ls3 pwd如果需要重新执行cd命令则可以执行 !3 命令 命令补全功能 比如你要执行history命令 可以打上histo+键 ...
- Ubuntu16.04下安装jdk1.8过程
笔者环境:腾讯云服务器 Ubuntu16.04 x64 一 . 去oracle官网下载对应的jdk 下载地址:http://www.oracle.com/technetwork/java/javase ...
- SpringBoot+Mybatis+ Druid+PageHelper 实现多数据源并分页
前言 本篇文章主要讲述的是SpringBoot整合Mybatis.Druid和PageHelper 并实现多数据源和分页.其中SpringBoot整合Mybatis这块,在之前的的一篇文章中已经讲述了 ...
- grpc的服务注册与发现及负载
参考文章: (1)https://segmentfault.com/a/1190000008672912 (2)https://grpc.io/docs/ (3)https://github.com/ ...
- Java不走弯路教程(3.用户验证与文件内容查询)
3.用户验证与文件内容查询 在上一章中,我们完成了对指定文件内容的输出操作. 我们现在有如下格式的文件product.db id,product_name,product_detail 1,noteb ...
- 百钱买百鸡问题Java
//百钱买百鸡public class baiqianbaiji { static void BQBJ(int m,int n)//m为钱的总数,n为鸡数 { int z; for(int x = 0 ...