博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据库连接池之简介及简单实现
阅读量:3711 次
发布时间:2019-05-21

本文共 5849 字,大约阅读时间需要 19 分钟。

简介

对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。

    连接复用。通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。

    对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所造成的问题的。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。

数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。如:

外部使用者可通过getConnection 方法获取连接,使用完毕后再通过releaseConnection 方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。

数据库连接池技术带来的优势:

1. 资源重用

由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。

2. 更快的系统响应速度

数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。

3. 新的资源分配手段

对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。

4. 统一的连接管理,避免数据库连接泄漏

在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。

连接池类是对某一数据库所有连接的“缓冲池”,主要实现以下功能:①从连接池获取或创建可用连接;②使用完毕之后,把连接返还给连接池;③在系统关闭前,断开所有连接并释放连接占用的系统资源;④还能够处理无效连接(原来登记为可用的连接,由于某种原因不再可用,如超时,通讯问题),并能够限制连接池中的连接总数不低于某个预定值和不超过某个预定值。

目前常用的连接池有:C3P0、DBCP、Druid

简单实现

自定义的数据库连接池ConnPool

package com.yj.pool;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.LinkedList;import java.util.Properties;import java.util.logging.Logger;import javax.sql.DataSource;/** * 采用代理模式简单实现数据库连接池 */public class ConnPool implements DataSource {	// 使用LinkedList集合存放数据库连接	private static LinkedList
connPool = new LinkedList
(); // 在静态代码块中加载配置文件 static { InputStream in = ConnPool.class.getClassLoader().getResourceAsStream("db.properties"); Properties prop = new Properties(); try { prop.load(in); String driver = prop.getProperty("driver"); String url = prop.getProperty("url"); String user = prop.getProperty("user"); String password = prop.getProperty("password"); // 数据库连接池的初始化连接数的大小 int initSize = Integer.parseInt(prop.getProperty("initSize")); // 加载驱动 Class.forName(driver); for (int i = 0; i < initSize; i++) { Connection conn = DriverManager.getConnection(url, user, password); // 将创建的连接添加的list中 System.out.println("初始化数据库连接池,创建第 " + (i + 1) + " 个连接,添加到池中"); connPool.add(conn); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } /** * 获取数据库连接 */ public Connection getConnection() throws SQLException { if (connPool.size() > 0) { // 从集合中获取一个连接 // 现在你一定看懂了我说的为什要用LinkedList了吧,因为下面的这个 // removeFirst()方法会将集合中的第一个元素删除,但是还会返回第一个元素 // 这样就省去了我们很多不必要的麻烦 final Connection conn = connPool.removeFirst(); // 返回Connection的代理对象 System.out.println("拿走了一个连接,池中还剩 " + connPool.size() + " 个连接"); return (Connection) Proxy.newProxyInstance(ConnPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (!"close".equals(method.getName())) { return method.invoke(conn, args); } else { connPool.add(conn); System.out.println("关闭连接,实际还给了连接池"); System.out.println("池中连接数为 " + connPool.size()); return null; } } }); } else { throw new RuntimeException("数据库繁忙,稍后再试"); } } public PrintWriter getLogWriter() throws SQLException { return null; } public void setLogWriter(PrintWriter out) throws SQLException { } public void setLoginTimeout(int seconds) throws SQLException { } public int getLoginTimeout() throws SQLException { return 0; } public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } public Object unwrap(Class iface) throws SQLException { return null; } public boolean isWrapperFor(Class iface) throws SQLException { return false; } public Connection getConnection(String username, String password) throws SQLException { return null; }}

数据库连接池操作工具类JdbcUtil

package com.yj.pool;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;/** * 若在并发情况下,应该有一个ThreadLocal来存储线程对应的连接 */public class JdbcUtil {	// 数据库连接池	private static ConnPool connPool = new ConnPool();	/**	 * 从池中获取一个连接	 * 	 * @return	 * @throws SQLException	 */	public static Connection getConnection() throws SQLException {		return connPool.getConnection();	}	/**	 * 关闭连接	 * 	 * @param conn	 * @param st	 * @param rs	 * @throws SQLException	 */	public static void closeConnection(Connection conn, Statement st, ResultSet rs) throws SQLException {		// 关闭存储查询结果的ResultSet对象		if (rs != null) {			rs.close();		}		// 关闭Statement对象		if (st != null) {			st.close();		}		// 关闭连接		if (conn != null) {			conn.close();		}	}}

db.properties

driver = com.mysql.jdbc.Driverurl = jdbc:mysql://192.168.124.129:3306/docker?useSSL=falseuser = rootpassword = 123456initSize= 15

准备完毕,我们进行单元测试

@Testpublic void connTest() {	try {		Connection conn = JdbcUtil.getConnection();		if (conn != null) {			System.out.println("得到了一个连接");		}		JdbcUtil.closeConnection(conn, null, null);	} catch (SQLException e) {		e.printStackTrace();	}}

查看测试结果

初始化数据库连接池,创建第 1 个连接,添加到池中初始化数据库连接池,创建第 2 个连接,添加到池中初始化数据库连接池,创建第 3 个连接,添加到池中初始化数据库连接池,创建第 4 个连接,添加到池中初始化数据库连接池,创建第 5 个连接,添加到池中初始化数据库连接池,创建第 6 个连接,添加到池中初始化数据库连接池,创建第 7 个连接,添加到池中初始化数据库连接池,创建第 8 个连接,添加到池中初始化数据库连接池,创建第 9 个连接,添加到池中初始化数据库连接池,创建第 10 个连接,添加到池中初始化数据库连接池,创建第 11 个连接,添加到池中初始化数据库连接池,创建第 12 个连接,添加到池中初始化数据库连接池,创建第 13 个连接,添加到池中初始化数据库连接池,创建第 14 个连接,添加到池中初始化数据库连接池,创建第 15 个连接,添加到池中拿走了一个连接,池中还剩 14 个连接得到了一个连接关闭连接,实际还给了连接池池中连接数为 15

 

转载地址:http://rpsjn.baihongyu.com/

你可能感兴趣的文章
ENSP 静态路由加VLANIF实例
查看>>
enspVLAN 实战项目
查看>>
Openstack Tarin版超详细部署及使用
查看>>
Python一行代码实现正三角形
查看>>
java基础之多线程
查看>>
CentOS6.8安装
查看>>
vue cli3修改默认的端口和vue cli3关闭烦人的eslint语法检查
查看>>
购物车功能设计(一)
查看>>
购物车功能设计(二)(使用redis实现购物车功能)
查看>>
springboot2.0 解决前后端跨域问题
查看>>
基于SSM框架实现的项目——绿色蔬果商城
查看>>
基于SSM框架实现的项目——绿色蔬果商城后台
查看>>
基于springboot+mybatis+vue+elementUI实现的网欲音乐系统
查看>>
springboot+vue实现滑块验证登入
查看>>
使用shiro实现多Realm认证——用户名密码、手机短信验证
查看>>
基于springboot+shiro阿里云短信服务实现短信验证登入
查看>>
非常好使的客户端redis工具Another Redis Desktop Manager
查看>>
一款基于 Spring Boot 2.1.0 、 Jpa、 Spring Security、redis、Vue 的前后端分离的后台管理系统
查看>>
Spring boot整合JWT实现登入认证
查看>>
使用requests模块爬取豆瓣电影top50数据
查看>>