-------前篇:手写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. uva10655

    Given the value of a+b and ab you will have to find the value of a n + b n Input The input file cont ...

  2. 【转】整套完整安全的API接口解决方案

    原文地址:http://www.cnblogs.com/hubro/p/6248353.html 在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API ...

  3. L1范数和L2范数

    给定向量x=(x1,x2,...xn)L1范数:向量各个元素绝对值之和L2范数:向量各个元素的平方求和然后求平方根Lp范数:向量各个元素绝对值的p次方求和然后求1/p次方L∞范数:向量各个元素求绝对值 ...

  4. nginx - ubutun下安装nginx(详述编译方法)

    一.使用apt命令安装 sudo apt-get install nginx 二.编译方法安装(个人实践方法,具体见官方文档) 1)说明:我使用的虚拟机是64位 ubuntu server14.04, ...

  5. js moment.js日期操作类 datetime,日期操作,dayjs

    http://momentjs.com/ JS时间处理插件MomentJS https://juejin.im/post/5a2bdc55f265da432b4abf5e Day.js 2kB超轻量时 ...

  6. java 线程开元篇

    学习java的读者都知道,Java的每个对象都会有默认的12个方法,这12个方法分别是 object() finalize() hashCode() equals() wait() wait(long ...

  7. (分治)51NOD 1019 逆序数

    在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序.一个排列中逆序的总数就称为这个排列的逆序数.   如2 4 3 1中,2 1,4 3,4 1,3 1是 ...

  8. 题解报告:hdu 1061 Rightmost Digit(快速幂取模)

    Problem Description Given a positive integer N, you should output the most right digit of N^N. Input ...

  9. 377 Combination Sum IV 组合之和 IV

    Given an integer array with all positive numbers and no duplicates, find the number of possible comb ...

  10. java 装饰者类

    装饰者模式:增强一个类的功能还可以让装饰者类之间互相装饰. 装饰者模式和继承的区别: 继承实现的增强类: 优点:代码结构清晰,而且实现简单 缺点:对于每一个的需要增强的类都要创建具体的子类来帮助其增强 ...