天天看點

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

作者:JAVA後端架構
如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

1. 前言

大家好,我是喬治。最近又肝了幾個大夜,總結了 JDBC 完整版的基礎教程和實戰案例訓練。快來看看這些 Java 基礎性的代碼你有沒有忘記?

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

在 Java 開發中,使用 Java 語言操作資料庫是非常重要的一部分,那麼 Java 語言是如何操作資料庫的呢?

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

我們需要使用不同廠商的資料庫時,例如 MySQL,Oracle 等,顯然一套 Java 代碼是不能同時操作不同的資料庫的,那麼怎樣實作一套 Java 代碼對不同的資料庫的操作呢?

JDBC 應運而生,JDBC 是使用 Java 語言操作關系型資料庫的一套 API,其中定義了對資料庫操作的規範,這裡的規範在 Java 中就是指接口,不同的資料庫廠商提供了不同的 JDBC 實作類,稱為驅動,使用時,隻需要導入需要的資料庫驅動 jar 包,便可以操作不同的關系型資料庫,其實際是使用了 jar 包中的實作類。

本系列文章的學習目标:

  • 什麼是 JDBC,如何使用?
  • 使用 JDBC 完成資料的增删改查操作
  • JDBC API 熟練使用
學會使用Java代碼操作資料庫,本文是2 萬字學習教程,閱讀本文需要20 分鐘,建議收藏長期學習使用。

2. JDBC 概述

2.1 概念

JDBC , Java DataBase Connectivity , 是使用 Java 語言操作關系型資料庫的一套API。本質上來說,JDBC 中定義了一套操作關系型資料庫的規範,但是我們不能直接使用這套接口來操作資料庫,于是各大資料庫廠商提供了 JDBC 不同的實作類,稱為驅動,例如 MySQL 驅動,此時,我們隻需要操作 JDBC 規範好的代碼便可以完成對資料庫的操作。在實作對資料庫的操作時,其底層是使用了 jar 包中定義的實作類。

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

當我們使用不同的資料庫時,例如測試時使用 MySQL 資料庫,部署時使用 Oracle 資料庫,隻需要編寫一套 Java 代碼便可以實作對不同關系型資料庫的操作。

2.2 優點

使用 JDBC 操作關系型資料庫時,各大資料庫廠商提供了不同的實作類,我們不需要針對不同的資料庫進行單獨開發,是以,我們也可以随時替換資料庫,而不用大量修改 Java 代碼。

我們隻需要在使用時導入需要使用的資料庫對應的驅動 jar包到項目中,便可以實作對指定資料庫的操作,使 Java 操作資料庫變得輕松便捷。

下圖就是MySQL的驅動jar包:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

不同版本的 jar 包可以在官網下載下傳。

3. JDBC 快速入門

使用 Java 語言操作資料庫,實際上就是 Java 代碼将 sql 語句發送到 MySQL 資料庫服務端,MySQL 服務端接收并執行 sql 語句,同時傳回一個執行結果,最後該結果會發送到 Java 代碼進行處理。

對資料庫的操作大緻分為以下幾個步驟:

  • 注冊驅動
  • 擷取連接配接
  • 編寫sql
  • 擷取執行sql對象
  • 執行sql
  • 處理傳回結果
  • 釋放資源

在進行編碼之前,需要先建立工程,導入資料庫的驅動 jar 包。這裡以 JDBC 操作 MySQL 資料庫為例,步驟如下:

建立空項目,定義項目名稱及路徑:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

在File / Project Structure 中進行項目設定,JDK版本,編譯版本等:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

建立新子產品,指定名稱及位置:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

在 jdbc-demo 子產品中建立 Lib 檔案夾:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

将下載下傳好的 MySQL驅動 jar 包導入 Lib 目錄下作為庫檔案:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

右鍵 MySQL 驅動 jar 包,點選 Add as Library ,并選擇子產品下有效:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

建立好工程以後,就可以編寫代碼操作資料庫啦!

在 idea 中右鍵 src / new / Java Class ,輸入類名 JDBCDemo 建立一個新的類。

第一步:注冊驅動

//1.注冊驅動
Class.forName("com.mysql.jdbc.Driver");           

