手写DAO框架(三)-数据库连接
-------前篇:手写DAO框架(二)-开发前的最后准备---------
前言
上一篇主要是温习了一下基础知识,然后将整个项目按照模块进行了划分。因为是个人项目,一个人开发,本人采用了自底向上的开发。
本篇会介绍连接层的代码,包括三部分:pom配置、数据库连接和数据库连接池。
pom配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>me.lovegao</groupId>
<artifactId>gdao</artifactId>
<version>0.0.2-SNAPSHOT</version>
<packaging>jar</packaging> <name>gdao</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.12</junit.version>
</properties> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency>
</dependencies>
<build>
<finalName>me_lovegao_gdao</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
</plugins>
</build>
</project>
maven定义了jdk的版本,免得导入IDE的时候变成1.5,又得修改。
数据库连接
数据库连接主要就是为了获取数据库连接,这个模块不关注连接的使用、关闭等,只提供获取连接,其他逻辑由调用方负责。
书上、网上都说,要面向接口编程。
本来我是定义了一个接口的,但是最终实现的时候没有用上。回头看这段代码,感觉用上接口之后,依赖关系有点复杂,所以就没改了。
而且,这里的获取连接,定位的是一个工具类,所以直接写了静态方法。
1、连接获取类:ConnectionGetor
package me.lovegao.gdao.connection; import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import me.lovegao.gdao.bean.SystemConstant; public class ConnectionGetor {
private final static Logger log = LoggerFactory.getLogger(ConnectionGetor.class); public static Connection createConnection(Properties properties) throws Exception {
Connection conn = null;
String driverName = properties.getProperty(SystemConstant.STR_DRIVER_NAME);
String url = properties.getProperty(SystemConstant.STR_CONNECTION_URL);
String userName = properties.getProperty(SystemConstant.STR_USER_NAME);
String passwd = properties.getProperty(SystemConstant.STR_USER_PASSWORD);
try {
Class.forName(driverName);
conn = DriverManager.getConnection(url, userName, passwd);
} catch (Exception e) {
log.error("createConnectionException", e);
}
return conn;
} }
为了保证通用性,入参我选择了Properties类型。这样,配置既可以从本地加载,也可以从流加载,确保了灵活性。
为了能够支持多数据源,所以这个方法里没有定义和数据库连接相关的局部变量或者常量。
2、数据库实例配置:Properties
说到了配置,我就把配置的字段先列一下。毕竟框架再好用,不教人怎么配置就是扯。
##驱动名称
driverName=com.mysql.jdbc.Driver
##连接url
connectionUrl=jdbc:mysql://localhost:3306/simple?useServerPrepStmts=false&rewriteBatchedStatements=true&connectTimeout=1000&useUnicode=true&characterEncoding=utf-8
##用户名
userName=name
##用户密码
userPassword=123456
##初始化连接数
initConnectionNum=10
##最大连接数
maxConnectionNum=50
##最大查询等待时间-秒
maxQueryTime=3
配置这块就是这样的。为什么定义了这几个配置,在前两篇文章里也有涉及,这里不再赘述。
数据库连接池
因为数据库连接的创建是需要时间的,我们可以在项目初始化的时候先创建一批连接,在需要使用连接的时候可以节省创建连接的等待时间,从而提高程序响应速度。而且,连接是可以复用的,所以,项目中使用连接池是有意义的。
要面向接口编程,所以这里先定义接口。
1、连接池接口:IConnectionPool
package me.lovegao.gdao.connection; import java.sql.Connection; /**
* 连接池
* @author simple
*
*/
public interface IConnectionPool {
/**
* 获取连接
* @return
*/
Connection getConnection() throws Exception;
/**
* 归还连接
* @param conn
*/
void returnConnection(Connection conn); /**
* 获取查询超时时间
* @return
*/
int getQueryTimeoutSecond();
}
从代码可以看到,连接池的主要功能包括:获取连接,归还连接。
“获取查询超时时间”这个接口是我后来加的,作为一个框架,应该保证不因为一个查询耗时超过正常区间仍然义无反顾的等待。只不过加在这里,感觉不太优雅,暂时也没有想到更好的写法,所以还是暂时放在这里吧。(有建议欢迎提出)
2、连接池的简单实现:SimpleConnectionPool
package me.lovegao.gdao.connection; import java.sql.Connection;
import java.util.Map;
import java.util.Properties;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import me.lovegao.gdao.bean.SystemConstant; public class SimpleConnectionPool implements IConnectionPool {
private final static Logger log = LoggerFactory.getLogger(SimpleConnectionPool.class);
/**配置**/
private Properties properties;
/**保存连接的map**/
private final Map<Integer, Connection> CONNECTION_MAP_POOL = new ConcurrentHashMap();
/**连接池的连接索引**/
private final Queue<Integer> CONNECTION_KEY_POOL = new ConcurrentLinkedQueue();
/**连接池初始连接数量**/
private int POOL_INIT_NUM;
/**连接池最大连接数量**/
private int POOL_MAX_NUM;
/**已创建连接数量**/
private AtomicInteger POOL_CREATE_NUM = new AtomicInteger(0);
/**查询超时时间-秒**/
private int QUERY_TIMEOUT_SECONDS; public SimpleConnectionPool(Properties properties) throws Exception {
this.properties = properties;
this.POOL_INIT_NUM = Integer.parseInt(properties.getProperty(SystemConstant.STR_INIT_CONNECTION_NUM));
this.POOL_MAX_NUM = Integer.parseInt(properties.getProperty(SystemConstant.STR_MAX_CONNECTION_NUM));
this.QUERY_TIMEOUT_SECONDS = Integer.parseInt(properties.getProperty(SystemConstant.STR_QUERY_TIME));
for(int i=0; i<POOL_INIT_NUM; i++) {
POOL_CREATE_NUM.incrementAndGet();
Connection conn = ConnectionGetor.createConnection(properties);
CONNECTION_MAP_POOL.put(conn.hashCode(), conn);
CONNECTION_KEY_POOL.add(conn.hashCode());
}
} @Override
public Connection getConnection() throws Exception {
Connection conn = null;
Integer connKey = CONNECTION_KEY_POOL.poll();
if(connKey == null) {
if(POOL_CREATE_NUM.intValue() < POOL_MAX_NUM) {
int poolNum = POOL_CREATE_NUM.incrementAndGet();
if(poolNum <= POOL_MAX_NUM) {
conn = ConnectionGetor.createConnection(properties);
CONNECTION_MAP_POOL.put(conn.hashCode(), conn);
} else {
POOL_CREATE_NUM.decrementAndGet();
}
}
} else {
conn = CONNECTION_MAP_POOL.get(connKey);
}
//没有获取到连接
if(conn == null) {
throw new NullPointerException("连接池连接用完");
}
return conn;
} @Override
public void returnConnection(Connection conn) {
if(conn != null) {
try {
if(conn.isClosed()) {
CONNECTION_MAP_POOL.remove(conn.hashCode());
POOL_CREATE_NUM.decrementAndGet();
} else {
CONNECTION_KEY_POOL.add(conn.hashCode());
}
} catch (Exception e) {
log.error("returnConnectionException", e);
}
}
} @Override
public int getQueryTimeoutSecond() {
return QUERY_TIMEOUT_SECONDS;
} }
逻辑简介:初始化的时候,先创建指定数量的连接存起来。在获取连接的时候,如果还有连接,就返回;如果没有连接了,就判断连接数是否到了最大上限,没有到就创建并返回,到上限了就返回空。
其他说明:
为了保证并发安全,这里是通过POOL_CREATE_NUM变量来保证的。
为了支持多数据源,这里同样没有静态变量。
为了保存连接,这里定义了两个容器,分别是Map<Integer, Connection> CONNECTION_MAP_POOL和 Queue<Integer> CONNECTION_KEY_POOL。为了并发安全,使用了ConcurrentHashMap和ConcurrentLinkedQueue来存储。
其中,队列是用于取出连接和返回连接,这里队列存放的是连接对应的key。MAP是为了保存对所有连接的引用,防止一些连接没有归还而系统却不知道,同时也可以知道连接的总数。也有一点考虑,是为了以后可以有单独线程来定时判断哪些线程应该关闭,主要是为了以后扩展用。
最初,我想把连接池做成那种:获取连接的时候,如果没有连接,就等待一段时间(指定时间,即获取连接等待时间)之后再试。查了一下对应的技术栈,综合考虑了一下,感觉有些不足之处,比如:如果连接用完了,可能出现大量线程等待的现象,反而会影响性能。
后来参考了一下其他连接池的实现,最终做成目前的这样:获取连接的时候,没有连接,看能不能创建,能创建就创建,不能创建就返回失败。逻辑简单,容易实现,快速错误。大概这就是fast-fail吧!
--本文内容到这里就结束了,欢迎指导交流--
下篇内容:手写DAO框架(四)-SQL执行
手写DAO框架(三)-数据库连接的更多相关文章
- 手写DAO框架(二)-开发前的最后准备
-------前篇:手写DAO框架(一)-从“1”开始 --------- 前言:前篇主要介绍了写此框架的动机,把主要功能点大致介绍了一下.此篇文章主要介绍开发前最后的一些准备.主要包括一些基础知识点 ...
- 手写DAO框架(四)-SQL执行
-------前篇:手写DAO框架(三)-数据库连接--------- 前言 通过上一篇写的方法,可以灵活的获取.释放数据库连接,拿到连接之后,我们就可以执行sql了!所以,本篇介绍的就是SQL执行器 ...
- 手写DAO框架(七)-如何保证连接可用
版权声明:本文为博客园博主「水木桶」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明.原文链接:https://www.cnblogs.com/shuimutong/p ...
- 手写DAO框架(一)-从“1”开始
背景: 很久(4年)之前写了一个DAO框架-zxdata(https://github.com/shuimutong/zxdata),这是我写的第一个框架.因为没有使用文档,我现在如果要用的话,得从头 ...
- 手写DAO框架(五)-DAO层实现
-------前篇:手写DAO框架(四)-SQL执行--------- 前言 通过上一篇,可以通过传入sql和对应的参数,可以执行sql并返回结果.但是对于一个DAO框架来说,要尽量的面向对象编程,也 ...
- 手写DAO框架(六)-框架使用示例
一.引入pom <dependency> <groupId>me.lovegao</groupId> <artifactId>gdao</arti ...
- 手写MVC框架(一)-再出发
背景 前段时间把之前写的DAO框架(手写DAO框架(一)-从“1”开始)整理了一下,重构了一版.整理过程中看以前写的代码,只是为了了解实现,只是为了实现,代码写的有点粗糙.既然已经整理了DAO框架,索 ...
- 手写MQ框架(一)-准备启程
一.背景 很久以前写了DAO框架和MVC框架,前段时间又重写了DAO框架-GDAO(手写DAO框架(一)-从“1”开始,源码:https://github.com/shuimutong/gdao.gi ...
- 手写MQ框架(二)-服务端实现
一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...
随机推荐
- E20170611-hm
ascending adj. 上升的,向上的; ascend vt. 攀登; 继承; 占领; vi. 上升; 爬坡; 追溯; descending n. 递减; descend v ...
- winMTR的使用
WinMTR下载链接:http://pan.baidu.com/share/link?shareid=236531&uk=1126982975 WinMTR 使用方法及软件介绍: WinMTR ...
- [TYVJ1391]走廊泼水节
Description 话说,中中带领的OIER们打算举行一次冬季泼水节,当然这是要秘密进行的,绝对不可以让中中知道.不过中中可是老江湖了,当然很快就发现了我们的小阴谋,于是他准备好水枪迫不及待的想要 ...
- EditText(4)常用属性详解
常用的属性: 显示密码 通过设置EditText的setTransformationMethod()方法来实现隐藏密码或这显示密码. editText.setTransformationMethod( ...
- 网站开发综合技术 HTML
HTML 内容(Hyper Text Markup Language,超文本标记语言) CSS 网页美化 Javascript 脚本语言 第一部 ...
- responsive-navigator
html 代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...
- js中原型和原型链
1.原型: 在JavaScript 中,对象被表现为prototype . 原型其实一直存在于我们接触过的任何一个对象. 2. Tip:在函数对象中也存在__proto__属性,但是查看函数对象的原型 ...
- Farseer.net轻量级开源框架 入门篇:删除数据详解
导航 目 录:Farseer.net轻量级开源框架 目录 上一篇:Farseer.net轻量级开源框架 入门篇: 修改数据详解 下一篇:Farseer.net轻量级开源框架 入门篇: 查询数据详解 ...
- Lazarus 字符集转换 Utf8ToAnsi,UTF8ToWinCP,UTF8ToSys,UTF8ToConsole
由于Lazarus从1.2版开始默认字符集就是UTF8,如果要转到系统正常显示或文本保存,就必须对字符集进行转换.Lazarus提供了很多函数.如题. 那么这里面有什么关系呢? UTF8ToSys 需 ...
- ERwin 正向工程
1.物理模型带字段备注 COMMENT ON 将模型切换至 physical 模式选择 Model ---> Domain Dictionary , 在 tab 标签中,切换至comment 然 ...