[续上文《JDBC数据源连接池(3)---Tomcat集成DBCP》]

我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究其原理,我在这里写一个自定义的数据源连接池。

我先在com.itszt.utils包下新建一个Utils_5_mydscp 文件夹,在该文件夹下写自定义的数据源连接池工具;同时,我在src包下建config_utils_5文件夹,该文件夹下建一个属性配置文件mydscp.properties,该属性配置文件信息如下:

DRIVER_NAME=com.mysql.jdbc.Driver
JDBC_URL=jdbc:mysql://localhost:3306/itszt2
USERNAME=root
PASSWORD=2017
INITIAL_SIZE=10
MAX_ACTIVE=100
MAX_IDLE=20
MIN_IDLE=10

接着,分析该数据源连接池,需要一个解析属性配置文件的类,需要一个数据源(拟采用单例模式)类,还需要一个Connection类;为了降低耦合度,采用工厂模式获得数据源实例;考虑到异常的个性化处理,还可以自定义异常。

有了上述分析后,第一步,写一个解析属性配置文件的工具类MyDSCPConfig,代码如下:

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; public abstract class MyDSCPConfig {
public static String DRIVER_NAME = null;
public static String JDBC_URL = null;
public static String USERNAME = null;
public static String PASSWORD = null;
public static Integer INITIAL_SIZE = null;
public static Integer MAX_ACTIVE = null;
public static Integer MAX_IDLE = null;
public static Integer MIN_IDLE = null; /**
* 解析属性配置文件
*
* @param propertiesPath
*/
public static void parseConfig(String propertiesPath) {
Properties properties = new Properties();
InputStream resourceAsStream = MyDSCPConfig.class.getClassLoader().getResourceAsStream(propertiesPath);
try {
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
DRIVER_NAME = properties.getProperty("DRIVER_NAME");
JDBC_URL = properties.getProperty("JDBC_URL");
USERNAME = properties.getProperty("USERNAME");
PASSWORD = properties.getProperty("PASSWORD");
INITIAL_SIZE = Integer.parseInt(properties.getProperty("INITIAL_SIZE"));
MAX_ACTIVE = Integer.parseInt(properties.getProperty("MAX_ACTIVE"));
MAX_IDLE = Integer.parseInt(properties.getProperty("MAX_IDLE"));
MIN_IDLE = Integer.parseInt(properties.getProperty("MIN_IDLE"));
}
}  

第二步,先采用适配器模式,写一个实现了Connection 接口的MyConnectionAdapter抽象类,便于MyConnectionAdapter的子类有选择地覆写所需的方法,MyConnectionAdapter类代码如下:

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor; /**
* 适配器
*/
public abstract class MyConnectionAdapter implements Connection {
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
} @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
} @Override
public Statement createStatement() throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return null;
} @Override
public CallableStatement prepareCall(String sql) throws SQLException {
return null;
} @Override
public String nativeSQL(String sql) throws SQLException {
return null;
} @Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
} @Override
public boolean getAutoCommit() throws SQLException {
return false;
} @Override
public void commit() throws SQLException {
} @Override
public void rollback() throws SQLException {
} @Override
public void close() throws SQLException {
} @Override
public boolean isClosed() throws SQLException {
return false;
} @Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
} @Override
public void setReadOnly(boolean readOnly) throws SQLException {
} @Override
public boolean isReadOnly() throws SQLException {
return false;
} @Override
public void setCatalog(String catalog) throws SQLException {
} @Override
public String getCatalog() throws SQLException {
return null;
} @Override
public void setTransactionIsolation(int level) throws SQLException {
} @Override
public int getTransactionIsolation() throws SQLException {
return 0;
} @Override
public SQLWarning getWarnings() throws SQLException {
return null;
} @Override
public void clearWarnings() throws SQLException {
} @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
return null;
} @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
} @Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
} @Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
} @Override
public void setHoldability(int holdability) throws SQLException {
} @Override
public int getHoldability() throws SQLException {
return 0;
} @Override
public Savepoint setSavepoint() throws SQLException {
return null;
} @Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
} @Override
public void rollback(Savepoint savepoint) throws SQLException {
} @Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
} @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return null;
} @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return null;
} @Override
public Clob createClob() throws SQLException {
return null;
} @Override
public Blob createBlob() throws SQLException {
return null;
} @Override
public NClob createNClob() throws SQLException {
return null;
} @Override
public SQLXML createSQLXML() throws SQLException {
return null;
} @Override
public boolean isValid(int timeout) throws SQLException {
return false;
} @Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
} @Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
} @Override
public String getClientInfo(String name) throws SQLException {
return null;
} @Override
public Properties getClientInfo() throws SQLException {
return null;
} @Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
} @Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
} @Override
public void setSchema(String schema) throws SQLException {
} @Override
public String getSchema() throws SQLException {
return null;
} @Override
public void abort(Executor executor) throws SQLException {
} @Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
} @Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
}  

第三步,再写一个继承了MyConnectionAdapter的子类MyConnection,代码如下:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement; /**
* 采用装饰者模式,基于Conneciton原有的close()功能,扩展其close()功能
*/
public class MyConnection extends MyConnectionAdapter {
private Connection connection;
private MyDataSource dataSource; public MyConnection(Connection connection, MyDataSource dataSource) {
this.connection = connection;
this.dataSource = dataSource;
} @Override
public Statement createStatement() throws SQLException {
return connection.createStatement();
} @Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
} @Override
public void close() throws SQLException {
dataSource.recycle(this);
} /*
* 关闭连接,释放资源
*/
public void dispose() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}  

第四步,写一个继承了SQLException的自定义异常MyConnectionExp,代码如下:

import java.sql.SQLException;
public class MyConnectionExp extends SQLException {
public MyConnectionExp(String reason) {
super(reason);
}
}  

第五步,写一个实现了DataSource接口的抽象类MyDataSourceAdapter,覆写接口的所有方法,该类代码如下:

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
* 适配器
*/
public abstract class MyDataSourceAdapter implements DataSource {
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
} @Override
public void setLogWriter(PrintWriter out) throws SQLException {
} @Override
public void setLoginTimeout(int seconds) throws SQLException {
} @Override
public int getLoginTimeout() throws SQLException {
return 0;
} @Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
} @Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
} @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
} @Override
public Connection getConnection() throws SQLException {
return null;
} @Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
}  

第六步,写一个继承了MyDataSourceAdapter 抽象类的子类MyDataSource,由于数据源连接池只能有一个,故采用单例模式,该子类代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
/**
* 单例模式
*/
public class MyDataSource extends MyDataSourceAdapter {
private static MyDataSource myDataSource;
private LinkedList<MyConnection> connectionQueue = new LinkedList<>();
private int totalActive = MyDSCPConfig.MAX_ACTIVE; static {
// 加载数据库驱动
try {
Class.forName(MyDSCPConfig.DRIVER_NAME);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} private MyDataSource() {
} //懒汉模式,同步,确保线程安全
public static MyDataSource getInstance() {
if (myDataSource == null) {
synchronized (MyDataSource.class) {
if (myDataSource == null) {
myDataSource = new MyDataSource();
myDataSource.initialize();
}
}
}
return myDataSource;
} //初始化方法
public void initialize() {
for (int i = 0; i < MyDSCPConfig.INITIAL_SIZE; i++) {
MyConnection connection = produceConnection();
if (connection == null) {
continue;
}
connectionQueue.add(connection);
}
} //构建一个连接的方法
private MyConnection produceConnection() {
try {
Connection connection = DriverManager.getConnection(MyDSCPConfig.JDBC_URL, MyDSCPConfig.USERNAME, MyDSCPConfig.PASSWORD);
return new MyConnection(connection, this);
} catch (SQLException e) {
MyConnectionExp exp = new MyConnectionExp("获取连接失败!");
exp.printStackTrace();
}
return null;
} //从连接池中获取连接
@Override
public Connection getConnection() throws SQLException {
int num1 = connectionQueue.size();
MyConnection myConnection = null;
if (this.totalActive > 0) {
if (num1 >= 1) {
myConnection = connectionQueue.pop();
this.totalActive--;
} else {
System.err.println("连接池已无空闲连接,需生产后才能使用!");
myConnection = produceConnection();
connectionQueue.add(myConnection);
getConnection();
}
int num2 = connectionQueue.size();
System.out.println("获取连接,原先有 " + num1 + " 个,现在有 " + num2 + " 个");
if (num2 < MyDSCPConfig.MIN_IDLE) {
System.out.println("不足最小空闲连接数,生产一个新的!");
connectionQueue.add(produceConnection());
}
} else {
System.err.println("已有一次超出最大连接数!");
this.totalActive = MyDSCPConfig.MAX_ACTIVE;
getConnection();
}
return myConnection;
} /*
* 回收连接
*/
protected void recycle(MyConnection myConnection) {
if (connectionQueue.size() >= MyDSCPConfig.MAX_IDLE) {
System.out.println("超出空闲连接数上限,不再回收!");
myConnection.dispose();
return;
} else {
connectionQueue.add(myConnection);
this.totalActive++;
}
}
}  

第七步,基于工厂模式,写一个获取数据源连接池实例的类MyDataSourceFactory,该类代码如下:

/**
* 工厂模式
*/
public abstract class MyDataSourceFactory {
public static MyDataSource createDataSource(String propertiesPath) {
//1.解析配置文件中的配置
MyDSCPConfig.parseConfig(propertiesPath);
//2.依据上一步骤的设置,生产我们的数据源
return MyDataSource.getInstance();
}
}  

最后,在com.itszt.demo文件夹中的LoginServlet.java的Servlet文件中,将产生Connection对象的方式修改为自定义的数据源连接池方式即可。

总结:在Web项目中,我们经常需要频繁连接数据库,采用数据源连接池的方式,能够有效地解决建立数据库连接时耗费较多CPU、时间等资源的问题,从而提高应用性能,改进用户体验。

JDBC数据源连接池(4)---自定义数据源连接池的更多相关文章