目的是把 Driver 類加載到記憶體中。其實,Java 中用于注冊驅動的是 registerDriver() 方法,而在 Driver 類中使用了該方法,是以隻要把 Driver 類加載到記憶體中 ,包含 registerDriver 方法的靜态代碼塊就會執行,驅動就會被注冊。我們可以檢視JDK源碼中的Driver 類:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}           

第二步:擷取連接配接

String url="jdbc:mysql://localhost:3306?useSSL=false";
String username="root";
String pass word="abc123";
Connection conn = DriverManager.getConnection(url, username, password);           

目的是通過 getConnection() 方法擷取資料庫連接配接 Connection 類對象,這裡沒有直接把資料傳入方法之中,而是定義了三個局部變量,url,資料庫使用者名和密碼,其中 url 參數有固定的文法格式:

jdbc:mysql://ip位址(域名):端口号?參數鍵值對1&參數鍵值對2           

第三步:定義sql

String sql="update account set money=2000 where name='張三'";           

目的是定義操作資料庫的 sql 語句。

第四步:擷取執行sql的對象

Statement stmt = conn.createStatement();           

目的是在執行 sql 語句之前,先擷取執行 sql 的對象 statement。

第五步:執行sql

int count = stmt.executeUpdate(sql);           

目的是為了将 sql 發送到資料庫服務端,讓資料庫服務端執行完 sql,并且傳回一個結果,這裡的 executeUpdate() 傳回受影響的行數。

第六步:處理傳回結果

System.out.println(count);           

目的是處理資料庫服務端傳回的 sq l執行結果,這裡我們列印了 executeUpdate() 方法傳回的受影響的行數。

第七步:釋放資源

stmt.close();
conn.close();           

目的是在完成對資料庫的操作以後釋放資源,否則資料庫檔案将一直被被程式占用,釋放資源時遵循先開後釋放的原則。

使用 JDBC 操作 MySQL 資料庫完整代碼:

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1.注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2.擷取連接配接
        String url="jdbc:mysql://localhost:3306/db1?useSSL=false";
        String username="root";
        String pass word="abc123";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3.定義sql
        String sql="update account set money=2000 where name='張三'";
        //4.擷取執行sql的對象
        Statement stmt = conn.createStatement();
        //5.執行sql
        int count = stmt.executeUpdate(sql);
        //6.處理結果
        System.out.println(count);
        //7.釋放資源
        stmt.close();
        conn.close();
    }
}
           

4. JDBC API詳解

4.1 DriverManager

DriverManager ,驅動管理類。在 JDBC 入門篇中,我們使用了該類的方法來注冊驅動和擷取連接配接。

DriverManager 類主要有兩個作用:

  • 注冊驅動
  • 擷取連接配接

4.1.1 注冊驅動

在 Driver 類靜态代碼塊中 DriverManager 類執行了其 registerDriver() 方法用于注冊驅動,當我們把類Driver 加載到記憶體中後,該靜态代碼塊就會執行,此時就完成了驅動注冊,我們就是使用這樣的方法注冊驅動的。檢視 JDK 源碼就不難了解:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}           

需要注意的是,在 MySQL 5 以後的驅動 jar 包可以不用進行此步驟來注冊驅動,當我們把 jar 導入到項目以後,程式會自動加載 jar 包中 META-INF/services/java.sql.Driver 檔案中的驅動類。

4.1.2 擷取連接配接

我們使用 DriverManager 類的 getConnection() 靜态方法來擷取資料庫連接配接對象,其方法有三個參數,分别是 url,資料庫的使用者名和密碼。url 有其固定的文法格式:

jdbc:mysql://ip位址(域名):端口号/連接配接的資料庫名?參數鍵值對1&參數鍵值對2

jdbc:mysql://連接配接的資料庫名?參數鍵值對1&參數鍵值對2 //如果使用的是本地的 MySQL ,并且使用預設的端口号 3306 的,url 的值可以簡寫
           

例如:

jdbc:mysql://db1?useSSL=false
           
這裡 useSSL = false 關閉了安全連接配接方式,解決了 idea 警告的問題。

4.2 Connection

Connection ,資料庫連接配接對象接口。在入門篇中使用了該類擷取 sql 的執行對象 statement。

