自定义一个简单的JDBC连接池
一、什么是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连接池的更多相关文章
- Go语言之从0到1实现一个简单的Redis连接池
Go语言之从0到1实现一个简单的Redis连接池 前言 最近学习了一些Go语言开发相关内容,但是苦于手头没有可以练手的项目,学的时候理解不清楚,学过容易忘. 结合之前组内分享时学到的Redis相关知识 ...
- 【Java编程】建立一个简单的JDBC连接-Drivers, Connection, Statement and PreparedStatement
本blog提供了一个简单的通过JDBC驱动建立JDBC连接例程.并分别通过Statement和PreparedStatement实现对数据库的查询. 在下一篇blog中将重点比較Statement与P ...
- 【JDBC&Dbutils】JDBC&JDBC连接池&DBUtils使用方法(重要)
-----------------------JDBC---------- 0. db.properties文件 driver=com.mysql.jdbc.Driver url=jdbc: ...
- JDBC连接池&DBUtils使用
使用连接池改造JDBC的工具类: 1.1.1 需求: 传统JDBC的操作,对连接的对象销毁不是特别好.每次创建和销毁连接都是需要花费时间.可以使用连接池优化的程序. * 在程序开始的 ...
- 用swoole简单实现MySQL连接池
MySQL连接池 在传统的网站开发中,比如LNMP模式,由Nginx的master进程接收请求然后分给多个worker进程,每个worker进程再链接php-fpm的master进程,php-fpm再 ...
- 转 Swoole】用swoole简单实现MySQL连接池
MySQL连接池 在传统的网站开发中,比如LNMP模式,由Nginx的master进程接收请求然后分给多个worker进程,每个worker进程再链接php-fpm的master进程,php-fpm再 ...
- JDBC连接池-自定义连接池
JDBC连接池 java JDBC连接中用到Connection 在每次对数据进行增删查改 都要 开启 .关闭 ,在实例开发项目中 ,浪费了很大的资源 ,以下是之前连接JDBC的案例 pack ...
- jdbc连接池&改进dbUtil成C3P0Util
一.jdbc连接池 1.连接池的存在理由 前面介绍的dbUtils工具类虽然实现了一个对jdbc的简单封装.但它依旧采取从驱动管理获取连接 (DriverManager.getConnection ...
- Tomcat 的 JDBC 连接池
JDBC 连接池 org.apache.tomcat.jdbc.pool 是 Apache Commons DBCP 连接池的一种替换或备选方案. 那究竟为何需要一个新的连接池? 原因如下: Comm ...
随机推荐
- Post Setting Proxy 设置 代理
postman的代理使用篇(四) - codingstudy - SegmentFault 思否https://segmentfault.com/a/1190000012024844 postman ...
- Java基础 while 简单示例
JDK :OpenJDK-11 OS :CentOS 7.6.1810 IDE :Eclipse 2019‑03 typesetting :Markdown code ...
- 【winform】主窗体多线程给子窗体传值
1.主窗体多线程给子窗体传值 解决方案:主要使用委托,因为会出现跨线程错误 主窗体 public FormMain() { InitializeComponent(); //background th ...
- rpm和deb包制作和升级执行步骤
deb安装包升级和安装都是dpkg -i .deb命令,升级时直接覆盖安装新包安装步骤:1 preinst install2 postinst configure升级步骤:1 先执行旧包prerm u ...
- dubbo调用外网接口 注册外网ip到zookeeper 暴露外网ip
dubbo注册时会通过主机名寻找ip,会将内网ip注册到zookeeper 如果我们调用外网服务器server的话是找不到的 会报如下错误 [DUBBO] client reconnect to 12 ...
- 机器学习技法总结(四)(aggregation,vote,bootstrap...)
研究的动机是:我们采用了不同的模型得到T个不同的g,那么我们是不是可以通过这些不同的g的融合得到更加出色的G呢?因此,便有了以上四种不同的方法:1)(select)直接选择最好的一个作为融合的结果:2 ...
- [04]Go设计模式:抽象工厂模式(Abstract Factory Pattern)
目录 抽象工厂模式 一.简介 二.代码 三. 参考资料 抽象工厂模式 一.简介 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂 ...
- Create C/C++ DLLs in Visual Studio
https://docs.microsoft.com/zh-tw/cpp/build/dlls-in-visual-cpp?view=vs-2019 Walkthrough: Create and u ...
- OpenGL入门学习--超级好的资料
近几天,由于工作项目需要利用openGL显示STL格式的三维模型,分享下面的BOOK,这个老外写得真是TMD太好了,资料免费! http://www.glprogramming.com/red/ind ...
- QML 下拉列表框的使用
世界上一成不变的东西,只有"任何事物都是在不断变化的"这条真理. -- 斯里兰卡 ComboBox { id:combox x: structureTab_label2.x+str ...