JDBC深入

前言

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

JDBC本質(zhì)

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

為什么面向接口編程

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

接口都有調(diào)用者和實現(xiàn)者,面向接口調(diào)用、面向接口寫實現(xiàn)類,這都屬于面向接口編程。

為什么要面向接口編程?

  • 解耦合:
    • 降低程序的耦合度
    • 提高程序的擴展力。

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

思考

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

  1. 因為每一個數(shù)據(jù)庫的底層實現(xiàn)原理都不一樣。
  2. Oracle數(shù)據(jù)庫有自己的原理。
  3. MySQL數(shù)據(jù)庫也有自己的原理。
  4. MS SqlServer數(shù)據(jù)庫也有自己的原理。

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

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

三方模擬JDBC本質(zhì)

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

SUN公司

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

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

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

MySQL

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

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

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

/*
    Oracle的數(shù)據(jù)庫廠家負責編寫JDBC接口的實現(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的實現(xiàn)類的jar包導入工程,也就是需要先把mysql驅(qū)動導入工程,不然接口的方法沒有實現(xiàn)類,程序自然無法執(zhí)行。

同理,java程序員在操作Oracle數(shù)據(jù)庫時,也需要先導入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)建對象可以通過反射機制。
        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ū)動這里還可以進行代碼優(yōu)化,我們就在下面JDBC工具類,DBUtils里面進行

第二步:獲取連接

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

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

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

url案例

jdbc:mysql://127.0.0.1:3306/swu

swu是西南大學的英文簡寫,我自己設置的數(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語句,因為有SQL注入問題。

我們一般使用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é)果集

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

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

如下圖:

遍歷結(jié)果集.jpg

這里詳細談談ResultSetnextLine()方法,getString()方法

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

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

當然,如果在()內(nèi)輸入字段名,則會以列的名字獲得該數(shù)據(jù),輸入單純的數(shù)字n,則取出第n列的數(shù)據(jù)

注意

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

第六步:釋放資源

  • 使用完資源之后一定要關(guān)閉資源。
  • Java和數(shù)據(jù)庫屬于進程間的通信,開啟之后一定要關(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ū)動,獲取連接,釋放資源部分的代碼,提高代碼復用性

import java.sql.*;
/**
 * JDBC工具類,簡化JDBC編程。
 */
public class DBUtil {
    /**
     * 工具類中的構(gòu)造方法都是私有的。
     * 因為工具類當中的方法都是靜態(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綁定屬性配置文件

資源配置文件準備

文件名:jdbc.properties

內(nèi)容:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://主機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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容