Connection 接口的作用有:

擷取執行 SQL 的對象

管理事務

4.2.1 擷取執行sql的對象

Connection 中的 createStatement() 方法可以擷取執行 sql 的對象 Statement ,用于把 sql 發送到資料庫服務端。使用 preparedStatement() 方法可以擷取預編譯 sql 的執行 sql 的對象,這個方法可以有效的防止 sql 注入的問題。

4.2.2 事務管理

在JDBC 中使用 Connection 對象進行事務管理,Connection 中定義了三個對應的方法:

開啟事務:

void setAutoCommit(boolean autoCommit) //将此連接配接的自動送出模式設定為給定狀态           

復原事務:

void rollback() //撤消目前事務中所做的所有更改,并釋放此 Connection對象目前持有的所有資料庫鎖           

送出事務:

void commit() //使自上次送出/復原以來所做的所有更改成為永久更改,并釋放此 Connection對象目前持有的所有資料庫鎖           

示例:

public class JDBCDemo {

    public static void main(String[] args) throws Exception {
        //1. 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2. 擷取連接配接:如果連接配接的是本機mysql并且端口是預設的 3306 可以簡化書寫
        String url = "jdbc:mysql://db1?useSSL=false";
        String username = "root";
        String ppassword = "abc123";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定義sql
        String sql1 = "update account set money = 3000 where id = 1";
        String sql2 = "update account set money = 3000 where id = 2";
        //4. 擷取執行sql的對象 Statement
        Statement stmt = conn.createStatement();

        try {
            // 開啟事務
            conn.setAutoCommit(false);
            //5. 執行sql
            int count1 = stmt.executeUpdate(sql1);//受影響的行數
            //6. 處理結果
            System.out.println(count1);
            int i = 3 / 0;
            //5. 執行sql
            int count2 = stmt.executeUpdate(sql2);
            //6. 處理結果
            System.out.println(count2);

            // 送出事務
            conn.commit();
        } catch (Exception e) {
            // 復原事務
            conn.rollback();
            e.printStackTrace();
        }

        //7. 釋放資源
        stmt.close();
        conn.close();
    }
}
           

我們使用 Java 中的異常捕獲機制來進行事務的管理。将執行 sql 的語句放在 try 代碼塊中,并且使用 setAutoCommit() 方法開啟事務,如果代碼塊中沒有出現異常,則使用 commit() 方法送出事務,如果 try 代碼塊某處出現了異常,則需要復原事務,是以将復原事務的 rollback() 方法定義在 catch 語句塊中。

4.3 Statement

Statement 類的對象用來執行 sql 語句,即把 sql 發送到資料庫服務端,但是使用此類對象執行 sql 會出現 sql 注入的問題。不同的 sql 語句使用不同的方法執行,執行 DDL,DML 語句使用下面的方法:

int executeUpdate(String sql)//執行sql語句完成增删改操作           

在執行 DML 語句完成對資料的增删改操作時,該方法傳回資料表中受影響的行數,可以使用這個傳回值來判斷是否成功完成對資料的操作。而使用 DDL 來操作資料庫和資料表時,傳回值可能為 0,是以不能用作上面的判斷,且在開發中 DDL 很少被用到。

示例1,使用 DML 修改資料:

//3.定義sql
String sql="update account set money=2000 where name='張三'";
//5.執行sql
int count = stmt.executeUpdate(sql);
//6.處理結果
if(count>0){
    System.out.println("修改成功!");
}else{
     System.out.println("修改失敗!");
}
           

示例2,使用 DDL 删除資料庫:

//3.定義sql
String sql="drop database db2";
//4.擷取執行sql的對象
Statement stmt = conn.createStatement();
//5.執行sql
int count = stmt.executeUpdate(sql);
//6.處理結果
System.out.println(count);
           

此時,控制台傳回了 0 ,但是對資料庫的删除操作已經完成。

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

執行 DQL 語句時需要使用下面的方法:

ResultSet excuteQuery(String sql)//執行sql語句完成查詢操作,傳回單個ResultSet對象           

該方法在下面 ResultSet類中講解。

4.4 ResultSet

