JDBC深入

前言

  1. 大二上學(xué)期跟著學(xué)校里面的老師學(xué)JavaSE的時候?qū)懥艘黄浅@腏DBC的文章JDBC捺疼,那時候自認(rèn)為自己對JDBC的認(rèn)識已經(jīng)很高了丈挟,認(rèn)為它很簡單,現(xiàn)在再回頭看撵孤,發(fā)現(xiàn)自己那時候完全是井底之蛙摔寨,所以我覺得得再寫一篇文章深入談?wù)凧DBC去枷,算是給自己的黑歷史抹白吧。
  2. 我之前寫的那篇jdbc文章是复,使用statement操作sql語句會存在SQL注入問題删顶,之后會再寫一篇文件談?wù)勥@個問題
  3. 鑒于之前寫了關(guān)于MySQL事務(wù)機(jī)制的文章,之后淑廊,我也會談?wù)凧DBC事務(wù)機(jī)制的使用

JDBC本質(zhì)

JDBC逗余,本質(zhì)上就是一套接口,是SUN公司制定的一套接口(interface)季惩,包名為:java.sql.*(這個軟件包下有很多接口)

為什么面向接口編程

我們先來看看什么是面向接口編程:

接口都有調(diào)用者和實(shí)現(xiàn)者录粱,面向接口調(diào)用、面向接口寫實(shí)現(xiàn)類画拾,這都屬于面向接口編程啥繁。

為什么要面向接口編程?

  • 解耦合:
    • 降低程序的耦合度
    • 提高程序的擴(kuò)展力青抛。

多態(tài)機(jī)制就是非常典型的:面向抽象編程旗闽。(不要面向具體編程)

思考

現(xiàn)在思考:為什么SUN公司制定一套JDBC接口呢?

  1. 因?yàn)槊恳粋€數(shù)據(jù)庫的底層實(shí)現(xiàn)原理都不一樣蜜另。
  2. Oracle數(shù)據(jù)庫有自己的原理适室。
  3. MySQL數(shù)據(jù)庫也有自己的原理。
  4. MS SqlServer數(shù)據(jù)庫也有自己的原理蚕钦。

每一個數(shù)據(jù)庫產(chǎn)品都有自己獨(dú)特的實(shí)現(xiàn)原理亭病,如果我們不寫一套接口,那么我們這些程序員對同一套Java程序嘶居,需要準(zhǔn)備幾套面向不同數(shù)據(jù)庫的Java代碼,程序員會累死在重復(fù)且無意義的工作上。

而面向接口編程可以實(shí)現(xiàn)一個規(guī)范邮屁,從而讓我們java程序員寫一套java代碼整袁,適配不同的數(shù)據(jù)庫,達(dá)到解放程序員的效果

三方模擬JDBC本質(zhì)

我們扮演SUN公司佑吝,Java程序員坐昙,數(shù)據(jù)庫廠商這三方來模擬一下JDBC

SUN公司

/*
SUN公司負(fù)責(zé)制定這套JDBC接口。
*/
public interface JDBC{
    /*
        連接數(shù)據(jù)庫的方法芋忿。
    */
    void getConnection();
}

數(shù)據(jù)庫廠商

數(shù)據(jù)庫廠商對SUN公司制定出的接口的實(shí)現(xiàn)類,我們稱之為驅(qū)動(Driver)

MySQL

/*
    MySQL的數(shù)據(jù)庫廠家負(fù)責(zé)編寫JDBC接口的實(shí)現(xiàn)類
*/
public class MySQL implements JDBC{

    public void getConnection(){
        // 具體這里的代碼怎么寫,對于我們Java程序員來說沒關(guān)系
        // 這段代碼涉及到mysql底層數(shù)據(jù)庫的實(shí)現(xiàn)原理灸芳。
        System.out.println("連接MYSQL數(shù)據(jù)庫成功期升!");
    }
}

Oracle數(shù)據(jù)庫廠商

/*
    Oracle的數(shù)據(jù)庫廠家負(fù)責(zé)編寫JDBC接口的實(shí)現(xiàn)類
*/
public class Oracle implements JDBC{

    public void getConnection(){
        System.out.println("連接Oracle數(shù)據(jù)庫成功!");
    }
}

Java程序員

顯然殉了,java程序員在操作MySQL數(shù)據(jù)庫時开仰,需要先把MySQL數(shù)據(jù)庫廠商的對JDBC的實(shí)現(xiàn)類的jar包導(dǎo)入工程,也就是需要先把mysql驅(qū)動導(dǎo)入工程薪铜,不然接口的方法沒有實(shí)現(xiàn)類众弓,程序自然無法執(zhí)行。

