JDBC概念及其底層原理

什麼是jdbc

維基百科定義:
Java數據庫連接,(Java Database Connectivity,簡稱JDBC)是Java語言中用來規範客戶端程序如何來訪問數據庫的應用程序接口,提供了諸如查詢和更新數據庫中數據的方法。JDBC也是Sun Microsystems的商標[1]。JDBC是面向關係型數據庫的。

通俗的說:JDBC就是java語言連接數據庫的規範,每個數據庫廠商的實現都遵守該規範進行實現。

JDBC爲什麼需要數據庫驅動

1,數據庫是一個產品,想要訪問它,就得通過它的方式去連接它的方式去訪問。就像我們寫的接口,別人訪問要按照我們的規則來。

2,底層採用自己的通信協議,每個數據庫廠商都有自己的通信協議。
通過查閱《MySQL技術內幕InnoDB存儲引擎》:mysql
常用的進程間通信方式有:管道,命名管道,命名字,TCP套接字,Unix域套接字。而MySQL提供的連接方式從本質上看都是上述提及的進程通信方式

1,TCP/IP
TCP/IP套接字的方式是MySQL在任何平臺上都提供的連接方式,也是用的最多的。一般情況客戶端一臺機器去連接服務器另一臺機器。兩臺機器之間就是通過TCP/IP連接。客戶端會向服務器MySQL實例發出TCP/IP連接請求,並連接成功。
……

實現六大步驟

1,註冊數據庫驅動

Connection conn = null;
Statement stat = null;
ResultSet rs = null;

Class.forName("com.mysql.jdbc.Driver");

使用反射優點:
解耦

2,獲取數據庫連接

conn = DriverManager.getConnection("jdbc:mysql:///mydb5", "root", "root");

當系統中有多個驅動時,手動管理很麻煩,所以DriverManager類會幫我們管理

原理:
1,它會自己保存一份所有的驅動列表:

// List of registered JDBC drivers
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();

2,我們前面已經調用Class.forName(),系統已經加載了我們所需要的實現類

3,DriverManager加載進來時會執行以下靜態語句

 /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     */
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

4,loadInitialDrivers方法會加載系統中已經加載的所有驅動Driver存放到剛剛那個list裏面
然後我們調用getConnection時,它就會遍歷列表的驅動,幫我們找到合適的驅動Driver並使用

//  Worker method called by the public getConnection() methods.
    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");
    }

3,獲取傳輸器

stat = conn.createStatement();

4,利用傳輸器,將程序構建的sql發送到數據庫中執行,返回執行結果。

rs = stat.executeQuery("select * from account");

5,處理結果

while(rs.next()){

int id = rs.getInt(1);

String name = rs.getString("name");

double money = rs.getDouble("money");

System.out.println(id+name+money);

}

6,釋放資源

rs.close();

防止sql注入

採用PreparedStatement 對象,優點如下:
1,可以防止sql注入攻擊(先將sql骨架發給數據庫編譯下來,後面發送的只能是參數的值,即使傳送特殊符號,也只會當成普通文本處理)。
2,通過方法來設置參數,省去了拼接sql語句的麻煩。
3,可以提高程序的效率(發送的骨架會被緩存下來,如果下次執行的sql與緩存中的相匹配,
就不再編譯而是直接使用緩存中的語句,減少sql的編譯;statement每次拼接好再發
送sql數據庫,每次參數不同,整條sql也就不同,所以每次都需要編譯)

爲什麼需要連接池

1, 用戶每次請求動需要向數據庫獲得連接,而數據創建連接通常需要消耗相對較大的資源,通過連接池共享連接,減少開關連接的次數,提高程序的效率。

2,自定義連接池:(也可採用開源數據庫連接池druid(阿里開源),c3p0(現在用的少了),HikariCP(springboot2.x默認支持))需要在使用完連接後記得不關閉連接,而是調用retrunconn方法將連接還回池中。

3,改造close 方法(a繼承,b裝飾 )目的:將連接還回去,而不是關閉

a

寫一個類繼承要改造的類,對於不想改造的方法不覆蓋,對於想要改造的方法複寫該方法,將代碼改造爲自己需要的邏輯代碼。
這種方式只能在還沒有對象的情況下使用,現在Connection對象已經存在了,再用繼承複寫的方式是不行的,所以我們不採用。

b

實現裝飾設計模式:
(1)寫一個裝飾類, 要求裝飾類和被裝飾者所屬的類實現同一個接口或者繼承同一個父類
(2)裝飾類必須提供構造方法接收被裝飾者, 並將被裝飾者保存在類的內部
(3)對於想要改造的方法直接進行改造, 對於不想改造的方法, 直接調用原有對象(被裝飾者)上的方法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章