ResultSet ,結果集對象類,其作用是封裝 sql 查詢語句的結果。執行了 DQL 查詢語句後就會傳回該類的對象,執行 DQL 語句的方法如下:

ResultSet excuteQuery(String sql)//執行sql語句完成查詢操作,傳回單個ResultSet對象           

ResultSet 類提供了操作查詢結果資料的方法,如下:

/*
作用:将指針從目前位置移動到下一行,然後這一行是否為有效行
傳回值:true 目前行為有效行,false 目前行沒有資料
*/
boolean next()
/*
作用:擷取資料  
xxx表示資料類型 例如 int getInt() 
參數:int類型的參數表示列的編号,這個編号從0開始;String類型的參數,表示列的名稱
*/
xxx getXxx(參數)
    
    
           

操作查詢結果資料的方法如下圖:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

一開始指針位于第一行前,如圖所示紅色箭頭指向于表頭行。當我們調用了 next() 方法後,指針就下移到第一行資料,并且方法傳回 true,此時就可以通過 getInt("id") 擷取目前行 id 字段的值,也可以通過 getString("name") 擷取目前行 name 字段的值。如果想擷取下一行的資料,繼續調用 next() 方法,以此類推。擷取某個字段的值時,既可以傳入 int 類型,即列的編号,也可以傳入列對應的字段名。

示例,查詢 account 表中資料,并且列印所有結果:

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1. 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2. 擷取連接配接:如果連接配接的是本機mysql并且端口是預設的 3306 可以簡化書寫
        String url = "jdbc:mysql://localhost:3306/db1?useSSL=false";
        String username = "root";
        String ppassword = "abc123";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定義sql
        String sql = "select * from account";
        //4. 擷取執行sql對象
        Statement stmt = conn.createStatement();
        //5. 執行sql
        ResultSet rs = stmt.executeQuery(sql);
        //6. 處理傳回結果, 周遊rs中的所有資料
        // 1 指針向下移動一行,并且判斷目前行是否有資料
        while (rs.next()) {
            // 2 擷取資料  getXxx()
            int id = rs.getInt("id");
            String name = rs.getString("name");
            double money = rs.getDouble("money");

            System.out.println(id);
            System.out.println(name);
            System.out.println(money);
        }
        //7. 釋放資源
        rs.close();
        stmt.close();
        conn.close();
    }
}
           

4.5 PreparedStatement

4.5.1 sql注入問題

前面 statement 類的對象用來執行sql語句,例如 :

int executeUpdate(String sql)//執行sql語句完成增删改操作
           

但是使用此方法存在 sql 注入的問題,什麼是 sql 注入呢?這裡做一個大緻的講解。

SQL注入是通過操作輸入來修改事先定義好的 sql 語句,用來達到執行代碼對伺服器進行攻擊的方法。例如,在程式的登入操作中,使用者輸入的使用者名和密碼會被發送到 Java 代碼,然後用于 Java 操作資料庫的sql 語句中,隻有當使用者輸入的使用者名和密碼與資料庫中的資料比對時才能實作登入,但是,隻要我們輸入事先定義好的語句,便可以進行破解。例如下面拼字元串的方式修改 sql 語句原來的含義:

String name ="lisi";
String ppwd="' or '1' ='1";//事先定義好的輸入,拼接到sql語句中後改變其含義
String sql = "Select * from tb_user where name='"+username+"' and ppassword='"+ppwd+"'";
           

此時,将使用者名和密碼拼接到 sql 中,如下:

select * from tb_user where username = 'lisi' and ppassword = ''or '1' = '1';           

可以看到,sql 中的判斷條件永遠為 true ,不管輸入什麼樣的使用者名,這條 sql 都成立,實作了 sql 注入。

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

為了解決這個問題,出現了 preparedStatement ,該類用于預編譯 sql 語句并執行,其優點是可以防止sql 注入,并且預編譯sql提高了性能。其實底層是将特殊字元進行了轉義,轉義的 sql 如下:

select * from tb_user where username = 'lisi' and ppassword = '\'or \'1\' = \'1'           

