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);
}

JDBC学习笔记——简单的连接池的更多相关文章

  1. 【JAVAWEB学习笔记】10_JDBC连接池&DBUtils

    使用连接池改造JDBC的工具类: 1.1.1          需求: 传统JDBC的操作,对连接的对象销毁不是特别好.每次创建和销毁连接都是需要花费时间.可以使用连接池优化的程序. * 在程序开始的 ...

  2. JDBC学习笔记之建立连接

    1. 引言 在一个JDBC应用程序中,如果想建立和数据源的连接,那么可以使用以下两个类: DriverManager:通过数据源的URL,我们可以建立与指定的数据源的连接.如果使用 JDBC 4.0 ...

  3. JDBC学习笔记二

    JDBC学习笔记二 4.execute()方法执行SQL语句 execute几乎可以执行任何SQL语句,当execute执行过SQL语句之后会返回一个布尔类型的值,代表是否返回了ResultSet对象 ...

  4. JDBC学习笔记一

    JDBC学习笔记一 JDBC全称 Java Database Connectivity,即数据库连接,它是一种可以执行SQL语句的Java API. ODBC全称 Open Database Conn ...

  5. CNN学习笔记:全连接层

    CNN学习笔记:全连接层 全连接层 全连接层在整个网络卷积神经网络中起到“分类器”的作用.如果说卷积层.池化层和激活函数等操作是将原始数据映射到隐层特征空间的话,全连接层则起到将学到的特征表示映射到样 ...

  6. 转 JDBC连接数据库(二)——连接池

    https://www.cnblogs.com/xiaotiaosi/p/6398371.html 数据库保持长连接,不过一直都是idle,除非有用户激活连接,这样后果是无法删除用户,但是不影响数据库 ...

  7. IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试

    IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...

  8. 一、DAO设计模式 二、DAO设计模式的优化 三、JDBC中的事务,连接池的使用

    一.DAO设计模式概述###<1>概念 DAO,Data Access Object ,用于访问数据库的对象. 位于业务逻辑和数据持久化层之间,实现对数据持久化层的访问![](1.png) ...

  9. JDBC 学习笔记(十一)—— JDBC 的事务支持

    1. 事务 在关系型数据库中,有一个很重要的概念,叫做事务(Transaction).它具有 ACID 四个特性: A(Atomicity):原子性,一个事务是一个不可分割的工作单位,事务中包括的诸操 ...

随机推荐

  1. 移动开发之css3实现背景几种渐变效果

    移动端背景渐变,非常的年轻,符合90后年轻一代的审美,css3的这个渐变目前主要是应用在手机前端领域. 产品设计中使用渐变色的好处:1:观众不至于眼睛过于疲劳(如果是浅色背景,3个小时下来极容易造成观 ...

  2. 并查集(disjoint set)的实现及应用

    这里有一篇十分精彩.生动的 并查集详解 (转): "朋友的朋友就是朋友"⇒ 传递性,建立连通关系 disjoint set,并查集(一种集合),也叫不相交集,同时也是一种树型的数据 ...

  3. Linux 下查看线程信息

    1. 使用 pstree -p PID ps aux | grep firefox | grep -v grepcharles  26058  0.0  0.0   4908  1152 ?      ...

  4. 解决gvim 8.1中zip插件打开zip文件内容时,而文件路径带有空格的问题。

    解决gvim 8.1中zip插件打开zip文件内容时,而文件路径带有空格的问题. 现象是只能打开一次,第二次打开就显示为空了. 通过 lcd切换工作目录.使得命令行操作中不再有带空格的路径 vim81 ...

  5. Behavioral模式之Memento模式

    1.意图 在不破坏封装性的前提下,捕获一个对象的内部状态.并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态. 2.别名 Token 3.动机 有时候有必要记录一个对象的内部状态.为 ...

  6. [Angular] Using InjectionToken

    Previously we have 'OpaqueToken', but it is DEPRECATED. The new one is called 'InjectionToken'. The ...

  7. java中关于File类的mkdirs()和FIle()构造方法在windows环境内网测试总结

    环境:环境是内网windows,传入路径的时候不带盘符,并且服务器的win10也不是超级管理员账户 关于File类的mkdirs()远程访问创建不了文件和路径的问题. 如果浏览器操作者是windows ...

  8. Springmvc+Shiro实战

    原文链接:http://blog.csdn.net/qq_37936542/article/details/79010449 springmvc+shiro实现系统粗细粒度的权限管理步骤: 1:表格设 ...

  9. 【u032】均衡发展

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 神牛小R在许多方面都有着很强的能力,具体的说,他总共有m种能力,并将这些能力编号为1到m.他的能力是一 ...

  10. Android 带清除功能的输入框控件EditTextWithDel

    记录下一个非常有用的小控件EditTextWithDel.就是在Android系统的输入框右边增加一个小图标.点击小图标能够清除输入框里面的内容,由于Android原生EditText不具备此功能,所 ...