jdbc源碼分析


今天看了jdbc的源碼,感覺特別有意思,下面是jdbc的傳統步驟:

 Connection connection=null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/myshool?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong","root","root");
            String sql="select * from student where name= ? ";
            preparedStatement=connection.prepareStatement(sql);
            preparedStatement.setString(1,"小明");
            resultSet=preparedStatement.executeQuery();
            while (resultSet.next()){
                System.out.println(resultSet.getString("id")+" " +resultSet.getString("name"));
            }


        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
       } catch (SQLException e) {
            e.printStackTrace();
        }

這里我用的是最新的mysql驅動包

第一點:加載驅動。

 Class.forName("com.mysql.cj.jdbc.Driver");
在最開始用的時候很不明白,首先為什么要加載一個驅動類,之后就可以取得
connection,也非常好奇DriverManager是怎么來拿到驅動類的信息,另外還有為什么要用Class.forName加載。
后來點進Driver里,看到:

所以也就是說,在Class.forName加載完驅動類后,開始執行靜態代碼塊時,會new一個Driver,並調用DriverManager的registerDriver把Driver給注冊到自己的驅動程序管理器(DriverManager)中去。

在這里說一下ClassLoader.loadClass和Class.forName的區別:

  • Classloder.loaderClass(String name)

    其實該方法內部調用的是:Classloder. loadClass(name, false)

    方法:Classloder. loadClass(String name, boolean resolve)

        1:參數name代表類的全限定類名

        2:參數resolve代表是否解析,resolve為true是解析該類

  • Class.forName(String name)

    其實該方法內部調用的是:Class.forName(className, true, ClassLoader.getClassLoader(caller))

    方法:Class.forName0(String name, boolean initialize, ClassLoader loader)

      參數name代表全限定類名

      參數initialize表示是否初始化該類,為true是初始化該類

      參數loader 對應的類加載器

  • 兩者最大的區別

    Class.forName得到的class是已經初始化完成的

    Classloder.loaderClass得到的class是沒有初始化的

這里Driver類也繼承NonRegisteringDriver並實現java.sql.Driver接口

我們點進NonRegisteringDriver類會發現,它也是實現java.sql.Driver接口:

最主要看一下Driver接口:

public interface Driver {

    //獲取Connection 方法。數據庫的url,及info至少得包含user,password key
    Connection connect(String url, java.util.Properties info)
        throws SQLException;

    //判斷是否是一個正確的url字符串。
    boolean acceptsURL(String url) throws SQLException;

    //得到驅動的屬性(user,password,port等)。
    DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException;

    //得到主要版本
    int getMajorVersion();

    //得到次要版本
    int getMinorVersion();

    //判斷是否是一個正確的driver
    boolean jdbcCompliant();

    //------------------------- JDBC 4.1 -----------------------------------
    //返回父日志
    public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}

其實Driver接口是每個數據庫驅動都必須繼承的接口。

言歸正傳,我們可以看到在Driver類中的靜態代碼塊有DriverManager.registerDriver(這是把Driver給注冊到自己的驅動程序管理器(DriverManager)中)的方法,我們點進去可以看到DriverManager類,此類沒有繼承和實現任何接口,它是管理一組 JDBC 驅動程序的基本服務。

這里顯示的是一些重要的方法

public class DriverManager {

    //已經注冊的驅動列表
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    private static volatile int loginTimeout = 0;
    private static volatile java.io.PrintWriter logWriter = null;
    private static volatile java.io.PrintStream logStream = null;
    // Used in println() to synchronize logWriter
    private final static  Object logSync = new Object();
    //阻止被初始化,DriverManager里面都是靜態的方法。
    private DriverManager(){}
    //初始化加載驅動,其中用到了ServiceLoader機制
    static {
        //初始化加載驅動
        loadInitialDrivers();
        //打印日志
        println("JDBC DriverManager initialized");
    }
    /*因為源代碼過長,這里我們只講重要的一些方法*/

    //初始化加載驅動
    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;
        }
        //這里涉及到一個ServiceLoader概念。上面我推薦了一篇文章,想了解的可以去看看。通過ServiceLoader去加載所有的driver。
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                //通過ServiceLoader.load()方法加載所有驅動
                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);
            }
        }
    }

    //3個獲取connection方法,對外提供的方法。
    @CallerSensitive
    public static Connection getConnection(String url, java.util.Properties info) throws SQLException {

        return (getConnection(url, info, Reflection.getCallerClass()));
    }   
    //這個方法中可以看到,properties中至少配置參數其實就是user和password
    @CallerSensitive
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }

        return (getConnection(url, info, Reflection.getCallerClass()));
    }

    @CallerSensitive
    public static Connection getConnection(String url)
        throws SQLException {

        java.util.Properties info = new java.util.Properties();
        return (getConnection(url, info, Reflection.getCallerClass()));
    }
     // 內部真正工作的方法(私有)
    private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        //檢查callerCL是否為空,如果為空則通過Thread.currentThread().getContextClassLoader()去加載
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }
        //這里判斷url
        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection(\"" + url + "\")");
        SQLException reason = null;
        //遍歷registeredDrivers去獲得正確的connection
        for(DriverInfo aDriver : registeredDrivers) {
            // 如果callerCL不允許讀取驅動,就會跳過。
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    //真正的獲取connection的方法,其實還是通過driver接口中的connect方法。
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } 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");
    }

    //通過url獲取driver。
    @CallerSensitive
    public static Driver getDriver(String url)
        throws SQLException {

        println("DriverManager.getDriver(\"" + url + "\")");

        Class<?> callerClass = Reflection.getCallerClass();

        // 通過遍歷registeredDrivers中每個驅動
        for (DriverInfo aDriver : registeredDrivers) {
            // acceptsURL()方法判斷url是否符合driver
            if(isDriverAllowed(aDriver.driver, callerClass)) {
                try {
                    if(aDriver.driver.acceptsURL(url)) {
                        println("getDriver returning " + aDriver.driver.getClass().getName());
                    return (aDriver.driver);
                    }

                } catch(SQLException sqe) {

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

        }

        println("getDriver: no suitable driver");
        throw new SQLException("No suitable driver", "08001");
    }

    //注冊驅動的方法
    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 {
        //判斷driver是否已經被加載到registeredDrivers,沒有就加進去
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            throw new NullPointerException();
        }
        println("registerDriver: " + driver);
    }

    //注銷driver方法。
    @CallerSensitive
    public static synchronized void deregisterDriver(Driver driver)
        throws SQLException {
        if (driver == null) {
            return;
        }
        SecurityManager sec = System.getSecurityManager();
        if (sec != null) {
            sec.checkPermission(DEREGISTER_DRIVER_PERMISSION);
        }
        println("DriverManager.deregisterDriver: " + driver);
        DriverInfo aDriver = new DriverInfo(driver, null);
        if(registeredDrivers.contains(aDriver)) {
            if (isDriverAllowed(driver, Reflection.getCallerClass())) {
                DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver));
                 if(di.action() != null) {
                     di.action().deregister();
                 }
                 //通過remove()注銷。
                 registeredDrivers.remove(aDriver);
            } else {
                throw new SecurityException();
            }
        } else {
            println("    couldn't find driver to unload");
        }
    }   
}
getConnection這個方法中的properties類注意一下,它是以后能用到的以.properties為后綴名的屬性文件

