Java 編程之 JDBC

0x01 前言

JDBC(Java DataBase Connectivity,java 數(shù)據(jù)庫連接)是一種用于執(zhí)行 SQL 語句的 Java API坎拐,可以為多種關(guān)系數(shù)據(jù)庫提供統(tǒng)一訪問桨昙,它由一組用 Java 語言編寫的類和接口組成。JDBC 提供了一種基準(zhǔn),據(jù)此可以構(gòu)建更高級的工具和接口啥容,使數(shù)據(jù)庫開發(fā)人員能夠編寫數(shù)據(jù)庫應(yīng)用程序。

JDBC架構(gòu)

JDBC API 支持兩層和三層處理模型進(jìn)行數(shù)據(jù)庫訪問顷霹,但在一般的 JDBC 體系結(jié)構(gòu)由兩層組成:

JDBC API:提供了應(yīng)用程序?qū)?JDBC 的管理連接咪惠。

JDBC Driver API:支持 JDBC 管理到驅(qū)動器連接。

JDBC API 的使用驅(qū)動程序管理器和數(shù)據(jù)庫特定的驅(qū)動程序提供透明的連接到異構(gòu)數(shù)據(jù)庫淋淀。

JDBC 驅(qū)動程序管理器可確保正確的驅(qū)動程序來訪問每個數(shù)據(jù)源遥昧。該驅(qū)動程序管理器能夠支持連接到多個異構(gòu)數(shù)據(jù)庫的多個并發(fā)的驅(qū)動程序。

以下是JDBC結(jié)構(gòu)圖朵纷,它顯示了驅(qū)動程序管理器方面的 JDBC 驅(qū)動程序和 Java 應(yīng)用程序的位置炭臭。

JDBC 結(jié)構(gòu)圖.jpg

常見的JDBC組件

  • DriverManager:這個類管理數(shù)據(jù)庫驅(qū)動程序的列表。確定內(nèi)容是否符合從 Java 應(yīng)用程序使用的通信子協(xié)議正確的數(shù)據(jù)庫驅(qū)動程序的連接請求袍辞。識別 JDBC 在一定子協(xié)議的第一個驅(qū)動器將被用來建立數(shù)據(jù)庫連接鞋仍。

  • Driver: 此接口處理與數(shù)據(jù)庫服務(wù)器通信。很少直接直接使用驅(qū)動程序(Driver)對象搅吁,一般使用 DriverManager 中的對象威创,它用于管理此類型的對象。它也抽象與驅(qū)動程序?qū)ο蠊ぷ飨嚓P(guān)的詳細(xì)信息似芝。

  • Connection:此接口與接觸數(shù)據(jù)庫的所有方法那婉。連接對象表示通信上下文,即党瓮,與數(shù)據(jù)庫中的所有的通信是通過此唯一的連接對象详炬。

  • Statement:可以使用這個接口創(chuàng)建的對象的SQL語句提交到數(shù)據(jù)庫。一些派生的接口接受除執(zhí)行存儲過程的參數(shù)寞奸。

  • ResultSet:這些對象保存從數(shù)據(jù)庫后呛谜,執(zhí)行使用 Statement 對象的 SQL 查詢中檢索數(shù)據(jù)。它作為一個迭代器枪萄,可以通過移動它來檢索下一個數(shù)據(jù)隐岛。

  • SQLException:這個類用于處理發(fā)生在數(shù)據(jù)庫應(yīng)用程序中的任何錯誤。

0x02 創(chuàng)建簡單的一個 JDBC

構(gòu)建 JDBC 應(yīng)用程序涉及以下六個步驟:

  1. 導(dǎo)入包:需要包含包含數(shù)據(jù)庫編程所需的 JDBC 類的包瓷翻。大多數(shù)情況下聚凹,使用 import java.sql.* 就足夠了割坠。

  2. 注冊 JDBC 驅(qū)動程序:需要初始化驅(qū)動程序,以便可以打開與數(shù)據(jù)庫的通信通道妒牙。

  3. 打開一個連接:需要使用 DriverManager.getConnection() 方法創(chuàng)建一個 Connection 對象彼哼,它表示與數(shù)據(jù)庫的物理連接。

  4. 執(zhí)行查詢:需要使用類型為 Statement 的對象來構(gòu)建和提交 SQL 語句到數(shù)據(jù)庫湘今。

  5. 從結(jié)果集中提取數(shù)據(jù):需要使用相應(yīng)的 ResultSet.getXXX() 方法從結(jié)果集中檢索數(shù)據(jù)敢朱。

  6. 清理環(huán)境:需要明確地關(guān)閉所有數(shù)據(jù)庫資源,而不依賴于 JVM 的垃圾收集摩瞎。

  7. 口訣:(賈璉欲執(zhí)事)拴签。

