JDBC数据源连接池(4)---自定义数据源连接池
[续上文《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)---自定义数据源连接池的更多相关文章
- JavaWeb之数据源连接池(4)---自定义数据源连接池
[续上文<JavaWeb之数据源连接池(3)---Tomcat>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究其原 ...
- SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)
一.引入依赖 引入数据库连接池的依赖--druid和面向切面编程的依赖--aop,如下所示: <!-- druid --> <dependency> <groupId&g ...
- JDBC 学习笔记(三)—— 数据源(数据库连接池):DBCP数据源、C3P0 数据源以及自定义数据源技术
本文目录: 1.应用程序直接获取连接的缺点(图解) 2.使用数据库连接池优化程序性能(图解) 3.可扩展增强某个类方法的功能的三种方式 4.自定 ...
- Loadrunner参数化连接oracle、mysql数据源报错及解决办法
Loadrunner参数化连接oracle.mysql数据源报错及解决办法 (本人系统是Win7 64, 两位小伙伴因为是默认安装lr,安装在 最终参数化的时候,出现连接字符串无法自动加载出来: 最 ...
- JDBC实例--JDBC连接池技术解密,连接池对我们不再陌生
一.为什么我们要用连接池技术? 前面的数据库连接的建立及关闭资源的方法有些缺陷.统舱传统数据库访问方式:一次数据库访问对应一个物理连接,每次操作数据库都要打开.关闭该物理连接, 系统性能严重受损. 解 ...
- SpringMVC配置双数据源,一个java项目同时连接两个数据库
数据源在配置文件中的配置 请点击---> java架构师项目实战,高并发集群分布式,大数据高可用,视频教程 <pre name="code" class=" ...
- 自定义redis连接池(字典操作)
pool=redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=1000)conn=redis.Redis(connecti ...
- druid连接池获取不到连接的一种情况
数据源一开始配置: jdbc.initialSize=1jdbc.minIdle=1jdbc.maxActive=5 程序运行一段时间后,执行查询抛如下异常: exception=org.mybati ...
- (转)WebSphere 中池资源调优 - 线程池、连接池和 ORB
WebSphere 中池资源调优 - 线程池.连接池和 ORB 来自:https://www.ibm.com/developerworks/cn/websphere/library/techartic ...
随机推荐
- [洛谷P4345][SHOI2015]超能粒子炮·改
题目大意:给你$n,k$,求:$$\sum\limits_{i=0}^k\binom n i\pmod{2333}$$题解:令$p=2333,f(n,k)\equiv\sum\limits_{i=0} ...
- JS获取移动端系统信息(操作系统、操作系统版本、横竖屏状态、设备类型、网络状态、生成浏览器指纹)
function getOS() { // 获取当前操作系统 var os; if (navigator.userAgent.indexOf('Android') > -1 || navigat ...
- 2017-7-18-每日博客-关于Linux下的鲜为人知的10条命令.doc
这篇文章的目的是介绍一些少有人知的Linux命令,它们一定会高效地帮你管理你的桌面/服务器. 1. sudo !!命令 没有特定输入sudo命令而运行,将给出没有权限的错误.那么,你不需要重写整个命令 ...
- Efficient Deblurring for Shaken and Partially Saturated Images
Try the online demo: http://willow-fd.rocq.inria.fr/unshake/ Overview One common feature of “shaken” ...
- nfs挂载权限问题
问题: 服务器A:192.168.10.230 服务器B:192.168.10.231 由于服务器A空间不足,打算将服务器A产生的数据库日志挂载到服务器B上,刚开始设定的anonuid和anongid ...
- PhoneGap API介绍:File
本文将介绍PhoneGap API——File:通过JavaScript截获本地文件系统.File是用于读取.写入和浏览文件系统层次结构的PhoneGap API. 对象: DirectoryEntr ...
- Educational Codeforces Round 48 (Rated for Div. 2) CD题解
Educational Codeforces Round 48 (Rated for Div. 2) C. Vasya And The Mushrooms 题目链接:https://codeforce ...
- LightOJ 1375 - LCM Extreme 莫比乌斯反演或欧拉扩展
题意:给出n [1,3*1e6] 求 并模2^64. 思路:先手写出算式 观察发现可以化成 那么关键在于如何求得i为1~n的lcm(i,n)之和.可以知道lcm(a,b)为ab/gcd(a,b) 变换 ...
- jsp 内置对象二
1.什么是session ? (1)session 表示客户端与服务器的一次回话. 2)Web中的session指的是用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网 ...
- Maven搭建SpringMVC + SpringJDBC项目详解
前言 上一次复习搭建了SpringMVC+Mybatis,这次搭建一下SpringMVC,采用的是SpringJDBC,没有采用任何其他的ORM框架,SpringMVC提供了一整套的WEB框架,所以如 ...