一、什么是JDBC连接池?

在传统的JDBC连接中,每次获得一个Connection连接都需要加载通过一些繁杂的代码去获取,例如以下代码:

public static Connection getConn(){
Connection conn = null;
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, user, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}

这样繁杂的操作只为了获取一次连接,当然,我们可以将其封装成一个工具类来访问(上图以封装好Connection的连接),但是每一次连接都需要取加载一次是不是很浪费性能,为了优化性能,那么就出现了连接池。

连接池在初始化的时候就创建了几个连接供我们使用,当我们需要连接时只需要从连接池中获取已存在的连接,当初始化的几个连接都没有时,会重新创建一个连接,使用完连接后不会去销毁连接,而是归还给连接池供后面需要连接的使用。(当然,连接池不仅仅只是这么简单,这里就只做这些介绍)

常用的连接池有DBCP、C3P0,现在最主流是好像是阿里的Druid连接池,还有tomcat的自带的JNDI连接池

二、自定义一个简单的连接池

对自定义连接池的分析:

1.2.因为是连接池 ,我们需要实现DataSource接口,并实现其中的方法,基于我们的情况,我们关于与getConnection()方法;

2.既然要存放几个连接对象,那么我们用一个集合来存放它,基于会经常操作增加和删除那么选用LinkedList;

3.连接的销毁并不是销毁连接,而是将连接归还给连接池

编码:

1.创建一个类MyDataSource 并实现DataSource接口

当此类加载时它就需要有一个容器来存放Connection,所以定义一个静态的属性:

private static List<Connection> connectionList = new LinkedList<>();

2.因为需要取获得数据库连接,所以我们封装一个获取数据库连接的方法

public Connection getOneConnection(){
Connection conn = null;
try{
//此处通过外部的properties文件来获取的数据,这样更加灵活。
InputStream in = MyDataSource.class.getClassLoader().
getResourceAsStream("jdbc/jdbc.properties");
Properties pro = new Properties();
pro.load(in);
driver = pro.getProperty("driver");
url = pro.getProperty("url");
username = pro.getProperty("user");
password = pro.getProperty("password");
Class.forName(driver);
conn = DriverManager.getConnection(url,username,password);
}catch (Exception e){
e.getStackTrace();
}
return conn;
}

注意的是我这里通过propertie文件的获取的数据,可根据实际情况来选择

3.初始化几个连接放入容器中。可以使用静态代码块来实现,但是如果没有使用此数据源那么就造成了资源的浪费,所以我考虑将初始化几个连接的实现放到他的构造方法中,即当需要此连接池的时候他才会随之创建几个连接。如下:

public MyDataSource() {
for (int i = 0; i < 5; i++) {
Connection conn = getOneConnection();//调用创建连接的方法
connectionList.add(conn);
}
}

4.现在开始重写外部从此连接池中获取连接的方法getConnection()

@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
if(connectionList == null || connectionList.size() <= 0){
Connection connection = getConnection();
connectionList.add(connection);
}
conn = connectionList.remove(0);
return conn;
}

5.创建一个对象返回的方法,即将用完的连接放入归还到连接池中

public void backConnection(Connection conn){
connectionList.add(conn);
}

OK,这样就完成了一个简单的自定义连接池,测试代码如下:

 public static void main( String[] args ) throws SQLException
{
MyDataSource dataSource = new MyDataSource();
Connection conn = dataSource.getConnection();
String sql = "select * from user where u_id = ?";
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
ps.setInt(1, 1);
rs = ps.executeQuery();
while (rs.next()) {
System.out.println("id="+rs.getInt(1));
System.out.println("username="+rs.getString(2));
System.out.println("password="+rs.getString(3));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
dataSource.backConnection(conn);
}
}

因为忽略,我的代码中没有关闭其他两个对象。

现在有一个小问题就是,我们的关闭连接是通过连接池的方法来实现的,但是,如果用户调用Connection对象的close方法,那么连接时被销毁了,并没有返回给连接池,那么我们来优化它,让用户使用close()方法不会去销毁连接,而是去归还连接。

方案有很多中,这里采用装饰着模式的一种。

优化:

1.新建一个类MyConnection来实现Connection接口,其中他有属性类型为Connection conn和一个Liis<Connection>。

private Connection conn;
private List<Connection> pool; public MyConnection(Connection conn, List<Connection> pool) {
this.conn = conn;
this.pool = pool;
}

2.然后实现接口的close方法

    @Override
public void close() throws SQLException {
System.out.println("回收连接");
pool.add(conn);
}

3.然后实现他获取Statement的方法,如果不实现那么获取此Statement会出现空指针错误,我这里就只实现了PreparedStatement的获取方法

@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
System.out.println("获得Statement");
return conn.prepareStatement(sql);
}

4.然后删除掉MyDataSource类中归还连接的方法backConnection,并将构造方法和获取连接的方法做如下改造

public MyDataSource2() {
for (int i = 0; i < 5; i++) {
Connection conn = getOneConnection();
MyConnection myConn = new MyConnection(conn, connectionList);
connectionList.add(myConn);
}
}
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
if(connectionList == null || connectionList.size() <= 0){
Connection connection = getConnection();
MyConnection myConn = new MyConnection(connection, connectionList);
connectionList.add(myConn);
}
conn = connectionList.remove(0);
return conn;
}

