您的位置 首页 java

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

本章节主要分析了 jdbc 协议第四章内容,并且顺带分析了 DriverManager 和DataSource类的主要接口。以及使用JDBC程序的两层模型和三层模型。

作者:鬣狗

日期:2021年6月28日

0.简介

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图1 jdbc 简介

简介主要说明了JDBC-API为 java 程序访问一个或多个数据源(data source)提供了一种方式。在大多数情况下数据源都是 关系型数据库 ,使用SQL访问其中的数据。 但是,支持JDBC技术的驱动程序也可以在别的数据源上进行实现,包括遗留文件系统和面向对象系统。JBDC-API 的主要动机就是提供一个标准的API来访问各式各样的数据源。

简介说明了一个重要问题,即JDBC是提供提供一个标准的API和方式来访问底层各式各样的数据源。例如在一些环境下,底层的数据源可能不是MySQL这样的数据库,有可能是DriosDB(一种olap),但是JDBC-API能够屏蔽底层数据源的差异,使得程序使用者可以像访问MySQL那样访问DriosDB。

1.JDBC-API及其使用步骤

  1. Register Driver 注册驱动
  2. Establishing a Connection 建立连接
  3. Executing SQL Statements 执行SQL语句
  4. Manipulating Results 操纵结果

Register Driver

注册驱动有三种方式,这里说的驱动一般都是 一般由数据库厂商实现,实现了java.sql.Driver接口的类。

方式1:第三方驱动( 一般由数据库厂商实现,实现了 Java .sql.Driver接口 )在其static代码块中通过DrvierManger.registerDriver接口注册驱动。

图2 MySQL Driver

方式2:java程序以命令行的方式启动,然后在 jdbc.drivers属性中指明要注册的驱动,多个驱动之间以”:”分割。

 java -Djdbc.drivers=com.xxx.driver1:com.xxx.driver2          

方式3:以spi的方式注册,在jar包的META-inf/services目录下创建以java.sql.Driver为文件名的文件,然后文件内容是具体的驱动实现。

图3 MySQL SPI

DriverManager.loadInitialDrivers()

图4 loadInitialDrivers

 private static void loadInitialDrivers() {
        String drivers;
        try {
          // step 1. 通过 jdbc.drivers 获取驱动
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch ( Exception  ex) {
            drivers = null;
        }
        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()

        AccessController.doPrivileged(new PrivilegedAction< Void >() {
            public Void run() {
  // step 2. 通过 spi方式加载驱动
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });

        println("DriverManager.initialize: jdbc.drivers = " + drivers);

        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }  

可以看到DriverManager类被载入到JVM的时候会调用其静态代码块的loadInitialDrivers方法,该方法首先通过jdbc.drivers属性获取驱动,然后通过spi的方式再获取驱动。

这里要注意的一个点就是spi获取服务的方式,这个在写第三方框架的时候可以用到

 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);  

如果要加载一个其它的服务:

1.把meta-inf/services下的文件名改成要加载的服务全限定名称。

2. ServiceLoader.load(YourOwnService.class); 改变方法入参为自己要加载的服务全限定名称

Establishing a Connection

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图5 Establishing a Connection

The JDBC API defines the Connection interface to represent a connection to an underlying data source.

jdbc api定义了 Connection 接口代表底层数据源的连接

有两种方式能够获得Connection对象,第一种是通过 DriverManager.getConnection(String url) 方式获得。另一种是通过 DataSource.getConnection() 接口获得。

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图6 DriverManager

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图7 Drvier

DriverManager类主要提供注册/移除驱动、获取连接的方法。DriverManager 使用注册的驱动类来获取连接 。Driver类主要方法有两个, acceptsURL,getConnection。

acceptsURL表示支持的jdbc连接。 getConnection返回底层数据源的Connection对象,注意:jdbc协议规定,如果当前Driver不支持某个url,getConnection要返回null。

下面是代码的具体实现。

 private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
         synchronized (DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection("" + url + "")");

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }  

这个代码的作用就是从注册的驱动中找到支持当前url的,并且返回一个可用的连接对象。并且, 如果当前Driver不支持某个url,getConnection要返回null,这一点在代码中是可以得到验证的。

  if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }  

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图8 DataSource

以下出自DataSource的java doc:

DataSource对象提供底层数据源的连接。DataSource的实现通常有三种:

1.Basic implementation — produces a standard Connection object //基本实现

2.Connection pooling implementation — produces a Connection object that will automatically participate in connection pooling. This implementation works with a middle-tier connection pooling manager. //连接池实现

3.Distributed transaction implementation — produces a Connection object that may be used for distributed transactions and almost always participates in connection pooling. This implementation works with a middle-tier transaction manager and almost always with a connection pooling manager.//分布式实现

除此之外jdbc协议中也分为定义了相应的接口:

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图9 DataSource扩展

ConnectionPoolDataSource 表示以连接池方式实现的DataSource, XA DataSource 表示此数据源能够产生一个参与分布式事务的连接。

Executing SQL Statements & Manipulating Results

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图10 Executing SQL Statements & Manipulating Results

一旦建立起和数据源建立了连接后,就可以进行查询。 Connection接口 可以指定事物属性(如事务的隔离级别),并且也可以创建Statement、PreparedStatement、CallableStatement对象,这些对象用来执行SQL查询。然后使用ResultSet接口来操纵结果集。

注意:本章不追究Statement、ResultSet、Connection接口的细节,这个在jdbc协议后章节会有说明

总结:

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图11 总结

如图可以通过DriverManager、DataSource获取到Connection对象,此时客户端与数据源建立起一个物理连接;拿到Connection对象后,然后创建Statement、PreparedStatement、CallableStatement对象用来执行SQL;SQL执行完成可以得到相应的结果集(ResultSet),然后进行相应结果的检索。

2.使用JDBC的程序模型

Two-tier Model(2层模型)

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图12 Two-tier Model

The client layer includes the application(s) and one or more JDBC drivers, with the application handling these areas of responsibility:

presentation logic、business logic、transaction management、resource management

In this model, the application interacts directly with the JDBC driver(s), including establishing and managing the physical connection(s) and dealing with the details of specific underlying data source implementations. The application may use its knowledge of a specific implementation to take advantage of nonstandard features or do performance tuning.

在2层模型中包含应用程序和一个或多个jdbc驱动,并且应用程序需要处理:展示逻辑、业务逻辑、事务管理、资源管理;这个模型下,应用程序直接和jdbc驱动进行交互,需要关心底层数据源的实现细节。

Three-tier Model(3层模型)

鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

图13 Three-tier Model

三层模型是目前web中经常遇到的模型,在前面两层模型的基础上增加了展示层(例如:浏览器)。

两层模型又细分为Application Server、Applications、jdbc driver。其中Application就是我们的应用程序;Application Server是服务器(例如:tomcat),服务器会为应用程序提供基础设施支持,例如:事务管理、 连接池

区别:与两层模型相比,三层模型增加了web client和Application Server,程序中的连接池和事务管理交给Applicaition Server去管理,而不需要开发人员在业务代码中关心。目前的tomcat+spring+mybatis就是这种三层模型。

3.JDBC在JavaEE平台的使用

Java EE components, such as JavaServerTM Pages, Servlets, and Enterprise Java BeansTM (EJBTM) components, often require access to relational data and use the JDBC API for this access. When Java EE components use the JDBC API, the container manages their transactions and data sources. This means that Java EE component developers do not directly use the JDBC API’s transaction and datasource management facilities. See the Java EE Platform Specification for further details.

上面是说,在JavaEE环境下, 开发人员不会直接使用jdb的api的事务和数据源相关接口,而是交给servlet容器去管理事务和数据源。

下篇文章将会具体分析MySQL Driver的具体实现,MySQL如何建立物理连接,执行SQL等等。敬请关注!!!

4.其他

github主页:

git仓库地址:

gitbook地址:

欢迎各位加入鬣狗技术社区,希望能够为您提供有思考、有深度的文章。欢迎加入我们,如果你也有想法在鬣狗技术社区发表文章,头条私聊即可。

求 关注➕转发➕点赞,谢谢各位!您的支持就是我们更新的动力

文章来源:智云一二三科技

文章标题:鬣狗技术- Java 数据库专题|2. JDBC协议 Chapter 4:概览&JDCB-API

文章地址:https://www.zhihuclub.com/176865.shtml

关于作者: 智云科技

热门文章

网站地图