-------前篇:手写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框架(三)-数据库连接的更多相关文章

  1. 手写DAO框架(二)-开发前的最后准备

    -------前篇:手写DAO框架(一)-从“1”开始 --------- 前言:前篇主要介绍了写此框架的动机,把主要功能点大致介绍了一下.此篇文章主要介绍开发前最后的一些准备.主要包括一些基础知识点 ...

  2. 手写DAO框架(四)-SQL执行

    -------前篇:手写DAO框架(三)-数据库连接--------- 前言 通过上一篇写的方法,可以灵活的获取.释放数据库连接,拿到连接之后,我们就可以执行sql了!所以,本篇介绍的就是SQL执行器 ...

  3. 手写DAO框架(七)-如何保证连接可用

    版权声明:本文为博客园博主「水木桶」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明.原文链接:https://www.cnblogs.com/shuimutong/p ...

  4. 手写DAO框架(一)-从“1”开始

    背景: 很久(4年)之前写了一个DAO框架-zxdata(https://github.com/shuimutong/zxdata),这是我写的第一个框架.因为没有使用文档,我现在如果要用的话,得从头 ...

  5. 手写DAO框架(五)-DAO层实现

    -------前篇:手写DAO框架(四)-SQL执行--------- 前言 通过上一篇,可以通过传入sql和对应的参数,可以执行sql并返回结果.但是对于一个DAO框架来说,要尽量的面向对象编程,也 ...

  6. 手写DAO框架(六)-框架使用示例

    一.引入pom <dependency> <groupId>me.lovegao</groupId> <artifactId>gdao</arti ...

  7. 手写MVC框架(一)-再出发

    背景 前段时间把之前写的DAO框架(手写DAO框架(一)-从“1”开始)整理了一下,重构了一版.整理过程中看以前写的代码,只是为了了解实现,只是为了实现,代码写的有点粗糙.既然已经整理了DAO框架,索 ...

  8. 手写MQ框架(一)-准备启程

    一.背景 很久以前写了DAO框架和MVC框架,前段时间又重写了DAO框架-GDAO(手写DAO框架(一)-从“1”开始,源码:https://github.com/shuimutong/gdao.gi ...

  9. 手写MQ框架(二)-服务端实现

    一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...

随机推荐

  1. struct框架

    配置文件struct-config.xml<?xml version="1.0"encoding="UTF-8"?><!DOCTYPE str ...

  2. jquery中$()的使用

    在jquery中最常使用的就是$这个符号了,在我没有系统的学习jquery之前,我用到的$都是用于对元素的选择,而这只是$的很简单的用法.在jquery$()函数一共有三种用法: $(selector ...

  3. 搞笑代码注释,佛祖保佑 永无BUG

    佛祖保佑 永无BUG 上传图片即可生成字符画,效果还不错, https://www.fontke.com/tool/image2ascii/ 神注释大全 https://github.com/Blan ...

  4. hdu 2209 翻纸牌游戏【贪心】

    本来是冲着搜索去的--其实可以贪心 因为能改变第一位的只有第一位和第二位,然后改完之后后面的同理,也就是说只要贪心的推一遍就可以 但是注意要在翻第一个和不翻第一个之间取个min #include< ...

  5. P1606 [USACO07FEB]荷叶塘Lilypad Pond(最短路计数)

    P1606 [USACO07FEB]荷叶塘Lilypad Pond 题目描述 FJ has installed a beautiful pond for his cows' aesthetic enj ...

  6. cookie使用详解

    cookie是用来保存客户资料的好方法,与同样可以用来保存客户资料的 session不同的是,session是把资料保存在服务器端,而cookie是把资料保存在客户端,我们平常接触的最多的cookie ...

  7. 自定义View(10)*onSizeChanged,onMeasure,onDraw的注意事项及正确写法

    1,onSizeChanged 触发: 当view的第一次分配大小或以后大小改变时的产生的事件. 工作: 计算绘制内容的位置,面积等相关值.避免每次在onDraw中计算了. 注意: 计算时不要忘记pa ...

  8. wordpress网站底部的运行时间是怎么设置的?

    别人网站底部显示的网站运行时间是什么设置的?是插件的效果吗? 不用插件,一段JS代码就可以实现同样的效果. 复制如下代码到 footer.php 页脚那里 <span id="runt ...

  9. ES6特性之模块【Modules】

    ES6之前已经出现了js模块加载的方案,最主要的是CommonJS和AMD规范.commonjs主要应用于服务器,实现同步加载,如nodejs.AMD规范应用于浏览器,如requirejs,为异步加载 ...

  10. ubuntu下删除和新建用户(并有su权限)

    http://blog.csdn.net/speedme/article/details/8206144ubuntu下删除和新建用户(并有su权限) 如何创建ubuntu新用户?输入:sudo add ...