具體實現(xiàn)如下:

1. 導(dǎo)入包

在程序中包含數(shù)據(jù)庫編程所需的 JDBC 類。大多數(shù)情況下旗们,使用 import java.sql.* 就足夠了蚓哩,如下所示。

//STEP 1. Import required packages
import java.sql.*;

2. 注冊JDBC驅(qū)動程序

需要初始化驅(qū)動程序蚪拦,這樣就可以打開與數(shù)據(jù)庫的通信杖剪。以下是代碼片段實現(xiàn)這一目標(biāo)。

//STEP 2: Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");

3. 打開一個連接

使用 DriverManager.getConnection() 方法來創(chuàng)建一個 Connection 對象驰贷,它代表一個數(shù)據(jù)庫的物理連接盛嘿,如下所示。

//STEP 3: Open a connection
//Database credentials
static final String USER = "root";
static final String PASS = "pwd123456";
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);

4. 執(zhí)行一個查詢

需要使用一個類型為 StatementPreparedStatement 的對象括袒,并提交一個 SQL 語句到數(shù)據(jù)庫執(zhí)行查詢次兆。如下:

//STEP 4: Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);

如果要執(zhí)行一個SQL語句:UPDATEINSERTDELETE 語句锹锰,那么需要下面的代碼片段:

//STEP 4: Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "DELETE FROM Employees";
ResultSet rs = stmt.executeUpdate(sql);

5. 從結(jié)果集中提取數(shù)據(jù)

這一步中演示如何從數(shù)據(jù)庫中獲取查詢結(jié)果的數(shù)據(jù)芥炭。可以使用適當(dāng)?shù)?ResultSet.getXXX() 方法來檢索的數(shù)據(jù)結(jié)果如下恃慧。

//STEP 5: Extract data from result set
while(rs.next()){
    //Retrieve by column name
    int id= rs.getInt("id");
    int age = rs.getInt("age");
    String first = rs.getString("first");
    String last = rs.getString("last");
    //Display values
    System.out.print("ID: " + id);
    System.out.print(", Age: " + age);
    System.out.print(", First: " + first);
    System.out.println(", Last: " + last);
}

6. 清理環(huán)境資源

在使用 JDBC 與數(shù)據(jù)交互操作數(shù)據(jù)庫中的數(shù)據(jù)后园蝠,應(yīng)該明確地關(guān)閉所有的數(shù)據(jù)庫資源以減少資源的浪費,對依賴于 JVM 的垃圾收集如下痢士。

//STEP 6: Clean-up environment
rs.close();
stmt.close();
conn.close();

0x03 JDBC Statements, PreparedStatement 和 CallableStatement 語句

接口 推薦使用
Statement 用于對數(shù)據(jù)庫進(jìn)行通用訪問彪薛,在運行時使用靜態(tài)SQL語句時很有用。Statement 接口不能接受參數(shù)怠蹂。
PreparedStatement 當(dāng)計劃要多次使用SQL語句時使用善延。PreparedStatement 接口在運行時接受輸入?yún)?shù)。
CallableStatement 當(dāng)想要訪問數(shù)據(jù)庫存儲過程時使用城侧。CallableStatement 接口也可以接受運行時輸入?yún)?shù)易遣。

處理 Statement 對象

創(chuàng)建 Statement 對象

在使用 Statement 對象執(zhí)行 SQL 語句之前,需要使用 Connection 對象的 createStatement() 方法創(chuàng)建一個 Statement 對象嫌佑。

在創(chuàng)建 Statement 對象后豆茫,可以使用它來執(zhí)行一個SQL語句侨歉,它有三個執(zhí)行方法可以執(zhí)行。

  • boolean execute (String SQL):如果可以檢索到 ResultSet 對象澜薄,則返回一個布爾值 true ; 否則返回 false 为肮。使用此方法執(zhí)行 SQL DDL 語句或需要使用真正的動態(tài) SQL,可使用于執(zhí)行創(chuàng)建數(shù)據(jù)庫肤京,創(chuàng)建表的 SQL 語句等等。

  • int executeUpdate (String SQL):返回受 SQL 語句執(zhí)行影響的行數(shù)茅特。使用此方法執(zhí)行預(yù)期會影響多行的 SQL 語句忘分,例如:INSERTUPDATEDELETE 語句白修。

  • ResultSet executeQuery(String SQL):返回一個 ResultSet 對象妒峦。 當(dāng)您希望獲得結(jié)果集時,請使用此方法兵睛,就像使用 SELECT 語句一樣肯骇。