示例:

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1. 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2. 擷取連接配接
        String url="jdbc:mysql://localhost:3306/db1?useSSL=false";
        String username="root";
        String ppassword="abc123";
        Connection conn = DriverManager.getConnection(url,username,password);
        //Java代碼接收到用戶端發送的使用者名和密碼
        String name="lisi";
        String ppwd="' or '1' ='1";
        //3. 定義sql
        String sql="select * from tb_user where username=? and password=?";
        //4. 擷取執行sql的對象psmt
        PreparedStatement psmt = conn.prepareStatement(sql);
        //設定問号的值
        psmt.setString(1,name);
        psmt.setString(2,pwd);
        //5. 執行sql
        ResultSet rs = psmt.executeQuery();//此時不再需要傳入sql語句
        //6. 處理傳回結果
        if(rs.next()){
            System.out.println("yes");
        }else{
            System.out.println("no");
        }
        //7. 釋放資源
        rs.close();
        psmt.close();
        conn.close();
    }
}
           
如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

4.5.2 preparedStatement 原理

前面使用 preparedStatement 解決了 sql 注入的問題,其實我們還沒有開啟預編譯的功能,JDBC 中是如何通過預編譯來提高性能的呢?

要學習 prepareStatement 實作預編譯的原理,首先要明白 Java 操作資料庫的步驟:

首先 Java 代碼将 sql 發送到 MySQL 服務端,MySQL 服務端接收到 sql 語句以後,會對 sql 語句進行檢查(檢查 sql 語句的文法),編譯(編譯 sql 語句,将 sql 語句編譯成可執行的函數),執行的操作。而檢查和編譯 sql 語句花費的時間往往較長,如果想要提高 sql 的性能,就可以從這方面入手。在使用預編譯的方法時,檢查和編譯 sql 語句的操作将會在擷取執行 sql 的對象時完成,并且不會重複執行,進而提高了性能。

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

要想打開預編譯的功能,就需要在 url 中設定如下的參數:

useServerPrepStmts=true //參數開啟預編譯功能           

示例:

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1. 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2. 擷取連接配接
        String url="jdbc:mysql://localhost:3306/db1?useSSL=false&useServerPrepStmts=true";
        String username="root";
        String ppassword="abc123";
        Connection conn = DriverManager.getConnection(url,username,password);
        //Java代碼接收到用戶端發送的使用者名和密碼
        String name="lisi";
        String ppwd="'or '1' = '1";
        //3. 定義sql
        String sql="select * from tb_user where username=? and password=?";
        //4. 擷取執行sql的對象psmt
        PreparedStatement psmt = conn.prepareStatement(sql);

        //設定問号的值
        psmt.setString(1,name);
        psmt.setString(2,pwd);
        ResultSet rs=null;
        //5. 執行sql
        rs = psmt.executeQuery();//此時不需要傳入sql語句

        psmt.setString(1,"zhangsan");
        psmt.setString(2,"abc123");
        rs=psmt.executeQuery();

        //6. 處理傳回結果
        if(rs.next()){
            System.out.println("yes");
        }else{
            System.out.println("no");
        }
        //7. 釋放資源
        rs.close();
        psmt.close();
        conn.close();
    }
}
           

在擷取 sql 的執行對象時,sql 會作為參數被發送到 MySQL 服務端進行檢查和編譯,在執行時,不再進行第二次檢查和編譯的操作,提高了 sql 執行的性能。

5. 資料庫連接配接池

5.1 概念

前面在 JDBC API 詳解中,擷取的資料庫連接配接對象 conn,在使用時建立,使用完畢就會将其銷毀。這樣重複建立和銷毀的過程實際上是消耗性能和時間的,當大量使用者通路資料庫時,每次都要進行資料庫連接配接對象的建立和銷毀,對系統來說是一種大量的消耗。那麼我們怎樣來提升性能和節省時間呢?

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

我們使用資料庫連接配接池來重複利用資料庫的連接配接對象,即 Connection 類對象。

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

資料庫連接配接池是一個負責配置設定,管理資料庫連接配接對象的容器,它允許應用程式重複使用同一個資料庫連接配接對象。資料庫連接配接池可以釋放空閑時間超過最大空閑時間的資料庫連接配接對象來避免因為沒有釋放資料庫連接配接對象而引起的資料庫連接配接遺漏。

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

