我们为什么需要JDBC和数据库连接池?

2,186 阅读6分钟

1. 为什么需要JDBC?

在JDBC(Java DataBase Connectivity)没有出现的时候,我们是如何连接数据库的,遇到了什么困境?

  1. 首先要理解数据库是运行在电脑上的一个独立的应用程序;
  2. 所以应用程序是需要通过网络通信协议与数据库进行命令交换的,eg:如应用程序发出了一个增删改查的命令,那么这个命令就需要通过一次网络连接将该命令传送给数据库,让数据库进行相应的操作,并返回相应的结果。
  3. 为了简便开发,将程序员从繁琐的与数据库命令交互底层的操作解放出来,所以数据库提供商一般都会将其封装成API对外暴露功能:eg:
    //根据ip地址,用户名与密码与数据库建立连接,这里没有端口是因为每个数据库提供商都会有自己的一套程序,所以每套程序中自然已经写好对应的
    端口
    SqlConnection connection = new SqlConnection("localhost", "root", "123456");
    //获取到对应的表空间
    connection.selectDB("user");
    //将应用程序的命令通过API传递给数据库
    SqlQuery query = connection.query("SELECT * FROM userTable");

可以看到,一切繁琐的功能,如建立与数据库的连接只需要new一个SqlConnection对象即可,传递命令只需要调用query方法即可;

  1. 但这样的命令交换自然会遇到一个困境,即数据库有很多种,Oracle,MySQL等,不同的数据库通常会有不同的通信协议,那么当一个应用程序涉及到几种库的时候,就不得不按照不同产商的API写出几套连接程序来;而API的不同又导致了另一个问题,即每个API的实现方式不同,因为涉及到网络连接,API可能会依赖与操作系统的相关功能,那么需要换操作系统时,可能需要更换支持对应操作系统的API;这个是绝对无法容忍的!
  2. 问题的关键在于: 程序员无法通过一致的API去操作数据库,而JDBC的出现解决了这个问题,它提供了一致的编码规范,无论是哪一种数据库,在应用程序层面,都是通过一致的API去访问:

JDBC驱动程序即为JDBC Driver,而JDBC驱动程序则是由相应的数据库厂商实现,每个数据库都会有自己的Driver,如下图所示:

下面的连接方法我想大家都已经很熟悉了:

    public void connect() {
        //用于确定DriverManager会加载哪一个驱动程序,DriverManager是用于管理驱动程序的类
        String jdbcUrl = "jdbc:h2:tcp://localhost";
        String username = "root";
        String password = "123456";
        try {
            //加载h2数据库驱动程序,这里会调用org.h2.Driver的static方法,将该驱动程序实例注册到DriverManager
            Class.forName("org.h2.Driver");
            //通过驱动程序连接到数据库
            Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
            //这个是sql语句代表对象
            Statement statement = connection.createStatement();
            ResultSet result = statement.executeQuery("SELECT * FROM userTable");
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
    }

可能会有疑问,为什么没有把连接关闭呢?是因为从JDK7之后,Connection等接口都是AutoCloseable的子接口,所以无需手动关闭。

2. 为什么需要数据库连接池?

思考这个问题不妨从JDBC有什么问题出发,其中最明显也最致命的一点就是每次连接都需要建立一次Connection,而如果在一个大型系统中,并且对数据库的访问比较频繁的情况下,多次建立TCP连接的代价还是比较高的,那能不能不那么频繁的建立TCP连接呢?最简洁明了的解决方法就是复用,显然数据库连接池是通过这个方法解决了JDBC的问题;

2.1 数据库连接池:

2.1.1 基本原理:

维护一个资源池,池中存储着一定数量的数据库连接,并通过方法对外暴露对于连接的获取(getConnection())与释放(releaseConnection(),释放连接并不是关闭连接,只是将这个连接重新放入资源池中)操作。

2.1.2 连接池带来的好处(与线程池的好处有很多相像的地方):

  1. 连接资源的重用,不再需要每次对数据库进行指令交换时,便会进行一次建立连接与关闭连接的操作;
  2. 连接的创建与连接的使用解耦合,使用者只需要调用getConnection()即可,而不需要再关心这个连接是怎么建立的(如之后改变了连接建立的参数,比如说数据库的ip地址变化了,就不再需要改变业务代码了);
  3. 更快的响应速度,因为连接在系统初始化的时候便放入连接池中,应用执行SQL语句时不再需要等待建立连接;
  4. 更好的对连接进行管理,对连接状态进行监控,及时收回被占用时间过长的连接,避免数据库连接泄露。

2.1.3 使一个Connection可以对应多个Statement

这一个点需要单独讲一下,传统的JDBC连接方法中,一般拿到一个连接后,便设置其Statement,即在一个连接上只运行一或几个Statement,而这远远没有达到Connection的瓶顶,而ConnectionPool可以解决这个问题,在通过getConnection()获取连接时,可以返回没有到达运行Statement数量瓶顶的连接,从而更好的利用了资源,通过ConnectionPool对资源的有效管理,应用可以获得的Statement总数到达 : (并发物理连接数)×(每个连接可提供的Statement数量) 而这是传统的非资源池连接没有办法做到的。

2.1.4 Java EE连接池的实现样例:使用DataSource取得连接

在Java EE环境中,将取得连接等与数据库来源相关的行为规范在javax.sql.DataSource接口中,之前提到通过DriverManager取得Connection,事实上在Web应用程序中很少会这样做,一般会通过JNDI(Java Naming Director Interface)从服务器上取得设置好的DataSource,再从DataSource的getConnection()取得Connection;在Tomcat9中默认设置的为DBCP(Database Connection Pool),是内置在Tomcat中的连接池机制;

2.2 Durid连接池的使用:

Durid连接池是目前最为流行的连接池框架,就以Druid的使用来印证一下理论(Durid入门最好的学习资料当然是在github上),这个留在下一篇再讲~

3. 总结:

  1. JDBC的出现是为了对应用程序提供一致的接口,将业务代码与具体的数据库之间解耦合;
  2. 数据库连接池的出现主要是为了复用数据库连接资源,避免频繁建立连接增加系统负担。