手写java数据库连接池,自定义实现数据库连接池,兼容springboot
一、目标
用精简的代码实现一个类似于Hikari,Druid一样的高性能数据库连接池。
二、实现思路
1:新建连接池配置类保存连接池配置。
2:实现DataSource接口。
3:新增SmpDbPool类,内部维护一个阻塞队列保存数据库连接,并提供数据库连接的获取回收等方法。
4:大致类图:
三、核心代码
1:数据库连接代理类SmpConnectionProxy
SmpConnectionProxy实现jdbc的Connection接口
重写close方法,确保外部调用close的时候将连接归还到连接池。
public void close() throws SQLException {if (this.closed) return;this.closed = true;//回滚掉未提交的脏statementif (this.isCommitStateDirty && !this.isAutoCommit){this.delegate.rollback();//}this.clearWarnings();this.smpDbPool.releaseConnection(this);}
新增判断连接是否有效方法供连接池类使用。
public boolean isValid() throws SQLException {if (System.currentTimeMillis() - this.lastActiveTime < this.smpDbPool.getConfig().getKeepaliveTimeInMill()) return true;long validationTimeout = this.smpDbPool.getConfig().getValidationTimeoutInMill();this.delegate.setNetworkTimeout(this.smpDbPool.getNetTimeoutExecutor(), (int) validationTimeout);int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;return this.delegate.isValid(validationSeconds);}
2:数据源类SmpDataSource
SmpDataSource实现DataSource接口
实现getConnection方法,以便外界从连接池获取连接。
@Overridepublic Connection getConnection() throws SQLException {try {return this.dbPool.borrowConnection();} catch (Exception e) {LOG.error("getConnection error", e);throw new SQLException(e);}}
3:连接池类SmpDbPool
获取数据库连接方法:
public Connection borrowConnection() throws Exception {boolean isDebugEnable = LOG.isDebugEnabled();long sTime = 0;if (isDebugEnable){//尽量少访问临界资源sTime = System.currentTimeMillis();}try {SmpConnectionProxy connection = this.getConnectionFromQueue();if (connection != null) return connection;//未获取到有效连接,校验是否创建连接int maxActive = this.smpConfig.getMaxActive(), dbAmount = createdDbConnectionAmount.get();if (dbAmount >= maxActive){//尝试等待其他线程释放连接connection = this.dbConnectQueue.poll(this.smpConfig.getMaxWaitInMill(), TimeUnit.MILLISECONDS);if (connection != null) return connection.borrowConnection();LOG.error("get connection error, no connection available,maxActive:{}, activated connection:{}", maxActive, dbAmount);throw new IllegalStateException("database connection pool too busy");}connection = this.createConnectionForPool();if (connection != null) return connection;throw new IllegalStateException("database connection pool too busy");} finally {if (isDebugEnable){LOG.debug("get database connection cost {} ms", System.currentTimeMillis() - sTime);}}}
从阻塞队列获取数据库连接方法:
private SmpConnectionProxy getConnectionFromQueue() throws SQLException {int times = 0;while (times++ < 1000){SmpConnectionProxy connection = this.dbConnectQueue.poll();if (connection == null) return null;if (connection.isValid()) return connection.borrowConnection();//destroy invalid connectionconnection.destroy();createdDbConnectionAmount.decrementAndGet();LOG.info("destroyed invalid jdbc connection");}return null;}
新建数据库连接:
private SmpConnectionProxy createConnectionForPool() throws Exception{boolean locked = false;try {locked = GET_CONNECTION_LOCK.tryLock(this.smpConfig.getMaxWaitInMill() >> 1, TimeUnit.MILLISECONDS);if (!locked){LOG.error("get lock to create connection error");return null;}SmpConnectionProxy connection = this.getConnectionFromQueue();if (connection != null) return connection;int dbAmount = createdDbConnectionAmount.get(), maxActive = this.smpConfig.getMaxActive();if (dbAmount >= maxActive){LOG.error("get connection error, no connection available,maxActive:{}, activated connection:{}", maxActive, dbAmount);return null;}connection = this.createConnection();createdDbConnectionAmount.addAndGet(1);return connection.borrowConnection();} catch (ClassNotFoundException ex){LOG.error("class {} not found", this.smpConfig.getDriverClassName(), ex);throw new SQLException(ex);}finally {if (locked){GET_CONNECTION_LOCK.unlock();}}}private SmpConnectionProxy createConnection() throws ClassNotFoundException, SQLException {Class.forName( this.smpConfig.getDriverClassName() );Connection connection = DriverManager.getConnection( this.smpConfig.getUrl(), this.smpConfig.getUsername(), this.smpConfig.getPassword());LOG.debug("create new database connection with url:{}", this.smpConfig.getUrl());return new SmpConnectionProxy(connection, this, connection.getAutoCommit());}
4:构建spring-boot-starter
这里需要说明的是springboot默认会尝试使用com.zaxxer.hikari.HikariDataSource,org.apache.tomcat.jdbc.pool.DataSource,org.apache.commons.dbcp2.BasicDataSource作为连接池,相关代码逻辑在org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration,因此配置spring-boot-starter的时候先要确保应用没有配置连接池。
核心代码:
5:springboot使用
添加依赖后再添加如下配置:
spring.datasource.type=com.lauor.smpdb.SmpDataSource
#最小连接数,默认4
spring.datasource.smpdb.minIdle=4
#最大连接数,默认40
spring.datasource.smpdb.maxActive=20
#数据库连接有效期检查间隔时间ms,默认30分钟,最小1s
spring.datasource.smpdb.keepaliveTimeInMill=1800000
#获取数据库连接最大等待时间ms,默认30s,最小1s
spring.datasource.smpdb.maxWaitInMill=30000
#数据连接合法性校验超时时间,默认5s,最小1s
spring.datasource.smpdb.validationTimeoutInMill=5000
连接池完整代码:https://gitee.com/tandatda/smpdb
连接池使用完整demo:https://gitee.com/tandatda/demo-edr-smpdb
手写java数据库连接池,自定义实现数据库连接池,兼容springboot相关推荐
- 安全系列之——手写JAVA加密、解密
其他文章: 安全系列之--手写JAVA加密.解密 安全系列之--数据传输的完整性.私密性.源认证.不可否认性 安全系列之--主流Hash散列算法介绍和使用 安全系列之--RSA的公钥私钥有多少人能分的 ...
- 【XML和Java】手写Java程序引用xsd验证xml
一.首先要有一个xml文件和xsd文件 (1) database.conf.xml <?xml version="1.0" encoding="UTF-8" ...
- 视频教程-手写Java框架系列教程之一反射(含配套资料)-Java
手写Java框架系列教程之一反射(含配套资料) 张长志技术全才.擅长领域:区块链.大数据.Java等.10余年软件研发及企业培训经验,曾为多家大型企业提供企业内训如中石化,中国联通,中国移动等知名企业 ...
- 手写Java线程池_超详细解说_绝对能运行_代码超详细注释
线程池 问题背景 只是单纯使用 new Thread(runnable).start(); 的方式创建线程, 将会导致严重的程序性能问题: 1.线程创建, 销毁需要消耗很大的系统资源; 2.虚拟机创建 ...
- android 手写签批_Android自定义实现手写签名功能
一.Android自定义View步骤 : 自定义属性: 选择和设置构造方法: 重写onMeasure()方法: 重写onDraw()方法: 重写onLayout()方法: 重写其他事件的方法(滑动监听 ...
- java linkedlist底层_手写Java LinkedList核心源码
上一章我们手写了ArrayList的核心源码,ArrayList底层是用了一个数组来保存数据,数组保存数据的优点就是查找效率高,但是删除效率特别低,最坏的情况下需要移动所有的元素.在查找需求比较重要的 ...
- android 手写签批_Android自定义View——手写签批
接到一个领导批示保留原笔迹的功能,类似于绘画板,用户打开后可以绘制,点击完成后以图片的形式保存在本地,并且显示绘制后图片,上传服务器,达到保留原笔迹的目的.可以运用于签字.审批等. 效果图: 手写签批 ...
- 1000题!!阿里P8架构师手写“Java面试宝典”带你横扫全网
序言 很多同学学习Java并发一头扎进源码,最后头破血流,无功而返.横看成岭侧成峰,远近高低各不同.学习要始终从不同的视角来看待问题.学习并发亦是如此,需要通过理论远看轮廓,然后通过源码近看明细. 今 ...
- 手写java_手写java锁
手写非公平可重入锁 公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁. 优点:所有的线程都能得到资源,不会饿死在队列中. 缺点:吞吐量会下降很多,队列 ...
最新文章
- 一个快速实现彩屏应用的跨平台快速原型开发工具平台,最重要的是还免费!8ms.xyz平台原以为是单片机版墨刀,今天上去玩了才知道平台厉害的很,基于WEB端免搭建开发环境,跑的还是C代码编译出来的程序!
- 菜鸟学Linux 第034篇笔记 vmlinuz, initrd, modules, script
- 新版手机浏览器_夸克浏览器发布全新3.0版,AI技术创新智能化信息服务
- java 同步块(Java Synchronized Blocks)
- mongooes怎么链接mysql_如何使用Node + Mongoose连接远程MongoDB数据库
- 让基础设施代码化更加容易,pulumi 都做了些什么?
- 【优化算法】多目标水循环算法(MOWCA)【含Matlab源码 1433期】
- 【数学建模】2021年美赛C题思路(预测模型和图像识别)【含Matlab源码 245期】
- 最好用的ps/lr滤镜插件ON1 photo raw 2021mac
- 使用selenium爬验证码图片并识别
- 775针服务器cpu性能排行,e7500(775针cpu天梯图)
- 【python】模拟斗牛纸牌游戏「牛牛」
- Java的在哪里找labor_LaborDay哪里玩
- 如何做内网穿透,在家里连回公司服务器做操作
- 邮箱自动化(smtplib模块)--以邮件正文HTML表格形式
- 【每日一道智力题】之 药瓶毒鼠鼠
- ubuntu20.04安装搜狗sogou输入法
- 背景动态线条js特效html5代码
- 老师助手为什么总是服务器错误,【家长助手】孩子在校总犯错 解决内因最关键...
- [Java大厂必备面试题] 点滴促就辉煌, 每日三题【Day5】:基础篇2
热门文章
- LLBLGen 关于类型转换
- SharePoint工作流解决方案QuickFlow系列(2)--Task
- ASP.Net2.0 数据绑定控件的优越性在哪里?
- 怎样对流媒体进行压力测试_暖气片怎样安装效果好?暖气片正确的安装,采暖效果更好!...
- Linux Centos6.5如何截图
- arm中的.a文件如何产生的_可变文件系统:如何在IPFS中处理文件?
- ceph与hdfs的比较_分布式存储中HDFS与Ceph两者的区别是什么,各有什么优势?
- 【Python金融量化 1- 100 】了解Python及常用财经数据接口包
- 化工仪表和自动化(自动控制系统)
- 三十二、从0到1教你用Scrapy来爬取整站天气网