連接配接池是在一開始就建立好了一些連接配接對象存儲起來,使用者需要連接配接資料庫時,不需要自己建立連接配接對象,而隻需要從連接配接池中擷取一個連接配接對象進行使用,使用完畢後再将連接配接對象歸還給連接配接池。這樣就可以起到資源重用的作用,也節省了頻繁建立連接配接銷毀連接配接所花費的時間,進而提升了系統響應的速度。

總結來說使用資料庫連接配接池有以下幾點好處:

  • 實作資源重用
  • 提升系統響應速度
  • 避免資料庫連接配接遺漏

5.2 實作

sun 公司提供了資料庫連接配接池的标準接口 DataSource ,我們一般使用第三方實作該接口的實作類,所有實作類都繼承了其擷取連接配接的方法:

Connection getConnection()
           

常見的資料庫連接配接池有:

  • Druid(德魯伊)
  • C3P0
  • DBCP

使用了資料庫連接配接池以後,在擷取資料庫連接配接對象時不需要通過 DriverManager類的 getConnection() 方法,而是直接從資料庫連接配接池中擷取。我們今天要使用的是 Druid 連接配接池,它是阿裡巴巴開源的資料庫連接配接池項目,其功能強大,性能優秀,使用占比高,是一款優秀的資料庫連接配接池。

6. Druid 連接配接池的使用

下面以 Druid 連接配接池為例,講解通過資料庫連接配接池擷取資料庫連接配接對象,主要有以下幾個步驟:

  1. 導入Druid 連接配接池的 jar 包
  2. 定義配置檔案
  3. 加載配置檔案
  4. 擷取資料庫連接配接池對象
  5. 擷取連接配接

第一步:将 Druid 的 jar 包放入項目中的 Lib 目錄下作為庫檔案,jar 包自行下載下傳。

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

選擇 Add as Library,選擇子產品下有效:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

第二步:編寫配置檔案,在 src 目錄下建立檔案 druid.properties 。

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true
username=root
password=1234
# 初始化連接配接數量
initialSize=5
# 最大連接配接數
maxActive=10
# 最大等待時間
maxWait=3000
           

Druid 配置檔案中有很很多的參數,這裡配置了用到的幾項,其中有連接配接資料庫的名稱和密碼,初始連接配接數,最大連接配接數,最大等待時間等,超過了最大等待時間配置檔案還沒有加載成功的話,程式就會報錯。

第三步:在代碼中加載配置檔案

//3. 加載配置檔案
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
           

第四步:在代碼中擷取連接配接池對象

//4. 擷取連接配接池對象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
           

第五步:擷取資料庫連接配接對象

//5. 擷取資料庫連接配接 Connection
Connection connection = dataSource.getConnection();
System.out.println(connection); //擷取到了連接配接後就可以繼續做其他操作了
           

示例,Druid的使用完整代碼:

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //1.導入jar包
        //2.定義配置檔案
        //3. 加載配置檔案
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //4. 擷取連接配接池對象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //5. 擷取資料庫連接配接 Connection
        Connection connection = dataSource.getConnection();
        System.out.println(connection); //擷取到了連接配接後就可以繼續做其他操作了  
    }
}
           

運作結果:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

其中,DruidDataSourceFactory 類中的 createDataSource() 方法既可以傳入一個 Map 集合,也可以傳入 prop 對象,其中都存放配置資訊,用來擷取連接配接池對象。

小tips:如果代碼中檔案的相對路徑報錯,可以使用 System.getProperty("user.dir") 擷取項目的目前路徑。
如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

7. 準備工作

前面我們說過,在 Java 代碼中,執行 sql 查詢語句以後傳回一個 ResultSet 類的對象,并且 ResultSet 類提供了方法讓我們可以操作查詢結果資料,例如可以直接列印所有查詢的資料。

示例:

public class JDBCDemo {

    public static void main(String[] args) throws Exception {
        //1. 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2. 擷取連接配接
        String url="jdbc:mysql://localhost:3306/blog?useSSL=false";
        String username="root";
        String ppassword="abc123";//密碼
        Connection conn = DriverManager.getConnection(url,username,password);
        //3. 定義sql
        String sql="select * from student";
        //4. 擷取sql執行對象
        Statement stmt = conn.createStatement();
        //5. 執行sql
        ResultSet rs = stmt.executeQuery(sql);
        //6. 處理結果
        while (rs.next()){
            int id=rs.getInt(1);
            String name=rs.getString(2);
            String gender=rs.getString(3);

            System.out.println(id);
            System.out.println(name);
            System.out.println(gender);

            System.out.println("---------");
        }
        //7. 釋放資源
        rs.close();
        stmt.close();
        conn.close();
    }
}
           

