`

自定义数据库连接池

 
阅读更多

一、直接获取连接与使用连接池的比较

应用程序直接获取连接示意图

 

 

缺点:

    用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。

 

 数据库连接池示意图

 

优势:

连接池中会有指定个数的连接对象,每次连接的时候,只要将请求发给连接池,连接池就会提供连接对象,使用完之后,再将连接对象放回连接池,

这样不用每次都去连接,大大提高了性能。

 

二、编写一个基本的连接池实现连接复用

 

原理:

通过一个LinkedList来模拟连接池,每次取Connection的时候,就remove,当释放的时候就再add进去,这里通过打印remove的前后,来说明每次使用之后会放回“连接池”

dbinfo.properties

复制代码
className=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb
user=root
password=root
复制代码

 

自定义连接池

复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.ResourceBundle;

import javax.management.RuntimeErrorException;

public class SimpleConnectionPool {
        private static String className;
        private static String url;
        private static String user;
        private static String password;

//创建一个集合用于模拟连接池
        private static LinkedList<Connection> pool = new LinkedList<Connection>();
//        private static LinkedList<Connection> pool =  (LinkedList<Connection>) Collections.synchronizedCollection(new LinkedList<Connection>());
        
//加载配置文件并注册驱动
        static{
            try {
                ResourceBundle bundle = ResourceBundle.getBundle("cn.itmonkey.util.dbinfo");
                className = bundle.getString("className");
                url = bundle.getString("url");
                user = bundle.getString("user");
                password = bundle.getString("password");
                Class.forName(className);
                
                //创建10个连接对象
                for(int i=0;i<10;i++){
                    Connection conn = DriverManager.getConnection(url,user,password);
                    pool.add(conn);
                }
                System.out.println("初始化连接");
                
                //打印所创建的连接对象
                int i=0;
                for(Connection conn: pool){
                    System.out.println(conn+"..."+i++);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
        //获取连接对象,由于可能同时会有多个对象来取,所以使用同步
        public synchronized static Connection getConnection(){
            
            System.out.println("取之前的连接如下");
            int i=0;
            for(Connection conn: pool){
                System.out.println(conn+"..."+i++);
            }
            
            //从队列中移出一个连接对象,返回给调用者
            if(pool.size()>0){    //防止为0的时候,导致异常,所以要进行判断
                Connection conn = pool.remove();
                return conn;
            }
            else{
                throw new RuntimeException("对不起,服务器忙!");
            }
        }
        
        //将连接对象释放到队列中
        public static void release(Connection conn){
            pool.addLast(conn);
        }
}
复制代码

二、编写标准的数据库连接池

标准的数据源:

需要实现javax.sal.DataSource接口的类,标准的数据源里默认了维护了一个连接池

 

问题:

在直接获取标准数据源的Connection后,在调用它的close方法时,不是还回连接池中,而是直接关闭,我们希望在调用close方法的时候,是将Connection还回连接池中,而不是关闭

 

解决方案一:装饰设计模式

通过装饰,对需要的已有功能进行增强

 

装饰设计模式编写步骤

1、编写一个类,实现与被增强对象相同的接口

2、定义一个引用变量,记住被增强对象

3、定义构造方法,参数为接口后者父类,比便实现多态,传入被增强对象, 并给第2部的变量赋值

4、对于要增强的方法,自己改写

5、对于不需要增强的方法,调用原有对象的对应方法。

复制代码
public class MyConnection implements Connection{
    private Connection conn;
    private LinkedList<Connection> pool;//模拟连接池
    
    public MyConnection(Connection conn,LinkedList<Connection> pool){
        this.conn = conn;
        this.pool = pool;
    }
    
    //释放连接对象到连接池
    @Override
    public void close() throws SQLException {
        pool.add(conn);
    }

    @Override
    public void clearWarnings() throws SQLException {
        conn.clearWarnings();
    }

    
    @Override
    public void commit() throws SQLException {
        conn.commit();
    }
//不需要增强的方法,调用原有对象的对应方法即可,
    @Override
    public Array createArrayOf(String typeName, Object[] elements)
            throws SQLException {
        
        return conn.createArrayOf(typeName, elements);
    }
  //后面有很多方法,因为不需要增强的方法,调用原有对象的对应方法即可,这里略去
  
  …………
}
复制代码

解决方案二:适配器模式

 通过一个类去继承一个接口或父类,对需要增强的方法进行改写即可,适配器模式在监听机制中出现比较多,由于父类中有很多的抽象方法,如果一一实现,比较麻烦,所以通常在API中会提供一个已经默认实现的类,我们只需去继承这个类,然后对希望增强的方法进行复写即可

 

适配器模式编写步骤

1.编写一个类,继承默认适配器

2.定义一个引用变量,记住被增强对象

3.定义构造方法,传入被增强的对象,并给第2部的变量赋值

4.对于要增强的方法,自己改写

 

为Connection准备的适配器

 适配器本身也是一个包装类,实现或者继承一个类,但是什么都不做,所有的方法都调用原有对象的对应方法

 

复制代码
public class ConnectionWrapper implements Connection{
    protected Connection conn;
    public ConnectionWrapper(Connection conn){
        this.conn = conn;
    }
    
    @Override
    public void clearWarnings() throws SQLException {
        conn.clearWarnings();
    }

    @Override
    public void close() throws SQLException {
        conn.close();
    }

    @Override
    public void commit() throws SQLException {
        conn.commit();
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements)
            throws SQLException {
        return null;
    }
  …………
}
复制代码

 

继承适配器,对需要增强的方法复写即可

复制代码
public class MyConnection2  extends ConnectionWrapper {
    private LinkedList<Connection> pool;
    public MyConnection2(Connection conn, LinkedList<Connection> pool) {
        super(conn);
        this.pool = pool;
    }
//将连接对象还回池中
    public void close() throws SQLException {
        pool.add(conn);
    }
}
复制代码

解决方案三:动态代理

使用动态代理,在获取Connection的方法中使用代理,所以在获取Connection的时候,就是一个代理的Connection,该代理的Connection对象,会对调用方法进行判断,如果是close方法,就对返回值进行改写(这里是将Connection对象放回连接池中),否则就按照原来方法去执行

关于更多动态代理的知识,笔者在Java基础里面有详细地提到,其机制,实现等,想深刻了解的亲们可以查看:http://www.cnblogs.com/xushuai123/archive/2012/12/02/2978070.html

复制代码
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.util.LinkedList;
import java.util.ResourceBundle;

import javax.sql.DataSource;

//标准的数据源
public class ProxyDataSource implements DataSource{
    
    private static String className;// 驱动类名
    private static String url;// 连接串
    private static String user;
    private static String password;
    private static LinkedList<Connection> pool = new LinkedList<Connection>();
    static {
        try {
            ResourceBundle rb = ResourceBundle.getBundle("cn.itxushuai.util.dbinfo");
            className = rb.getString("className");
            url = rb.getString("url");
            user = rb.getString("user");
            password = rb.getString("password");
            Class.forName(className);

            // 初始化10个连接
            for (int i = 0; i < 10; i++) {
                Connection conn = DriverManager.getConnection(url, user,
                        password);
                pool.add(conn);
            }

        } catch (Exception e) {
            throw new ExceptionInInitializerError("驱动加载失败");
        }
    }
    @Override
    public synchronized Connection getConnection() throws SQLException {
        if(pool.size()>0){
            System.out.println("池中的连接如下");
        int i=1;
        for(Connection conn : pool){
            System.out.println(conn+"----"+i++);
        }
        
        final Connection conn = pool.remove();
        Connection proxyConn = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), 
                new InvocationHandler(){

                    @Override
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        if("close".equals(method.getName())){    
                            return pool.add(conn);
                        }else{
                            return method.invoke(conn, args);
                        }
                    }}
                );    
            return proxyConn;
        }else{
            throw new RuntimeException("服务器忙");
        }
        }

    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

}
复制代码

 

分享到:
评论

相关推荐

    手动设计自定义数据库连接池

    ### 手动设计自定义数据库连接池 #### 一、数据库连接池概念及作用 数据库连接池是一种用于管理与数据库建立连接的技术方案。它通过维护一个预设的连接集合,来实现对数据库连接的高效利用。连接池的核心价值在于...

    自定义的数据库连接池

    自定义数据库连接池是为了更好地适应特定应用的需求,提高数据存取的效率,减少系统开销,避免频繁创建和关闭数据库连接带来的性能损失。 连接池的基本原理是预先初始化一定数量的数据库连接,这些连接在空闲时被...

    C#高效数据库连接池源码

    自定义数据库连接池还可以考虑支持多种数据库类型,例如SQL Server、MySQL、Oracle等,通过泛型或接口实现对不同数据库的适配。 理解并掌握C#高效数据库连接池的实现,对于开发高性能的数据库应用至关重要。通过...

    MySql数据库连接池C#代码(有注释含测试代码)

    本示例包含的代码文件`ConnectionPool.cs`、`TestConnectionPool.cs`和`PooledConnection.cs`提供了自定义数据库连接池的实现。 首先,我们来看`ConnectionPool.cs`。这个文件通常会包含一个类,比如`...

    自己写的数据库连接池

    然而,自定义数据库连接池可以让你更好地理解和控制数据库连接的生命周期,同时也可以根据特定的应用场景进行定制化设计。 在这个项目中,你运用了代理模式。代理模式是一种设计模式,它提供一个代理对象来控制对原...

    自定义连接池

    自定义连接池是数据库操作中的一个重要概念,它允许开发者创建并管理数据库连接,以提高应用程序的性能和资源利用率。在Java编程中,我们通常使用诸如C3P0、DBCP或HikariCP等第三方连接池库。然而,理解自定义连接池...

    自己写的数据库连接池(java)

    在自定义数据库连接池时,需要注意的关键点包括连接池的大小管理、连接超时、事务管理、健康检查策略以及与实际数据库的兼容性等。对于初学者来说,理解这些核心概念并尝试实现自己的连接池是提升技能的好方法,但...

    扩展Hibernate使用自定义数据库连接池的方法

    然而,当涉及到多模块整合或已有自定义数据库连接池时,可能需要扩展Hibernate以使用自定义的数据库连接池。本文将详细讲解如何实现这一过程。 首先,Hibernate通过`ConnectionProvider`接口来管理数据库连接。这个...

    数据库连接池的一个简单实现

    本文将介绍一个简单的自定义数据库连接池实现,主要涉及以下几个核心组件: 1. **连接池配置**:定义连接池的大小、超时时间、数据库连接参数(如URL、用户名、密码)等。这些参数可以通过配置文件或者硬编码在代码...

    数据库连接池案例包括c3p0,druid,和自己手写的连接池

    然后,我们来谈谈自定义数据库连接池的实现。尽管已有成熟的连接池解决方案,但有时为了满足特定的需求或进行性能优化,开发者会选择自定义连接池。自定义连接池的基本思想是维护一个连接池,当应用需要连接时从池中...

    JAVA数据库连接池

    自定义数据库连接池可以更好地适应特定项目需求,但通常需要考虑以下几个核心功能: 1. **初始化配置**:设置最小连接数、最大连接数、超时时间等参数,确保系统在不同负载下都能正常工作。 2. **连接创建与回收**...

    C# 数据库自定义链接应用池

    文件名为"PoolDemoForm",可能是一个包含示例代码的窗体应用程序,用于演示如何创建和使用自定义数据库连接池。在实际的C#编程中,我们通常会使用System.Data.SqlClient命名空间下的SqlConnection类,它支持内置的...

    poolman 数据库连接池+源码例子,自己写的代码

    这将有助于你在实际项目中优化数据库性能,合理利用系统资源,同时也可以为自定义数据库连接池的开发提供参考。 总的来说,Poolman数据库连接池是一个学习数据库连接池管理机制的好素材。通过深入研究其源码,你...

    Hikari数据库连接池

    **自定义数据库连接池**的优点在于,可以更好地控制连接的生命周期,如设置连接的最大最小值、超时策略等。Hikari数据库连接池的设计目标是提供比其他连接池实现更高的性能和更低的资源消耗。它可能包含了以下特性:...

    一个效果非常不错的JAVA数据库连接池.doc

    【自定义数据库连接池】 文档中提到的代码实现是一个自定义的数据库连接池。这个连接池类`ConnectionPool`包含了数据库连接的相关配置,如JDBC驱动、数据库URL、用户名、密码,以及连接池的初始化大小、自动增长...

    一个Demo小例子让你了解数据库连接池实现的4种方式

    1. **JdbcPool**:这是一个简单的自定义数据库连接池实现,通常用于教学或小型项目。它通过维护一个连接池,每当需要数据库连接时,就从池中获取,用完后归还。JdbcPool的基本思路是初始化时创建一定数量的数据库...

    数据库连接池java代码实现

    数据库连接池在Java中的实现是提高应用程序性能的关键技术之一,它通过复用已存在的数据库连接,避免了频繁创建和销毁连接导致的系统资源浪费。本文将深入探讨如何使用Java代码来实现一个简单的数据库连接池,并解释...

    自定义JDBC连接池及常用连接池介绍

    4. Druid:阿里巴巴开源的数据库连接池,除了基本的连接池功能,还提供了监控、SQL审计、日志等功能,是许多大型互联网公司的首选。 5. Tomcat JDBC Connection Pool:Tomcat服务器自带的连接池,适用于那些不希望...

    delphi实现数据库连接池

    在Delphi中,我们可以使用TDBXConnectionPool组件或者自定义类来实现数据库连接池。TDBXConnectionPool是IBX(InterBase XE)库的一部分,它可以管理和复用数据库连接。如果选择自定义,我们需要设计一个类来维护...

Global site tag (gtag.js) - Google Analytics