第二點:獲取數據庫鏈接Connection

代表與數據庫的鏈接,並擁有創建SQL語句的方法,以完成基本的SQL操作,同時為數據庫事務提供提交和回滾方法

這里說一下Connection接口的重要方法:

public interface Connection  extends Wrapper, AutoCloseable {
    //創建statement
    Statement createStatement() throws SQLException;
    //創建prepareStatement
    PreparedStatement prepareStatement(String sql)
        throws SQLException;
    //創建CallableStatement
    CallableStatement prepareCall(String sql) throws SQLException;
    //轉換sql為本機執行sql
    String nativeSQL(String sql) throws SQLException;
    //設置是否自動提交 狀態
    void setAutoCommit(boolean autoCommit) throws SQLException;
    //判斷是否是自動提交
    boolean getAutoCommit() throws SQLException;
    //提交
    void commit() throws SQLException;
    //回滾
    void rollback() throws SQLException;
    //關閉連接
    void close() throws SQLException;
    //判斷是否關閉
    boolean isClosed() throws SQLException;
    ...//都是一些規范的接口,太多就不一一列舉了。
}

第三點:獲取statement、preparedstatement

statement和preparedStatement的作用是一樣的,都是用來執行sql。區別:

1.代碼的可讀性和可維護性. 
雖然用PreparedStatement來代替Statement會使代碼多出幾行,但這樣的代碼無論從可讀性還是可維護性上來說.都比直接用Statement的代碼高很多檔次: 
stmt.executeUpdate(“insert into tb_name (col1,col2,col2,col4) values (‘”+var1+”’,’”+var2+”’,”+var3+”,’”+var4+”’)”);//stmt是Statement對象實例

perstmt = con.prepareStatement(“insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)”); 
perstmt.setString(1,var1); 
perstmt.setString(2,var2); 
perstmt.setString(3,var3); 
perstmt.setString(4,var4); 
perstmt.executeUpdate(); //prestmt是 PreparedStatement 對象實例

2.PreparedStatement盡最大可能提高性能. 

3.最重要的一點是極大地提高了安全性.

由此可見:開發的時候盡量用preparedStatement,少用statement。 

preparedStatement中一些重要的方法:

public interface PreparedStatement extends Statement {
    //用於產生單個結果集的語句,例如 SELECT 語句
    ResultSet executeQuery() throws SQLException;
    //用於執行 INSERT、UPDATE 或 DELETE 語句以及 SQL DDL(數據定義語言)語句
    int executeUpdate() throws SQLException;
    //設置空值,必須穿入type,不然可能報空指針異常
    void setNull(int parameterIndex, int sqlType) throws SQLException;
    ...(同理有很多set的方法)
    //清空屬性
    void clearParameters() throws SQLException;
    //用於執行返回多個結果集、多個更新計數或二者組合的語句
    boolean execute() throws SQLException;
    ...//都是一些規范的接口,太多就不一一列舉了。
}

還有一個接口CallableStatement 提供了一種以標准形式調用已儲存過程的方法。

第四點:ResultSet

結果集(ResultSet)是數據中查詢結果返回的一種對象,可以說結果集是一個存儲查詢結果的對象,但是結果集並不僅僅具有存儲的功能,他同時還具有操縱數據的功能,可能完成對數據的更新等

ResultSet中一些重要的方法:

public interface ResultSet extends Wrapper, AutoCloseable {
    //是否有下一個值
    boolean next() throws SQLException;
    //關閉
    void close() throws SQLException;
    //是否為空
    boolean wasNull() throws SQLException;
    //得到第幾列的String類型數據
    String getString(int columnIndex) throws SQLException;
    boolean getBoolean(int columnIndex) throws SQLException;
    ...(太多get方法不一一列舉)
    //得到列名為columnLabel的值
    String getString(String columnLabel) throws SQLException;

    //更新第幾列為空(各種update方法)
    void updateNull(int columnIndex) throws SQLException;
    //插入(updateRow、deleteRow、refreshRow 等)
    void insertRow() throws SQLException;
}

以上就是jdbc中所注意的類,接口源碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM