• 自定义一个简单的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方法就不会去销毁连接了,会正确的归还给了连接池了,对测试代码稍做修改即可测试。

    热爱生活,积极进取!
  • 相关阅读:
    window.location.href无法跳转的解决办法
    HTTP 错误 405.0
    C# 浅拷贝与深拷贝
    C# .ToString() 格式化
    深入理解AsyncTask
    【转】Android子线程真的不能更新UI么
    深入理解Activity的启动模式
    Android7.0,剪裁后提示“无法保存经过裁剪的图片”
    Android工程改包名
    javah命令,提示“找不到类文件”
  • 原文地址:https://www.cnblogs.com/samluby/p/8068277.html
Copyright © 2020-2023  润新知