同理隔箍,java程序員在操作Oracle數(shù)據(jù)庫時谓娃,也需要先導(dǎo)入Oracle驅(qū)動

/*
    Java程序員角色。
    不需要關(guān)心具體是哪個品牌的數(shù)據(jù)庫蜒滩,只需要面向JDBC接口寫代碼傻粘。
    面向接口編程,面向抽象編程帮掉,不要面向具體編程弦悉。
*/
import java.util.*;

public class JavaProgrammer
{
    public static void main(String[] args) throws Exception{
        // JDBC jdbc = new MySQL();
        // JDBC jdbc = new SqlServer();

        // 創(chuàng)建對象可以通過反射機(jī)制。
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String className = bundle.getString("className");
        Class c = Class.forName(className);
        JDBC jdbc = (JDBC)c.newInstance();

        // 以下代碼都是面向接口調(diào)用方法蟆炊,不需要修改
        jdbc.getConnection();
    }
}

三方之間的關(guān)系圖

JDBC本質(zhì).jpg

JDBC編程六步法

第一步:注冊驅(qū)動

  • 作用:告訴Java程序稽莉,即將要連接的是哪個品牌的數(shù)據(jù)庫
Driver driver = new com.mysql.cj.jdbc.Driver(); // 使用多態(tài),父類型引用指向子類型對象
DriverManager.registerDriver(driver);

上面代碼可以合并為下面一行

DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());

注冊驅(qū)動這里還可以進(jìn)行代碼優(yōu)化涩搓,我們就在下面JDBC工具類污秆,DBUtils里面進(jìn)行

第二步:獲取連接

  • 表示JVM的進(jìn)程和數(shù)據(jù)庫進(jìn)程之間的通道打開了,這屬于進(jìn)程之間的通信昧甘,重量級的操作良拼,使用完之后一定要關(guān)閉通道

獲取連接的url需要遵循通信協(xié)議

  • 通信協(xié)議是通信之前就提前定好的數(shù)據(jù)傳送格式
  • 它包括數(shù)據(jù)包具體怎樣傳數(shù)據(jù),其格式是提前定好的

url案例

jdbc:mysql://127.0.0.1:3306/swu

swu是西南大學(xué)的英文簡寫充边,我自己設(shè)置的數(shù)據(jù)庫的名字

  • jdbc:mysql:// -------->協(xié)議
  • 127.0.0.1 ------------->IP地址
  • 3306/mysql----------->數(shù)據(jù)庫端口號
  • swu--------------------->具體的數(shù)據(jù)庫名
String url = "jdbc:mysql://192.168.151.9:3306/bjpowernode";
String user = "root";
String password = "981127";
String connection = DriverManager.getConnection(url,user,password);

注意

不同數(shù)據(jù)庫的通信的協(xié)議不同庸推,例如Oracle數(shù)據(jù)庫的通信協(xié)議如下:

jdbc:oracle:thin:@localhost:1521:數(shù)據(jù)庫名

第三步:獲取數(shù)據(jù)庫操作對象

  • 專門執(zhí)行sql語句的對象
Statement stmt = connection.createStatement();

一般較少使用statement執(zhí)行SQL語句常侦,因?yàn)橛蠸QL注入問題。

我們一般使用PrepareStatement對象贬媒,之后會談到

第四步:執(zhí)行SQL語句

JDBC中的sql語句不需要提供分號結(jié)尾

String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";
// 專門執(zhí)行DML語句的(insert delete update)
// 返回值是“影響數(shù)據(jù)庫中的記錄條數(shù)”
int count = stmt.executeUpdate(sql); 
System.out.println(count == 1 ? "保存成功" : "保存失敗");

第五步:處理查詢結(jié)果集

  • 只有當(dāng)?shù)谒牟綀?zhí)行的是select語句的時候聋亡,才要處理結(jié)果集

我們用ResultSet對象來接收執(zhí)行sql語句的返回值后,這個數(shù)據(jù)集是什么樣子呢际乘?

如下圖:

遍歷結(jié)果集.jpg

這里詳細(xì)談?wù)?em>ResultSet的nextLine()方法坡倔,getString()方法

nextLine()方法判斷下一行是否有數(shù)據(jù),它類似于一個浮標(biāo)的移動脖含,如果下一行有數(shù)據(jù)罪塔,則返回true,否則返回false