關(guān)閉 Statement 對象

操作結(jié)束后,應(yīng)關(guān)閉 Statement 對象祖很。就像關(guān)閉一個 Connection 對象一樣笛丙,以保存數(shù)據(jù)庫資源一樣,由于同樣的原因假颇,還應(yīng)該關(guān)閉 Statement 對象胚鸯。

一個簡單的調(diào)用 close() 方法將執(zhí)行該作業(yè)(工作)。 如果先關(guān)閉 Connection 對象笨鸡,它也會關(guān)閉 Statement 對象姜钳。但是,應(yīng)該始終顯式關(guān)閉 Statement 對象形耗,以確保正確的清理順序哥桥。

Statement stmt = null;
try {
    stmt = conn.createStatement( );
    // TODO
}
catch (SQLException e) {
    // TODO
}
finally {
    stmt.close();
}

Statement 對象具體實現(xiàn)

在使用 Statement 對象執(zhí)行 SQL 語句之前,需要使用 Connection 對象的 createStatement() 方法創(chuàng)建一個 Statement 對象激涤,然后調(diào)用上述處理 Statement 類方法提交執(zhí)行 SQL 拟糕。

Statement 操作 DDL

使用 Satemtnt 類創(chuàng)建數(shù)據(jù)庫表。

@Test
public void testDDL() {
    String sql = "CREATE TABLE student(id BIGINT AUTO_INCREMENT, name VARCHAR(20), age INT, PRIMARY KEY(id), intro VARCHAR(64))";
    Connection connection = null; // 數(shù)據(jù)庫連接對象
    Statement statement = null; // 用于執(zhí)行靜態(tài) SQL 語句并返回它所生成結(jié)果的對象
    try {
        Class.forName("com.mysql.jdbc.Driver"); // 加載注冊驅(qū)動
        connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root"); // 創(chuàng)建 MySQL 連接對象
        statement = connection.createStatement(); // 創(chuàng)建 Statement 對象昔期,用于提交和接收 MySQL
        int row = statement.executeUpdate(sql); // 提交 SQL 語句已卸,并接收執(zhí)行結(jié)果
        System.out.println("數(shù)據(jù)庫修改行數(shù):" + row); // 執(zhí)行創(chuàng)建表 SQL 影響行數(shù)為 0
    } catch (Exception e) { // 釋放資源
        e.printStackTrace();
    } finally {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
Java7 新特性創(chuàng)建數(shù)據(jù)庫表
@Test
public void testDDL_JAVA7() {
    String sql = "CREATE TABLE student(id BIGINT AUTO_INCREMENT, name VARCHAR(20), age INT, PRIMARY KEY(id))";
    try {
        Class.forName("com.mysql.jdbc.Driver");
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root"); Statement st = conn.createStatement();) {
            st.executeUpdate(sql);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Statement 操作 DML
@Test
public void testInsert() {
    String sql = "INSERT INTO t_student (id, name, age, intro) VALUES (null, '王五', 30, '王五是個好孩子')";
    Connection connection = null;
    Statement statement = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root");
        statement = connection.createStatement();
        statement.executeUpdate(sql);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
Statement 操作 DQL
@Test
public void testQuery() {
    String sql = "SELECT * FROM t_student";
    Connection connection = null;
    Statement statement = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root");
        statement = connection.createStatement();
        ResultSet query = statement.executeQuery(sql); // 接收查詢返回的結(jié)果
        while (query.next()) { // 遍歷返回結(jié)果集
            Long id = query.getLong("id"); // 根據(jù)列名獲取結(jié)果
            String name = query.getString("name");
            Integer age = query.getInt("age");
            String intro = query.getString("intro");
            System.out.println(id + " " + name + " " + age + " " + intro);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

PreparedStatement 對象

PreparedStatement 接口擴(kuò)展了 Statement 接口,它添加了比 Statement 對象更好一些優(yōu)點的功能硼一。此語句可以動態(tài)地提供 / 接受參數(shù)累澡。(實例后面提供,將抽取重復(fù)代碼般贼,統(tǒng)一處理結(jié)果集和賦值)

創(chuàng)建 PreparedStatement 對象

JDBC 中的所有參數(shù)都由 ? 符號作為占位符愧哟,這被稱為參數(shù)標(biāo)記奥吩。在執(zhí)行SQL語句之前,必須為每個參數(shù)(占位符)提供值蕊梧。

setXXX() 方法將值綁定到參數(shù)霞赫,其中 XXX 表示要綁定到輸入?yún)?shù)的值的 Java 數(shù)據(jù)類型。 如果忘記提供綁定值肥矢,則將會拋出一個 SQLException 端衰。

每個參數(shù)標(biāo)記是它其順序位置引用。第一個標(biāo)記表示位置 1甘改,下一個位置 2 等等旅东。該方法與 Java 數(shù)組索引不同(它不從 0 開始)。

所有 Statement 對象與數(shù)據(jù)庫交互的方法 execute()十艾, executeQuery()executeUpdate() 也可以用于 PreparedStatement 對象抵代。但是,這些方法被修改為可以使用輸入?yún)?shù)的 SQL 語句忘嫉。

關(guān)閉PreparedStatement對象

就像關(guān)閉 Statement 對象一樣荤牍,由于同樣的原因 (節(jié)省數(shù)據(jù)庫系統(tǒng)資源),也應(yīng)該關(guān)閉 PreparedStatement 對象庆冕。

簡單的調(diào)用 close() 方法將執(zhí)行關(guān)閉康吵。 如果先關(guān)閉 Connection 對象,它也會關(guān)閉 PreparedStatement 對象愧杯。 但是涎才,應(yīng)該始終顯式關(guān)閉 PreparedStatement 對象,以確保以正確順序清理資源力九。

PreparedStatement pstmt = null;
try {
    String SQL = "Update Employees SET age = ? WHERE id = ?";
    pstmt = conn.prepareStatement(SQL);
    // TODO
}
catch (SQLException e) {
    // TODO
}
finally {
    pstmt.close();
}

CallableStatement 對象

創(chuàng)建 CallableStatement 對象

類似 Connection 對象創(chuàng)建 StatementPreparedStatement 對象一樣耍铜,它還可以使用同樣的方式創(chuàng)建 CallableStatement 對象,該對象將用于執(zhí)行對數(shù)據(jù)庫存儲過程的調(diào)用跌前。

留坑棕兼。。抵乓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伴挚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子灾炭,更是在濱河造成了極大的恐慌茎芋,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜈出,死亡現(xiàn)場離奇詭異田弥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)铡原,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門偷厦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來商叹,“玉大人,你說我怎么就攤上這事只泼∑鼠希” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵请唱,是天一觀的道長弥咪。 經(jīng)常有香客問我,道長籍滴,這世上最難降的妖魔是什么酪夷? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮孽惰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸥印。我一直安慰自己勋功,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布库说。 她就那樣靜靜地躺著狂鞋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪潜的。 梳的紋絲不亂的頭發(fā)上骚揍,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機(jī)與錄音啰挪,去河邊找鬼信不。 笑死,一個胖子當(dāng)著我的面吹牛亡呵,可吹牛的內(nèi)容都是我干的抽活。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼锰什,長吁一口氣:“原來是場噩夢啊……” “哼下硕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汁胆,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤梭姓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嫩码,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體誉尖,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年谢谦,在試婚紗的時候發(fā)現(xiàn)自己被綠了释牺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萝衩。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖没咙,靈堂內(nèi)的尸體忽然破棺而出猩谊,到底是詐尸還是另有隱情,我是刑警寧澤祭刚,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布牌捷,位于F島的核電站,受9級特大地震影響涡驮,放射性物質(zhì)發(fā)生泄漏暗甥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一捉捅、第九天 我趴在偏房一處隱蔽的房頂上張望撤防。 院中可真熱鬧,春花似錦棒口、人聲如沸寄月。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至灸眼,卻和暖如春茎毁,著一層夾襖步出監(jiān)牢的瞬間克懊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工七蜘, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留谭溉,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓崔梗,卻偏偏與公主長得像夜只,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蒜魄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法扔亥,類相關(guān)的語法,內(nèi)部類的語法谈为,繼承相關(guān)的語法旅挤,異常的語法,線程的語...
    子非魚_t_閱讀 31,665評論 18 399
  • JDBC概述 在Java中伞鲫,數(shù)據(jù)庫存取技術(shù)可分為如下幾類:JDBC直接訪問數(shù)據(jù)庫粘茄、JDO技術(shù)、第三方O/R工具,如...
    usopp閱讀 3,543評論 3 75
  • 1. 簡介 1.1 什么是 MyBatis 柒瓣? MyBatis 是支持定制化 SQL儒搭、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,532評論 0 4
  • 本節(jié)介紹Statement接口及其子類PreparedStatement和CallableStatement。 它...
    zlb閱讀 1,165評論 0 0
  • 在中國芙贫,有一群可愛的人 戰(zhàn)爭年代搂鲫,用生命抵擋敵人的戰(zhàn)火 和平年代,用身軀與天災(zāi)相搏 他們的名字叫:中國軍人 他們的...
    靜聽若言閱讀 337評論 0 3