JDBC解析

来源:互联网 发布:控制面板里没有java 编辑:程序博客网 时间:2024/05/29 02:37

mysql版本:Ver 14.14 Distrib 5.7.16, for Win64 (x86_64)

驱动版本号:5.1.40


所有的JDBC应用程序都具有下面的基本流程:
1、加载数据库驱动。(JDBC4.0版本后依赖service provider默认执行,也就是不再需要使用Class.forName加载驱动)

2、建立到数据库的连接。

3、执行SQL语句。

4、处理结果。

5、从数据库断开连接并释放资源。

接下来,我们依据上述流程作出解释。

1、加载数据库驱动。

最常见的使用方式:
try {    Class.forName("com.mysql.jdbc.Driver");} catch (ClassNotFoundException e) {    e.printStackTrace();}
Class.forName用来加载某个类,为类中的静态变量分配存储空间,并执行其静态代码块。
此处对应于:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {    static {        try {            java.sql.DriverManager.registerDriver(new Driver());        } catch (SQLException E) {            throw new RuntimeException("Can't register driver!");        }    }    public Driver() throws SQLException {    }}

知道了上述作用,我们当然也可以这样使用Driver driver = new com.mysql.jdbc.Driver();来加载Mysql驱动程序。

当然没有必要,我们没有使用driver。

那么,我们不写可以吗?

测试:

import java.sql.*;/** * Created by N3verL4nd on 2017/4/25. */public class jdbc {    public static void main(String[] args) {        String driverClass = "com.mysql.jdbc.Driver";        String jdbcUrl = "jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&useSSL=true";        String user = "root";        String password = "lgh123";        String sql = "SELECT * FROM persons";        Connection conn = null;        /*try {            Class.forName(driverClass);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }*/        try {            conn = DriverManager.getConnection(jdbcUrl, user, password);            System.out.println(conn);        } catch (SQLException e) {            e.printStackTrace();        } finally {            if (conn != null) {                try {                    conn.close();                } catch (SQLException e) {                    e.printStackTrace();                }            }        }    }}

貌似不可以?由上报错,是因为没有找到所依赖的mysql驱动程序。我们可以这样设置:

D:\N3verL4nd\Desktop>set classpath=D:\Java\test\mysql-connector-java-5.1.40-bin.jar;
D:\N3verL4nd\Desktop>java jdbc
com.mysql.jdbc.JDBC4Connection@6433a2
再来做一个有意思的测试:

将mysql-connector-java-5.1.40-bin.jar的com目录(里面包含驱动的核心文件)解压到当前目录下。


我们把加载驱动的代码注释取消,再执行


jar包只是起到了压缩的作用,内部使用过程中也一定会涉及解压缩操作来调用驱动内部代码。

命令行下使用更能体会程序的运行原理。

接下来分析下DriverManager,它是JDBC里的一个管理驱动的工具类。

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
保存注册过的驱动列表
DriverManager的静态代码块:

static {    loadInitialDrivers();    println("JDBC DriverManager initialized");}
通过系统属性jdbc.drivers加载JDBC驱动程序。