運作結果:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

顯然這樣并不是我們想要的效果,資料零散不好處理。是以我們可以把每條資料封裝為一個實體類的對象,每個對象對應資料庫表的一條記錄,并且把每個對象放到集合中存儲。練習使用的資料庫表:

drop table if exists student;

create table student(
	id int primary key auto_increment,
	name varchar(10),
	gender char(1)
);

insert into student(name,gender) values
('張三','男'),
('李四','女'),
('王五','男');
           

在 Navicat 中檢視資料表:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

8. 實戰案例

查詢學生資訊表資料,封裝為 Student 類對象,并存放在 ArrayList 集合中。

思路:要解決這個問題,大概分為以下幾個步驟:

  • 建立一個 Student 實體類
  • 查詢資料庫中的資料,并且指派給對象的屬性
  • 将對象存儲到 ArrayList 集合中

第一步:建立一個Student 實體類,并定義 set() 方法,重寫 Object 類中的 toString 方法,友善檢視列印效果。

public class Student {
    private int id;
    private String name;
    private String gender;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}
           
小tips :在 idea 中使用 Alt + Inset 快捷鍵可以快速給類添加或重寫一些方法,例如 get() ,set() ,toString() 方法等。使用 Ctrl + 滑鼠左鍵可以快速選擇多項方法。

第二步:使用 Java 代碼操作資料庫,查詢資料庫表中所有學生資訊并通過 set() 方法指派給 Student 類的對象,将對象存儲到集合中。

public class JDBCDemo {

    public static void main(String[] args) throws Exception {
        //1. 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2. 擷取連接配接
        String url = "jdbc:mysql://localhost:3306/blog?useSSL=false";
        String username = "root";
        String ppassword = "abc123";//密碼
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定義sql
        String sql = "select * from student";
        //4. 擷取sql執行對象
        Statement stmt = conn.createStatement();
        //5. 執行sql
        ResultSet rs = stmt.executeQuery(sql);
        //6. 處理結果
        List<Student> students = new ArrayList<>();

        while (rs.next()) {
            Student s = new Student();
            int id = rs.getInt(1);
            String name = rs.getString(2);
            String gender = rs.getString(3);

            s.setId(id);
            s.setName(name);
            s.setGender(gender);

            students.add(s);
        }
        System.out.println(students);
        //7. 釋放資源
        rs.close();
        stmt.close();
        conn.close();
    }
}
           

運作結果:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

這樣,我們的程式就達到了預期的效果,将來需要使用的資料全部封裝為對象并存放在了集合中。

9. 增删改查操作練習

在資料庫連接配接池入門篇中,我們學習了 Druid 連接配接池的使用,資料庫連接配接池允許重複使用一個現有的資料庫連接配接對象,提升了系統的響應速度和時間。下面我們使用資料庫連接配接池來練習解決上面的問題,并且在擷取 sql 執行對象時,我們使用 PreparedStatement 類,解決sql 注入的問題。

9.1 查詢所有

查詢所有資料,并把查詢結果資料封裝為對象存儲在集合中,這裡的資料表,Student 實體類和上面例子中相同。

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //加載配置檔案
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //擷取資料庫連接配接池對象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //擷取資料庫連接配接對象
        Connection conn = dataSource.getConnection();
        //定義sql
        String sql = "select * from student";
        //擷取 sql 執行對象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //執行 sql
        ResultSet rs = pstmt.executeQuery();
        //處理資料,将查詢結果資料封裝為對象存儲在集合中
        List<Student> students = new ArrayList<>();
  
        while (rs.next()) {
            Student s = new Student();
            int id = rs.getInt(1);
            String name = rs.getString(2);
            String gender = rs.getString(3);

            s.setId(id);
            s.setName(name);
            s.setGender(gender);

            students.add(s);
        }
        System.out.println(students);
        //釋放資源
        rs.close();
        pstmt.close();
        conn.close();
    }
}
           