  1. JavaWeb之数据源连接池(4)---自定义数据源连接池

    [续上文<JavaWeb之数据源连接池(3)---Tomcat>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究其原 ...

  2. SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)

    一.引入依赖 引入数据库连接池的依赖--druid和面向切面编程的依赖--aop,如下所示: <!-- druid --> <dependency> <groupId&g ...

  3. JDBC 学习笔记(三)—— 数据源(数据库连接池):DBCP数据源、C3P0 数据源以及自定义数据源技术

    本文目录:        1.应用程序直接获取连接的缺点(图解)        2.使用数据库连接池优化程序性能(图解)        3.可扩展增强某个类方法的功能的三种方式        4.自定 ...

  4. Loadrunner参数化连接oracle、mysql数据源报错及解决办法

    Loadrunner参数化连接oracle.mysql数据源报错及解决办法 (本人系统是Win7 64,  两位小伙伴因为是默认安装lr,安装在 最终参数化的时候,出现连接字符串无法自动加载出来: 最 ...

  5. JDBC实例--JDBC连接池技术解密,连接池对我们不再陌生

    一.为什么我们要用连接池技术? 前面的数据库连接的建立及关闭资源的方法有些缺陷.统舱传统数据库访问方式:一次数据库访问对应一个物理连接,每次操作数据库都要打开.关闭该物理连接, 系统性能严重受损. 解 ...

  6. SpringMVC配置双数据源,一个java项目同时连接两个数据库

    数据源在配置文件中的配置 请点击--->   java架构师项目实战,高并发集群分布式,大数据高可用,视频教程 <pre name="code" class=" ...

  7. 自定义redis连接池(字典操作)

    pool=redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=1000)conn=redis.Redis(connecti ...

  8. druid连接池获取不到连接的一种情况

    数据源一开始配置: jdbc.initialSize=1jdbc.minIdle=1jdbc.maxActive=5 程序运行一段时间后,执行查询抛如下异常: exception=org.mybati ...

  9. (转)WebSphere 中池资源调优 - 线程池、连接池和 ORB

    WebSphere 中池资源调优 - 线程池.连接池和 ORB 来自:https://www.ibm.com/developerworks/cn/websphere/library/techartic ...

随机推荐

  1. 【刷题】BZOJ 1003 [ZJOI2006]物流运输

    Description 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格 ...

  2. 洛谷 P2757 [国家集训队]等差子序列 解题报告

    P2757 [国家集训队]等差子序列 题目描述 给一个\(1\)到\(N\)的排列\(\{A_i\}\),询问是否存在 \[1 \le p_1<p_2<p_3<p_4<p_5& ...

  3. jquery 操作实例一

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...

  4. RGB向yuv的转化最优算法,快得让你吃惊!

    朋友曾经给我推荐了一个有关代码优化的pdf文档<让你的软件飞起来>,看完之后,感受颇深.为了推广其,同时也为了自己加深印象,故将其总结为word文档.下面就是其的详细内容总结,希望能于己于 ...

  5. Codeforces Round #344 (Div. 2) A

    A. Interview time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  6. Atlantis HDU - 1542 (线段树扫描线)

    There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some ...

  7. 手脱ASProtect v1.23 RC1(有Stolen Code)

    1.载入PEID ASProtect v1.23 RC1 常见ASprotect版本壳: ASProtect 1.23 RC4 按shift+f9键26次后来到典型异常 ASProtect 1.31 ...

  8. redis 查看所有键值

    zb@zb-computer:/home/wwwroot/default/lion/Admin$ /usr/local/redis/bin/redis-cli 127.0.0.1:6379> k ...

  9. C++指针与数组

    对数组地址的理解,如 int c[2] = {2,3}; int(*cp)[2] = &c; cout << &c[0] << c << cp &l ...

  10. 上下文路径request.getContextPath();与${pageContext.request.contextPath}

    (1) request.getContextPath();与${pageContext.request.contextPath}都是获取上下文路径: 1. request.getContextPath ...