zoukankan      html  css  js  c++  java
  • JDBC学习笔记——简单的连接池

    1、使用LinkedList保存连接                                                              

          即使是最简单的JDBC操作,也需要包含以下几步:建立连接、创建SQL语句、执行语句、处理执行结果、释放资源,其中建立连接步骤是很耗费计算机性能的,如果我们每次进行JDBC操作都创建新的JDBC连接,使用完后再立即释放连接,这样做会耗费大量性能。更合理的做法应该是:创建JDBC连接,使用JDBC连接,使用完后不是立刻释放JDBC连接,而是把连接缓存起来,当下一次操作JDBC时,我们可以直接使用缓存中已经连接上的JDBC连接。

          以下的代码通过一个LinkedList保存创建的JDBC连接,在刚创建的时候,我们会先建立10个连接并保存在list中,当客户代码需要使用Connection的时候,我们直接从list中取出第一个Connection返回,当客户代码使用完Connection,需要调用free()方法,把Connection再放回list中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class DataSource1 {
        LinkedList<Connection> connectionPool = new LinkedList<Connection>();
     
        public DataSource1() {
            try {
                for (int i = 0; i < 10; i++) {
                    this.connectionPool.addLast(this.createConnection());
                }
            } catch (SQLException e) {
                throw new ExceptionInInitializerError(e);
            }
        }
       
        public Connection getConnection() {
            return connectionPool.removeFirst();
        }
       
        public void free(Connection conn) {
            connectionPool.addLast(conn);
        }
       
        private Connection createConnection() throws SQLException {
            return DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc",
                    "root", "");
        }
    }

    2、控制连接数量                                                                            

          在上例的getConnection()方法中,我们没有检测connectionPool中是否包含Connection,直接返回connectionPool中的第一个Connection,这样做是不安全的,在从connectionPool中返回Connection之前,我们首先应该检测connectionPool中是否有Connection,若有,返回connectionPool中的第一个Connection,如果没有,我们可以新创建一个并返回。但是这样有一个问题,如果请求的线程很多,我们这样无限制地创建很多Connection可能导致数据库阻塞,因为数据库可以支持的连接数是有限的,所以我们应该控制新建Connection的上限,若没有达到上限,我们可以创建并返回,如果达到连接上限,那么我们就抛出异常。同时我们应该在getConnection()上加锁,保证多线程安全性,修改后代码如下,我们用initCount表示list初始化大小,maxCount表示list最大大小,currentCount表示现在存活的Connection数量:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class DataSource2 {
        private static int initCount = 10;
        private static int maxCount = 30;
        private int currentCount = 0;
     
        LinkedList<Connection> connectionPool = new LinkedList<Connection>();
     
        public DataSource2() {
            try {
                for (int i = 0; i < initCount; i++) {
                    this.connectionPool.addLast(this.createConnection());
                    this.currentCount++;
                }
            } catch (SQLException e) {
                throw new ExceptionInInitializerError(e);
            }
        }
     
        public Connection getConnection() throws SQLException {
            synchronized (connectionPool) {
                if (connectionPool.size() > 0)
                    return connectionPool.removeFirst();
     
                if (this.currentCount < maxCount) {
                    this.currentCount++;
                    return createConnection();
                }
     
                throw new SQLException("已没有链接");
            }
        }
     
        public void free(Connection conn) {
            connectionPool.addLast(conn);
        }
     
        private Connection createConnection() throws SQLException {
            return DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc",
                    "root", "");
        }
    }

    3、动态代理拦截close方法                                                         

         上面的代码有个问题,那就是要关闭Connection必须调用我们的free()方法,不能直接调用Connection上的close()方法,这对于一些习惯使用完Connection就close()的用户并不是很友好,很容易导致它们忘记把用完的Connection还回connectionPool,为了让用户保持原有习惯,我们希望能够改写Connection的close()方法,让它不是直接关闭连接,而是把连接还回connectionPool中,其他方法保持原来不变,对于这种需求,我们可以使用动态代理实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    public class DataSource3 {
        private static int initCount = 1;
        private static int maxCount = 1;
        int currentCount = 0;
     
        LinkedList<Connection> connectionsPool = new LinkedList<Connection>();
     
        public DataSource3() {
            try {
                for (int i = 0; i < initCount; i++) {
                    this.connectionsPool.addLast(this.createConnection());
                    this.currentCount++;
                }
            } catch (SQLException e) {
                throw new ExceptionInInitializerError(e);
            }
        }
     
     
        public Connection getConnection() throws SQLException {
            synchronized (connectionsPool) {
                if (this.connectionsPool.size() > 0)
                    return this.connectionsPool.removeFirst();
     
                if (this.currentCount < maxCount) {
                    this.currentCount++;
                    return this.createConnection();
                }
     
                throw new SQLException("已没有链接");
            }
        }
     
        public void free(Connection conn) {
            this.connectionsPool.addLast(conn);
        }
     
        private Connection createConnection() throws SQLException {
            Connection realConn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/jdbc", "root", "");
            MyConnectionHandler proxy = new MyConnectionHandler(this);
            return proxy.bind(realConn);
        }
    }
     
     
    class MyConnectionHandler implements InvocationHandler {
        private Connection realConnection;
        private Connection warpedConnection;
        private DataSource3 dataSource;
     
        MyConnectionHandler(DataSource3 dataSource) {
            this.dataSource = dataSource;
        }
     
        Connection bind(Connection realConn) {
            this.realConnection = realConn;
            this.warpedConnection = (Connection) Proxy.newProxyInstance(this
                    .getClass().getClassLoader(), new Class[] { Connection.class },
                    this);
            return warpedConnection;
        }
     
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            if ("close".equals(method.getName())) {
                this.dataSource.connectionsPool.addLast(this.warpedConnection);
            }
            return method.invoke(this.realConnection, args);
        }
    }

      上述代码的关键点是MyConnectionHandler类,在该类的bind()方法中返回了一个wrapedConnection,wrapedConnection的创建方法如下:

    1
    this.warpedConnection = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), <br>        new Class[] { Connection.class },this);

      Proxy.newProxyInstance()方法是Java动态代理的关键方法,该方法会在运行时在内存中动态创建一个类,该方法的第一个参数是指定一个ClassLoader,第二个参数指定动态创建类实现的接口,第三个参数指定在该类上调用的方法应该转给哪个类,在这里指定了this,所以所有方法都会转给MyConnectionHandler,准确的说是MyConnectionHandler的invoke方法:

    1
    2
    3
    4
    5
    6
    7
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        if ("close".equals(method.getName())) {
            this.dataSource.connectionsPool.addLast(this.warpedConnection);
        }
        return method.invoke(this.realConnection, args);
    }

      在invoke()方法中,我们可以看到第二个参数传递了调用的Method,我们根据传递的Method对象判断,用户调用的是否是close方法,如果是close方法,那么我们就把这个Connection重新加入list,如果不是,那么我们就执行真正Connection上的相应方法。

          还需要注意的是,现在调用createConnection()产生的Connection对象已经不是原始的Connection对象,而是调用MyConnectionHandler类上bind()方法动态产生的代理类:

    1
    2
    3
    4
    5
    6
    private Connection createConnection() throws SQLException {
        Connection realConn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/jdbc", "root", "");
        MyConnectionHandler proxy = new MyConnectionHandler(this);
        return proxy.bind(realConn);
    }
  • 相关阅读:
    在 json4s 中自定义CustomSerializer
    【重点】2020年宝山区义务教育阶段学校校区范围与招生计划(小学)
    2019宝山区小升初对口地段表及对口初中片区划分
    2019上海市各区重点幼儿园、小学和中学排名(建议收藏)
    转:一千行MYSQL 笔记
    基于weixin-java-mp 做微信JS签名 invalid signature签名错误 官方说明
    转 : 深入解析Java锁机制
    微服务架构转型升级
    抽奖活动 mark
    抽奖 mark
  • 原文地址:https://www.cnblogs.com/jameslif/p/4081249.html
Copyright © 2011-2022 走看看