mysql数据库连接池使用(二)实现自己的数据库连接池

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 上一个章节,我们讲了xml文件的解析框架XMLConfiguration的使用,不懂的可以参考Apache Commons Configuration读取xml配置具体使用。

上一个章节,我们讲了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关闭,不能在层之间传递的问题。

逻辑如下图:

 


 

 

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
14天前
|
SQL 数据可视化 关系型数据库
轻松入门MySQL:深入探究MySQL的ER模型,数据库设计的利器与挑战(22)
轻松入门MySQL:深入探究MySQL的ER模型,数据库设计的利器与挑战(22)
|
14天前
|
存储 关系型数据库 MySQL
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
|
14天前
|
关系型数据库 MySQL 数据库
轻松入门MySQL:精准查询,巧用WHERE与HAVING,数据库查询如虎添翼(7)
轻松入门MySQL:精准查询,巧用WHERE与HAVING,数据库查询如虎添翼(7)
|
10天前
|
存储 关系型数据库 MySQL
MySQL基础入门:数据库操作全攻略
MySQL基础入门:数据库操作全攻略
44 0
|
10天前
|
关系型数据库 MySQL 数据库
卸载云服务器上的 MySQL 数据库
卸载云服务器上的 MySQL 数据库
30 0
|
14天前
|
SQL 关系型数据库 MySQL
轻松入门MySQL:深入学习数据库表管理,创建、修改、约束、建议与性能优化(3)
轻松入门MySQL:深入学习数据库表管理,创建、修改、约束、建议与性能优化(3)
|
2天前
|
SQL 缓存 Java
Java数据库连接池:优化数据库访问性能
【4月更文挑战第16天】本文探讨了Java数据库连接池的重要性和优势,它能减少延迟、提高效率并增强系统的可伸缩性和稳定性。通过选择如Apache DBCP、C3P0或HikariCP等连接池技术,并进行正确配置和集成,开发者可以优化数据库访问性能。此外,批处理、缓存、索引优化和SQL调整也是提升性能的有效手段。掌握数据库连接池的使用是优化Java企业级应用的关键。
|
3天前
|
关系型数据库 MySQL 数据库
数据库基础(mysql)
数据库基础(mysql)
|
3天前
|
SQL 关系型数据库 数据库
【后端面经】【数据库与MySQL】SQL优化:如何发现SQL中的问题?
【4月更文挑战第12天】数据库优化涉及硬件升级、操作系统调整、服务器/引擎优化和SQL优化。SQL优化目标是减少磁盘IO和内存/CPU消耗。`EXPLAIN`命令用于检查SQL执行计划,关注`type`、`possible_keys`、`key`、`rows`和`filtered`字段。设计索引时考虑外键、频繁出现在`where`、`order by`和关联查询中的列,以及区分度高的列。大数据表改结构需谨慎,可能需要停机、低峰期变更或新建表。面试中应准备SQL优化案例,如覆盖索引、优化`order by`、`count`和索引提示。优化分页查询时避免大偏移量,可利用上一批的最大ID进行限制。
17 3
|
4天前
|
存储 关系型数据库 MySQL
【后端面经】【数据库与MySQL】为什么MySQL用B+树而不用B树?-02
【4月更文挑战第11天】数据库索引使用规则:`AND`用`OR`不用,正用反不用,范围中断。索引带来空间和内存代价,包括额外磁盘空间、内存占用和数据修改时的维护成本。面试中可能涉及B+树、聚簇索引、覆盖索引等知识点。MySQL采用B+树,因其利于范围查询和内存效率。数据库不使用索引可能因`!=`、`LIKE`、字段区分度低、特殊表达式或全表扫描更快。索引与NULL值处理在不同数据库中有差异,MySQL允许NULL在索引中的使用。
10 3

推荐镜像

更多