9.2 添加資料

現在示範往資料庫中添加一條記錄,應用場景為使用者在用戶端輸入一條資料時,我們需要将資料添加到資料庫。示例:

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //接收到使用者的資料
        int id=4;
        String name="小美";
        String gender="女";
        //加載配置檔案
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //擷取資料庫連接配接池對象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //擷取資料庫連接配接對象
        Connection conn = dataSource.getConnection();
        //定義sql
        String sql = "insert into student values(?,?,?)";
        //擷取 sql 執行對象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //設定參數
        pstmt.setInt(1,id);
        pstmt.setString(2,name);
        pstmt.setString(3,gender);
        //執行 sql
        int count = pstmt.executeUpdate();//傳回受影響的行數
        //處理資料
        if(count>0){
            System.out.println("添加成功");
        }else{
            System.out.println("添加失敗");
        }
        //釋放資源
        pstmt.close();
        conn.close();
    }
}
           
如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

此時的資料表:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

9.3 修改資料

現在資料表中的資料,應用場景為使用者在用戶端修改資料,對應資料庫中的資料也要完成修改。示例:

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //接收到使用者的資料
        int id=1;
        String name="小王";
        String gender="女";
        //加載配置檔案
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //擷取資料庫連接配接池對象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //擷取資料庫連接配接對象
        Connection conn = dataSource.getConnection();
        //定義sql
        String sql = "update student set name=?,gender=? where id=?";
        //擷取 sql 執行對象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //設定參數
        pstmt.setString(1,name);
        pstmt.setString(2,gender);
        pstmt.setInt(3,id);
        //執行 sql
        int count = pstmt.executeUpdate();//傳回受影響的行數
        //處理資料
        if(count>0){
            System.out.println("修改成功");
        }else{
            System.out.println("修改失敗");
        }
        //釋放資源
        pstmt.close();
        conn.close();
    }
}
           
如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

此時的資料表:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

9.4 删除資料

下面示範删除資料,使用者在用戶端選擇删除某條資料記錄時,資料庫中的資料也要完成删除操作。示例:

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //接收到使用者的資料
        int id=4;
        //加載配置檔案
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //擷取資料庫連接配接池對象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //擷取資料庫連接配接對象
        Connection conn = dataSource.getConnection();
        //定義sql
        String sql = "delete from student where id=?";
        //擷取 sql 執行對象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //設定參數
        pstmt.setInt(1,id);

        //執行 sql
        int count = pstmt.executeUpdate();//傳回受影響的行數
        //處理資料
        if(count>0){
            System.out.println("删除成功");
        }else{
            System.out.println("删除失敗");
        }
        //釋放資源
        pstmt.close();
        conn.close();
    }
}
           
如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

此時的資料表:

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

10. 總結

學習完這篇文章,你将學會使用 Java 代碼操作資料庫,包括資料庫的連接配接,建立,資料的增删改查等操作。

JDBC 作為基礎性的代碼,在開發中其實存在着很多的缺點,是以在開發中并不會直接使用,而一般會使用架構開發,使用架構開發不僅能夠提高開發的效率,而且大大提高了代碼的規範性。MyBatis 就是一款優秀的持久層架構,專門用來簡化 JDBC 開發,後面我們馬上會接觸到,但是在這之前,一定要先熟練 JDBC 的使用。

為幫助開發者們提升面試技能、有機會入職BATJ等大廠公司,特别制作了這個專輯——這一次整體放出。

大緻内容包括了: Java 集合、JVM、多線程、并發程式設計、設計模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat等大廠面試題等、等技術棧!

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

歡迎大家關注公衆号【Java爛豬皮】,回複【666】,擷取以上最新Java後端架構VIP學習資料以及視訊學習教程,然後一起學習,一文在手,面試我有。

每一個專欄都是大家非常關心,和非常有價值的話題,如果我的文章對你有所幫助,還請幫忙點贊、好評、轉發一下,你的支援會激勵我輸出更高品質的文章,非常感謝!

如何使用JDBC操作資料庫?一文帶你吃透JDBC規範

繼續閱讀