private static void loadInitialDrivers() {    String drivers;    try {        drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {            public String run() {                return System.getProperty("jdbc.drivers");            }        });    } catch (Exception ex) {        drivers = null;    }    AccessController.doPrivileged(new PrivilegedAction<Void>() {//找到所有的拥有权限的java.sql.Driver的实现        public Void run() {            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);//从系统服务中加载驱动            Iterator<Driver> driversIterator = loadedDrivers.iterator();            try{                while(driversIterator.hasNext()) {//遍历驱动                    driversIterator.next();                }            } catch(Throwable t) {            }            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);        }    }}
接着判断驱动对象集是否为null,如果为null则返回,否则进入for循环,这个循环会依次遍历多个数据库驱动,因为jdbc:drivers会有多个数据库驱动,驱动名是以:分割,接下来就是通过Class.forName依次装载驱动类,在其中使用了ClassLoader.getSystemClassLoader()系统类装载器。

Driver类的静态代码块关键的一句代码:

java.sql.DriverManager.registerDriver(new Driver());
public static synchronized void registerDriver(java.sql.Driver driver)    throws SQLException {    registerDriver(driver, null);}
public static synchronized void registerDriver(java.sql.Driver driver,        DriverAction da)    throws SQLException {    /* Register the driver if it has not already been added to our list */    if(driver != null) {        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));    } else {        // This is for compatibility with the original DriverManager        throw new NullPointerException();    }    println("registerDriver: " + driver);}
DriverInfo驱动信息类,是一个内部类,实际上就是对Driver的封装。然后再添加到registeredDrivers集合中。

2、建立到数据库的连接。

然后我们才能用:
DriverManager.getConnection()方法来获取连接;我们知道这个是获取一个Connection,那么我们首先来看看DriverManager的getConnection到底做了什么?
public static Connection getConnection(String url)//所有参数全部通过URL传递过去

public static Connection getConnection(String url,String user, String password)//用户名和密码单独传递
public static Connection getConnection(String url,java.util.Properties info)//传递多个参数的K-V结构
以上三个建立数据库连接的操作都会调用下边这一个:

private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) 
private static Connection getConnection(    String url, java.util.Properties info, Class<?> caller) throws SQLException {    ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;    synchronized(DriverManager.class) {//如果没传递CLassLoader用当前线程的         if (callerCL == null) {            callerCL = Thread.currentThread().getContextClassLoader();        }    }    if(url == null) {        throw new SQLException("The url cannot be null", "08001");//没有传递URL,则抛出异常    }//注意这个日志,默认是打印不出来的,程序内部会去判定logWriter是否为空,若为空则不会输出,默认为空      println("DriverManager.getConnection(\"" + url + "\")");    SQLException reason = null;    for(DriverInfo aDriver : registeredDrivers) {//循环扫描所有的Drivers        if(isDriverAllowed(aDriver.driver, callerCL)) {            try {                println("    trying " + aDriver.driver.getClass().getName());                Connection con = aDriver.driver.connect(url, info);//调用对应Driver的connect方法,得到Connection对象                if (con != null) {                    // Success!                    println("getConnection returning " + aDriver.driver.getClass().getName());                    return (con);//如果得到了,则返回Connection                }            } catch (SQLException ex) {                if (reason == null) {                    reason = ex;                }            }        } else {            println("    skipping: " + aDriver.getClass().getName());        }    }    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");//没有获取到连接会抛出一个SQLException}
synchronized(DriverManager.class) {    if (callerCL == null) {        callerCL = Thread.currentThread().getContextClassLoader();    }}
同步DriverManager的Class对象,synchronized同步的对象为DriverManager.class,是为同步正确的类加载器来加载类,在同步块
中判断传入的类装载器对象是否存在,如果为null, 通过当前线程来获取上下文类装载器,保证JDBC驱动程序类以外的rt.jar中的类

Connection con = aDriver.driver.connect(url, info);

找到符合的驱动器就可以建立连接了。

3、执行SQL语句。

stmt = conn.createStatement();rs = stmt.executeQuery("SELECT * FROM persons");

4、处理结果。

while (rs.next()) {    System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));}

5、从数据库断开连接并释放资源。

finally {    if (rs != null) {        try {            rs.close();        } catch (SQLException e) {            e.printStackTrace();        }    }    if (stmt != null) {        try {            stmt.close();        } catch (SQLException e) {            e.printStackTrace();        }    }    if (conn != null) {        try {            conn.close();        } catch (SQLException e) {            e.printStackTrace();        }    }}

package com.xiya.test;import java.io.PrintWriter;import java.sql.*;public class Test {    public static void main(String[] args) {        try {            Class.forName("com.mysql.jdbc.Driver");        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        //遍历加载的驱动程序        /*System.out.println("-----------------------------------------------");        Enumeration<Driver> enumeration = DriverManager.getDrivers();        while (enumeration.hasMoreElements()) {            System.out.println(enumeration.nextElement());        }        System.out.println("-----------------------------------------------");*/        Connection conn = null;        Statement stmt = null;        ResultSet rs = null;        try {            //设置将数据输出到控制台            DriverManager.setLogWriter(new PrintWriter(System.out));            conn = DriverManager.getConnection("jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&useSSL=true", "root", "lgh123");            stmt = conn.createStatement();            rs = stmt.executeQuery("SELECT * FROM persons");            while (rs.next()) {                System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));            }        } catch (SQLException e) {            e.printStackTrace();        } finally {            if (rs != null) {                try {                    rs.close();                } catch (SQLException e) {                    e.printStackTrace();                }            }            if (stmt != null) {                try {                    stmt.close();                } catch (SQLException e) {                    e.printStackTrace();                }            }            if (conn != null) {                try {                    conn.close();                } catch (SQLException e) {                    e.printStackTrace();                }            }        }    }}


参考:

http://blog.csdn.net/luanlouis/article/details/29850811

http://blog.csdn.net/brilliancezhou/article/details/5425655

http://blog.csdn.net/xieyuooo/article/details/8502585

0 0
原创粉丝点击