好了,这样用户直接调用我们的Connection的close方法就不会去销毁连接了,会正确的归还给了连接池了,对测试代码稍做修改即可测试。

自定义一个简单的JDBC连接池的更多相关文章

  1. Go语言之从0到1实现一个简单的Redis连接池

    Go语言之从0到1实现一个简单的Redis连接池 前言 最近学习了一些Go语言开发相关内容,但是苦于手头没有可以练手的项目,学的时候理解不清楚,学过容易忘. 结合之前组内分享时学到的Redis相关知识 ...

  2. 【Java编程】建立一个简单的JDBC连接-Drivers, Connection, Statement and PreparedStatement

    本blog提供了一个简单的通过JDBC驱动建立JDBC连接例程.并分别通过Statement和PreparedStatement实现对数据库的查询. 在下一篇blog中将重点比較Statement与P ...

  3. 【JDBC&Dbutils】JDBC&JDBC连接池&DBUtils使用方法(重要)

    -----------------------JDBC---------- 0.      db.properties文件 driver=com.mysql.jdbc.Driver url=jdbc: ...

  4. JDBC连接池&DBUtils使用

    使用连接池改造JDBC的工具类: 1.1.1          需求: 传统JDBC的操作,对连接的对象销毁不是特别好.每次创建和销毁连接都是需要花费时间.可以使用连接池优化的程序. * 在程序开始的 ...

  5. 用swoole简单实现MySQL连接池

    MySQL连接池 在传统的网站开发中,比如LNMP模式,由Nginx的master进程接收请求然后分给多个worker进程,每个worker进程再链接php-fpm的master进程,php-fpm再 ...

  6. 转 Swoole】用swoole简单实现MySQL连接池

    MySQL连接池 在传统的网站开发中,比如LNMP模式,由Nginx的master进程接收请求然后分给多个worker进程,每个worker进程再链接php-fpm的master进程,php-fpm再 ...

  7. JDBC连接池-自定义连接池

    JDBC连接池 java JDBC连接中用到Connection   在每次对数据进行增删查改 都要 开启  .关闭  ,在实例开发项目中 ,浪费了很大的资源 ,以下是之前连接JDBC的案例 pack ...

  8. jdbc连接池&改进dbUtil成C3P0Util

    一.jdbc连接池 1.连接池的存在理由   前面介绍的dbUtils工具类虽然实现了一个对jdbc的简单封装.但它依旧采取从驱动管理获取连接 (DriverManager.getConnection ...

  9. Tomcat 的 JDBC 连接池

    JDBC 连接池 org.apache.tomcat.jdbc.pool 是 Apache Commons DBCP 连接池的一种替换或备选方案. 那究竟为何需要一个新的连接池? 原因如下: Comm ...

随机推荐

  1. locust参数化(数据库取值)

    locust参数化(数据库取值) 基于上一篇参数化的梳理,本篇用另一种方法从数据库中取出这100个用户来登录 思路:在 TaskSet 中的 on_start 方法表示执行任务前的操作,可以将数据库取 ...

  2. IDEA2019.2.1中文乱码解决

    写在前面 太晚了, 长话短说, idea更新到2019.2.1, 项目任何地方输入中文都是乱码, 修改编码UTF-8依然如此.参考https://blog.csdn.net/chenjk10/arti ...

  3. git 项目最常用命令总结

    本文为博主原创,未经允许不得转载: 1.查看git基础配置信息 1.查看用户名和邮箱地址 git config user.name   git config user.email 2.修改用户名和邮箱 ...

  4. vue---数据列表过滤筛选

    使用vue进行数据过滤筛选是比较常用的功能,常见的使用场景就是搜索框数据筛选过滤了.简单示例: <template> <div> <input type="te ...

  5. Robot Framwork关键字驱动+RedwoodHQ安装

    一.Robot Framwork介绍 Robot Framwork是一款python编写的功能框架.具备良好的可扩展性,支持关键字驱动,可以同时测试多种类型的客户端或者接口,可以进行分布式测试执行. ...

  6. Spring cloud微服务安全实战-7-7自定义metrics监控指标(2)

    Gauge用来显示单词一个数的 勾选,这里编程仪表盘 设置仪表盘的最大值.最小值 保存 直接保存 保存成功的提示 返回 这就是我们做的一个简单的仪表盘 这个不适合我们的counter,因为没有最大值 ...

  7. Qt编写图片及视频TCP/UDP网络传输

    一.前言 很多年前就做过类似的项目,无非就是将本地的图片上传到服务器,就这么简单,其实用http的post上传比较简单容易,无需自定义协议,直接设置好二进制数据即可,而采用TCP或者UDP通信的话,必 ...

  8. [LeetCode] 62. Unique Paths 唯一路径

    A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The ...

  9. teamviewer破解版

    简介 TeamViewer是一个能在任何防火墙和NAT代理的后台用于远程控制的应用程序,桌面共享和文件传输的简单且快速的解决方案.为了连接到另一台计算机,只需要在两台计算机上同时运行 TeamView ...

  10. SpringBoot系列教程web篇之如何自定义参数解析器

    title: 190831-SpringBoot系列教程web篇之如何自定义参数解析器 banner: /spring-blog/imgs/190831/logo.jpg tags: 请求参数 cat ...