getString()方法:不管數(shù)據(jù)庫中的數(shù)據(jù)類型是什么养葵,都以String的形式取出征堪,同理:還有g(shù)etInt,getDouble等

當(dāng)然港柜,如果在()內(nèi)輸入字段名请契,則會以列的名字獲得該數(shù)據(jù),輸入單純的數(shù)字n夏醉,則取出第n列的數(shù)據(jù)

注意

  1. JDBC中所有下標(biāo)從1開始爽锥,不是從0開始。
  2. 列名稱不是表中的列名稱畔柔,是查詢結(jié)果集的列名稱

第六步:釋放資源

  • 使用完資源之后一定要關(guān)閉資源氯夷。
  • Java和數(shù)據(jù)庫屬于進(jìn)程間的通信,開啟之后一定要關(guān)閉靶擦。
  • 不關(guān)閉通道腮考,時間一長可能出現(xiàn)問題

為了保證資源一定釋放,在finally語句塊中關(guān)閉資源玄捕,并且要遵循從小到大依次關(guān)閉

try{
    if(stmt != null){
    stmt.close();
    }
}catch(SQLException e){
    e.printStackTrace();
}
try{
    if(conn != null){
        conn.close();
    }
}catch(SQLException e){
    e.printStackTrace();
}

JDBC工具類的寫法

我們通常創(chuàng)建一個類名為DBUtils的工具類來封裝JDBC中注冊驅(qū)動踩蔚,獲取連接,釋放資源部分的代碼枚粘,提高代碼復(fù)用性

import java.sql.*;
/**
 * JDBC工具類馅闽,簡化JDBC編程。
 */
public class DBUtil {
    /**
     * 工具類中的構(gòu)造方法都是私有的馍迄。
     * 因?yàn)楣ぞ哳惍?dāng)中的方法都是靜態(tài)的福也,不需要new對象,直接采用類名調(diào)用攀圈。
     */
    private DBUtil() {
    }
    // 靜態(tài)代碼塊在類加載時執(zhí)行暴凑,并且只執(zhí)行一次。
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    /**
     * 獲取數(shù)據(jù)庫連接對象
     *
     * @return 連接對象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/swu", "root", "password");
    }

    /**
     * 關(guān)閉資源
     * @param conn 連接對象
     * @param ps 數(shù)據(jù)庫操作對象
     * @param rs 結(jié)果集
     */
    public static void close(Connection conn, Statement ps, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(ps != null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

ResourceBundle綁定屬性配置文件

資源配置文件準(zhǔn)備

文件名:jdbc.properties

內(nèi)容:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://主機(jī)IP/數(shù)據(jù)庫名
user=數(shù)據(jù)庫用戶名
password=數(shù)據(jù)庫用戶密碼

綁定

使用ResourceBundle的getBundle方法赘来,傳遞參數(shù)為資源配置文件(.properties后綴文件)名字现喳,不包括后綴

ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凯傲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拿穴,更是在濱河造成了極大的恐慌泣洞,老刑警劉巖忧风,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件默色,死亡現(xiàn)場離奇詭異,居然都是意外死亡狮腿,警方通過查閱死者的電腦和手機(jī)腿宰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缘厢,“玉大人吃度,你說我怎么就攤上這事√颍” “怎么了椿每?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長英遭。 經(jīng)常有香客問我间护,道長,這世上最難降的妖魔是什么挖诸? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任汁尺,我火速辦了婚禮,結(jié)果婚禮上多律,老公的妹妹穿的比我還像新娘痴突。我一直安慰自己,他們只是感情好狼荞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布辽装。 她就那樣靜靜地躺著,像睡著了一般相味。 火紅的嫁衣襯著肌膚如雪拾积。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天攻走,我揣著相機(jī)與錄音殷勘,去河邊找鬼。 笑死昔搂,一個胖子當(dāng)著我的面吹牛玲销,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摘符,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贤斜,長吁一口氣:“原來是場噩夢啊……” “哼策吠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瘩绒,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤猴抹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锁荔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蟀给,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年阳堕,在試婚紗的時候發(fā)現(xiàn)自己被綠了跋理。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡恬总,死狀恐怖前普,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情壹堰,我是刑警寧澤拭卿,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站贱纠,受9級特大地震影響峻厚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜并巍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一目木、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧懊渡,春花似錦刽射、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肾档,卻和暖如春摹恰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怒见。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工俗慈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人遣耍。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓闺阱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舵变。 傳聞我的和親對象是個殘疾皇子酣溃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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