JDBC解析1

173 篇文章 16 订阅
86 篇文章 18 订阅

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
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值