JDBC教程——檢視閱讀

驅(qū)動程序(Drivers)

JDBC驅(qū)動管理器(java.sql.DriverManager)是JDBC API中最重要的元素之一首昔。 它是用來處理一系列JDBC驅(qū)動程序的基本服務(wù)。它含有很多機制和對象糙俗,能夠?qū)ava應(yīng)用程序連接到所需的JDBC驅(qū)動程序勒奇。它負責(zé)管理這些不同類型的JDBC數(shù)據(jù)庫驅(qū)動程序∏缮В總結(jié)一下驅(qū)動管理器的主要功能就是:獲取當(dāng)前可用的驅(qū)動列表赊颠;處理特定的的驅(qū)動程序和數(shù)據(jù)庫之間的連接。

我們通過方法DriverManager.registerDriver()來注冊驅(qū)動程序:

new org.hsqldb.jdbc.JDBCDriver();
DriverManager.registerDriver( new org.hsqldb.jdbc.JDBCDriver() );

我們也可以調(diào)用方法Class.forName() 加載驅(qū)動程序:

// Loading the HSQLDB JDBC driver
Class.forName( "org.hsqldb.jdbc.JDBCDriver" );

// connection to JDBC using mysql driver
Class.forName( "com.mysql.jdbc.Driver" );

這二者的主要區(qū)別是:前者方法registerDerver()需要保證驅(qū)動程序在編譯時就是可用的劈彪;后者加載驅(qū)動程序類文件的方式竣蹦,不需要驅(qū)動程序在編譯時是可用的。JDBC 4版本后沧奴,實際上沒有必要調(diào)用這些方法痘括,應(yīng)用程序不需要單獨注冊這些驅(qū)動,也不需要加載驅(qū)動類。我們也不推薦使用方法registerDriver()來手動加載驅(qū)動程序纲菌。

JDBC 指南

JDBC 簡介

什么是 JDBC挠日?

JDBC (Java Database Connectivity )指 Java 數(shù)據(jù)庫連接,是一種標準Java應(yīng)用編程接口( JAVA API)翰舌,用來連接 Java 編程語言和廣泛的數(shù)據(jù)庫嚣潜。

JDBC API 庫包含下面提到的每個任務(wù),都是與數(shù)據(jù)庫相關(guān)的常用用法椅贱。

  • 制作到數(shù)據(jù)庫的連接懂算。
  • 創(chuàng)建 SQL 或 MySQL 語句。
  • 執(zhí)行 SQL 或 MySQL 查詢數(shù)據(jù)庫庇麦。
  • 查看和修改所產(chǎn)生的記錄计技。

從根本上來說,JDBC 是一種規(guī)范女器,它提供了一套完整的接口酸役,允許便攜式訪問到底層數(shù)據(jù)庫住诸,因此可以用 Java 編寫不同類型的可執(zhí)行文件.

JDBC 架構(gòu)

JDBC 的 API 支持兩層和三層處理模式進行數(shù)據(jù)庫訪問驾胆,但一般的 JDBC 架構(gòu)由兩層處理模式組成:

  • JDBC API: 提供了應(yīng)用程序?qū)?JDBC 管理器的連接(crud數(shù)據(jù)庫操作)。
  • JDBC Driver API: 提供了 JDBC 管理器對驅(qū)動程序連接(驅(qū)動連接)贱呐。

JDBC API 使用驅(qū)動程序管理器和數(shù)據(jù)庫特定的驅(qū)動程序來提供各種各樣的 (heterogeneous)數(shù)據(jù)庫的透明連接丧诺。

JDBC 驅(qū)動程序管理器可確保正確的驅(qū)動程序來訪問每個數(shù)據(jù)源。該驅(qū)動程序管理器能夠支持連接到多個各種各樣的數(shù)據(jù)庫的多個并發(fā)的驅(qū)動程序奄薇。

以下是結(jié)構(gòu)圖驳阎,其中顯示了驅(qū)動程序管理器相對于在 JDBC 驅(qū)動程序和 Java 應(yīng)用程序所處的位置。

image.png

常見的 JDBC 組件

JDBC 的 API 提供了以下接口和類:

DriverManager :這個類管理一系列數(shù)據(jù)庫驅(qū)動程序馁蒂。匹配連接使用通信子協(xié)議從 JAVA 應(yīng)用程序中請求合適的數(shù)據(jù)庫驅(qū)動程序呵晚。識別 JDBC 下某個子協(xié)議的第一驅(qū)動程序?qū)⒈挥糜?strong>建立數(shù)據(jù)庫連接。

**Driver **: 這個接口處理與數(shù)據(jù)庫服務(wù)器的通信沫屡。你將很少直接與驅(qū)動程序互動饵隙。相反,你使用 DriverManager 中的對象沮脖,它管理此類型的對象金矛。它也抽象與驅(qū)動程序?qū)ο蠊ぷ飨嚓P(guān)的詳細信息。

Connection : 此接口具有接觸數(shù)據(jù)庫的所有方法勺届。該連接對象表示通信上下文驶俊,即,所有與數(shù)據(jù)庫的通信僅通過這個連接對象進行免姿。

Statement : 使用創(chuàng)建于這個接口的對象將 SQL 語句提交到數(shù)據(jù)庫饼酿。除了執(zhí)行存儲過程以外,一些派生的接口也接受參數(shù)胚膊。

ResultSet : 在你使用語句對象執(zhí)行 SQL 查詢后故俐,這些對象保存從數(shù)據(jù)獲得的數(shù)據(jù)奈应。它作為一個迭代器,讓您可以通過它的數(shù)據(jù)來移動购披。

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

實例:

public class JDBCTest {

    private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";

    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/hello_mybatis";

    private static final String USER = "root";

    private static final String PASSWORD = "123456";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //STEP 2: Register JDBC driver
        Class.forName(JDBC_DRIVER);
        ResultSet rs = null;
        Connection conn = null;
        Statement stmt = null;
        try {
            //STEP 3: Open a connection
            conn = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);

            //STEP 4: Execute a query
            stmt = conn.createStatement();
            String sql;
            sql = "SELECT id, name, dept, phone FROM t_user";
            rs = stmt.executeQuery(sql);

            //STEP 5: Extract data from result set
            //調(diào)用rs.next()時,執(zhí)行this.thisRow = this.rowData.next();
            //這時候返回值會把當(dāng)前行數(shù)據(jù)指向游標的下一行數(shù)據(jù)
            while (rs.next()) {
                //Retrieve by column name 通過列名獲取值
            /*    int id = rs.getInt("id");
                String name = rs.getString("name");
                String dept = rs.getString("dept");
                String phone = rs.getString("phone");*/

                //Retrieve by columnIndex 通過列的下標獲取值
                int id = rs.getInt(1);
                String name = rs.getString(2);
                String dept = rs.getString(3);
                String phone = rs.getString(4);
                //Display values
                System.out.println("id: " + id + "name: " + name + "dept: " + dept + "phone: " + phone);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            rs.close();
            stmt.close();
            conn.close();
        }
        System.out.println("over !");
    }
}

輸出:

id: 1name: 大青山dept: amy empirephone: 18956563228
id: 2name: 艾米哈珀dept: amy empirephone: 18956563228
id: 3name: 池寒楓dept: amy empirephone: 22056545
id: 4name: 霍恩斯dept: 森林矮人王國phone: 852-253521
over !

報錯:

1刚陡、java.sql.SQLException: Column Index out of range, 0 < 1.

這是因為ResultSet結(jié)果集是獲取列是從1開始的惩妇。

 int id = rs.getInt(0);//java.sql.SQLException: Column Index out of range, 0 < 1. 
 //正確操作如下:
 int id = rs.getInt(1);

JDBC 驅(qū)動類型

什么是 JDBC 驅(qū)動程序?

JDBC 驅(qū)動實現(xiàn)了 JDBC API 中定義的接口筐乳,該接口用于與數(shù)據(jù)庫服務(wù)器進行交互歌殃。

例如,使用 JDBC 驅(qū)動程序可以讓你打開數(shù)據(jù)庫連接蝙云,并通過發(fā)送 SQL 或數(shù)據(jù)庫命令氓皱,然后通過 Java 接收結(jié)果。

java.sql 包中附帶的 JDK勃刨,包含了定義各種類與他們的行為和實際實現(xiàn)波材,這些類都在第三方驅(qū)動程序中完成。第三方供應(yīng)商在他們的數(shù)據(jù)庫驅(qū)動程序中都實現(xiàn)了 java.sql.Driver 接口身隐。

JDBC 驅(qū)動程序類型

JDBC 驅(qū)動程序的實現(xiàn)廷区,因為各種各樣的操作系統(tǒng)和 Java 運行在硬件平臺的不同而不同。Sun 公司將實現(xiàn)類型分為四類:類型1贾铝,2隙轻,3,4垢揩,其解釋如下.

類型1:JDBC-ODBC 橋驅(qū)動程序:

在類型1驅(qū)動程序中玖绿,一個 JDBC 橋接器是用來訪問安裝在每個客戶機上的 ODBC 驅(qū)動程序。為了使用 ODBC叁巨,需要在目標數(shù)據(jù)庫上配置系統(tǒng)數(shù)據(jù)源名稱(DSN)斑匪。

當(dāng) Java 剛出來時,這是一個很有用的驅(qū)動程序俘种,因為大多數(shù)的數(shù)據(jù)庫只支持 ODBC 訪問秤标,但現(xiàn)在此類型的驅(qū)動程序僅適用于實驗用途或在沒有其他選擇的情況。

img

自帶 JDK 1.2 中的 JDBC-ODBC 橋是這類驅(qū)動程序的一個很好的例子宙刘。

類型2:JDBC-Native API

在類型2驅(qū)動程序中苍姜,JDBC API 調(diào)用轉(zhuǎn)換成原生的 C/C++ API 調(diào)用,這對于數(shù)據(jù)庫來說具有唯一性悬包。這些驅(qū)動程序通常由數(shù)據(jù)庫供應(yīng)商提供衙猪,并和 JDBC-ODBC 橋驅(qū)動程序同樣的方式使用。該供應(yīng)商的驅(qū)動程序必須安裝在每臺客戶機上。

如果我們改變了當(dāng)前數(shù)據(jù)庫垫释,我們必須改變原生 API 丝格,因為它是具體到某一個數(shù)據(jù)庫,并且他們大多已經(jīng)失效了棵譬。即使這樣用類型2驅(qū)動程序也能提高一些速度显蝌,因為他消除了 ODBC 的開銷。

img

Oracle 調(diào)用接口(OCI)驅(qū)動程序是一個類型2驅(qū)動程序的示例订咸。

類型3:JDBC-Net 純 Java

在類型3驅(qū)動程序中曼尊,一般用三層方法來訪問數(shù)據(jù)庫。JDBC 客戶端使用標準的網(wǎng)絡(luò)套接字與中間件應(yīng)用服務(wù)器進行通信脏嚷。套接字的相關(guān)信息被中間件應(yīng)用服務(wù)器轉(zhuǎn)換為數(shù)據(jù)庫管理系統(tǒng)所要求的的調(diào)用格式骆撇,并轉(zhuǎn)發(fā)到數(shù)據(jù)庫服務(wù)器。

這種驅(qū)動程序是非常靈活的父叙,因為它不需要在客戶端上安裝代碼神郊,而且單個驅(qū)動程序能提供訪問多個數(shù)據(jù)庫。

img

你可以將應(yīng)用服務(wù)器作為一個 JDBC “代理”趾唱,這意味著它會調(diào)用客戶端應(yīng)用程序涌乳。因此,你需要一些有關(guān)服務(wù)器配置方面的知識鲸匿,這樣可以高效地使用此驅(qū)動程序類型爷怀。

你的應(yīng)用服務(wù)器可能使用類型1阻肩,2带欢,或4驅(qū)動程序與數(shù)據(jù)庫進行通信,了解它們的細微之處將會很有幫助烤惊。

類型4:100%純 Java

在類型4驅(qū)動程序中乔煞,一個純粹的基于 Java 的驅(qū)動程序通過 socket 連接與供應(yīng)商的數(shù)據(jù)庫進行通信。這是可用于數(shù)據(jù)庫的最高性能的驅(qū)動程序柒室,并且通常由供應(yīng)商自身提供渡贾。

這種驅(qū)動器是非常靈活的,你不需要在客戶端或服務(wù)端上安裝特殊的軟件雄右。此外空骚,這些驅(qū)動程序是可以動態(tài)下載的。

img

MySQL Connector/J 的驅(qū)動程序是一個類型4驅(qū)動程序擂仍。因為它們的網(wǎng)絡(luò)協(xié)議的專有屬性囤屹,數(shù)據(jù)庫供應(yīng)商通常提供類型4的驅(qū)動程序。

該使用哪種驅(qū)動程序逢渔?

如果你正在訪問一個數(shù)據(jù)庫肋坚,如 Oracle,Sybase 或 IBM,首選的驅(qū)動程序是類型4智厌。

如果你的 Java 應(yīng)用程序同時訪問多個數(shù)據(jù)庫類型诲泌,類型3是首選的驅(qū)動程序。

類型2驅(qū)動程序是在你的數(shù)據(jù)庫沒有提供類型3或類型4驅(qū)動程序時使用的铣鹏。

類型1驅(qū)動程序不被認為是部署級的驅(qū)動程序敷扫,它存在的目的通常僅用于開發(fā)和測試。

JDBC 連接數(shù)據(jù)庫

連接數(shù)據(jù)庫

在你安裝相應(yīng)的驅(qū)動程序后诚卸,就可以用 JDBC 建立一個數(shù)據(jù)庫連接呻澜。

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.31</version>
    </dependency>

編寫建立一個 JDBC 連接的程序是相當(dāng)簡單的。下面是簡單的四個步驟:

  • 導(dǎo)入 JDBC 包:在你的 Java 代碼中惨险,用 import 語句添加你所需的類羹幸。
  • 注冊 JDBC 驅(qū)動程序:這一步會導(dǎo)致 JVM 加載所需的驅(qū)動程序到內(nèi)存中執(zhí)行,因此它可以實現(xiàn)你的 JDBC 請求辫愉。
  • 數(shù)據(jù)庫 URL 制定:這是用來創(chuàng)建格式正確的地址指向你想要連接的數(shù)據(jù)庫栅受。
  • 創(chuàng)建連接對象:最后,代碼調(diào)用 DriverManager 對象的 getConnection() 方法來建立實際的數(shù)據(jù)庫連接恭朗。
導(dǎo)入包:
image.png
注冊 JDBC 驅(qū)動程序
//方法1 - Class.forName()
//這種方法更優(yōu)越一些屏镊,因為它允許你對驅(qū)動程序的注冊信息進行配置,便于移植。
 Class.forName("oracle.jdbc.driver.OracleDriver");//推薦
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();

//方法2 - DriverManager.registerDriver()
//如果你使用的是不兼容 JVM 的非 JDK,比如微軟提供的比然,你必須使用 registerDriver() 方法钠右。
 Driver myDriver = new oracle.jdbc.driver.OracleDriver();
 DriverManager.registerDriver( myDriver );

數(shù)據(jù)庫 URL 制定

當(dāng)你加載了驅(qū)動程序之后,你可以通過 DriverManager.getConnection() 方法建立一個連接锦秒。為方便參考,以下列出了三個加載 DriverManager.getConnection() 方法:

  • getConnection(String url)
  • getConnection(String url, Properties prop)
  • getConnection(String url, String user, String password)

下表列出了常用的 JDBC 驅(qū)動程序名和數(shù)據(jù)庫URL。

RDBMS JDBC 驅(qū)動程序名稱 URL 格式
MySQL com.mysql.jdbc.Driver jdbc:mysql://hostname/ databaseName
ORACLE oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@hostname:port Number:databaseName
DB2 COM.ibm.db2.jdbc.net.DB2Driver jdbc:db2:hostname:port Number/databaseName
Sybase com.sybase.jdbc.SybDriver jdbc:sybase:Tds:hostname: port Number/databaseName

URL 格式所有加粗的部分都是靜態(tài)的歌逢,你需要將剩余部分按照你的數(shù)據(jù)庫實際情況進行設(shè)置。

 //使用數(shù)據(jù)庫 URL 和 Properties 對象
 Properties info = new Properties();
 info.put("user", USER);
 info.put("password", PASSWORD);
 conn = DriverManager.getConnection(DATABASE_URL, info);
關(guān)閉 JDBC 連接
//Java 垃圾收集器也會關(guān)閉連接翘狱,它會完全清除過期的對象秘案。但最好用try finally來關(guān)閉連接。
conn.close();

JDBC Statement 對象

Statement 對象

一旦我們獲得了數(shù)據(jù)庫的連接潦匈,我們就可以和數(shù)據(jù)庫進行交互阱高。JDBC 的 Statement,CallableStatement 和 PreparedStatement 接口定義的方法和屬性茬缩,可以讓你發(fā)送 SQL 命令或 PL/SQL 命令到數(shù)據(jù)庫赤惊,并從你的數(shù)據(jù)庫接收數(shù)據(jù)。

下表提供了每個接口的用途概要寒屯,根據(jù)實際目的決定使用哪個接口荐捻。

StatementImpl stmt = new StatementImpl(this.getLoadBalanceSafeProxy(), this.database);
接口 推薦使用
Statement 可以正常訪問數(shù)據(jù)庫黍少,適用于運行靜態(tài) SQL 語句。 Statement 接口不接受參數(shù)处面。
PreparedStatement 計劃多次使用 SQL 語句厂置, PreparedStatement 接口運行時接受輸入的參數(shù)。
CallableStatement 適用于當(dāng)你要訪問數(shù)據(jù)庫存儲過程的時候魂角, CallableStatement 接口運行時也接受輸入的參數(shù)昵济。

當(dāng)你創(chuàng)建了一個 Statement 對象之后,你可以用它的三個執(zhí)行方法的任一方法來執(zhí)行 SQL 語句野揪。

  • boolean execute(String SQL) : 如果 ResultSet 對象可以被檢索访忿,則返回的布爾值為 true ,否則返回 false 斯稳。當(dāng)你需要使用真正的動態(tài) SQL 時海铆,可以使用這個方法來執(zhí)行 SQL DDL 語句
  • int executeUpdate(String SQL) : 返回執(zhí)行 SQL 語句影響的行的數(shù)目挣惰。使用該方法來執(zhí)行 SQL 語句卧斟,是希望得到一些受影響的行的數(shù)目,例如憎茂,INSERT珍语,UPDATE 或 DELETE 語句。
  • ResultSet executeQuery(String SQL) : 返回一個 ResultSet 對象竖幔。當(dāng)你希望得到一個結(jié)果集時使用該方法板乙,就像你使用一個 SELECT 語句。
關(guān)閉 Statement 對象

正如你關(guān)閉一個 Connection 對象來節(jié)約數(shù)據(jù)庫資源拳氢,出于同樣的原因你也應(yīng)該關(guān)閉 Statement 對象募逞。

簡單的調(diào)用 close() 方法就可以完成這項工作。如果你關(guān)閉了 Connection 對象饿幅,那么它也會關(guān)閉 Statement 對象凡辱。然而,你應(yīng)該始終明確關(guān)閉 Statement 對象栗恩,以確保真正的清除。

PreparedStatement 對象

PreparedStatement 接口擴展了 Statement 接口洪燥,它讓你用一個常用的 Statement 對象增加幾個高級功能磕秤。

JDBC 中所有的參數(shù)都被用 ? 符號表示,這是已知的參數(shù)標記捧韵。在執(zhí)行 SQL 語句之前市咆,你必須賦予每一個參數(shù)確切的數(shù)值。

setXXX() 方法將值綁定到參數(shù)再来,其中 XXX 表示你希望綁定到輸入?yún)?shù)的 Java 數(shù)據(jù)類型蒙兰。如果你忘了賦予值磷瘤,你將收到一個 SQLException。

每個參數(shù)標記映射它的序號位置搜变。第一標記表示位置 1 采缚,下一個位置為 2 .

prepareStatement對象防止sql注入的方式是把用戶非法輸入的單引號用\反斜杠做了轉(zhuǎn)義,從而達到了防止sql注入的目的 挠他。

實例:

public class JDBCTest2 {

    private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";

    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/hello_mybatis";

    private static final String USER = "root";

    private static final String PASSWORD = "123456";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //STEP 2: Register JDBC driver
        Class.forName(JDBC_DRIVER);
        ResultSet rs = null;
        Connection conn = null;
        PreparedStatement pstmt = null;
        PreparedStatement pstmtUp = null;
        try {
            //STEP 3: Open a connection
            //conn = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);

            //使用數(shù)據(jù)庫 URL 和 Properties 對象
            Properties info = new Properties();
            info.put("user", USER);
            info.put("password", PASSWORD);
            conn = DriverManager.getConnection(DATABASE_URL, info);

            pstmtUp = conn.prepareStatement("UPDATE t_user SET phone = ? WHERE id = ?");
            pstmtUp.setString(1,"13565422554");
            pstmtUp.setInt(2,2);
            int execute = pstmtUp.executeUpdate();
            System.out.println("execute update :"+execute);
            //STEP 4: Execute a query
            pstmt = conn.prepareStatement("SELECT id, name, dept, phone FROM t_user WHERE id = ? and name = ?");
            pstmt.setInt(1,2);
            pstmt.setString(2,"艾米哈珀");
            rs = pstmt.executeQuery();
            while (rs.next()) {
                //Retrieve by column name 通過列名獲取值
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String dept = rs.getString("dept");
                String phone = rs.getString("phone");
                //Display values
                System.out.println("id: " + id + "name: " + name + "dept: " + dept + "phone: " + phone);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            rs.close();
            //關(guān)閉 PreparedStatement 對象
            pstmt.close();
            pstmtUp.close();
            conn.close();
        }
        System.out.println("over !");
    }
}

輸出:

execute update :1
id: 2name: 艾米哈珀dept: amy empirephone: 13565422554
over !

CallableStatement 對象

CallableStatement是創(chuàng)建被用來執(zhí)行調(diào)用數(shù)據(jù)庫存儲過程的 CallableStatement 對象扳抽。

三種類型的參數(shù)有:IN,OUT 和 INOUT殖侵。PreparedStatement 對象只使用 IN 參數(shù)贸呢。CallableStatement 對象可以使用所有的三個參數(shù)。

這里是每個參數(shù)的定義:

參數(shù) 描述
IN 在 SQL 語句創(chuàng)建的時候該參數(shù)是未知的拢军。你可以用 setXXX() 方法將值綁定到IN參數(shù)中楞陷。
OUT 該參數(shù)由 SQL 語句的返回值提供。你可以用 getXXX() 方法獲取 OUT 參數(shù)的值茉唉。
INOUT 該參數(shù)同時提供輸入輸出的值。你可以用 setXXX() 方法將值綁定參數(shù)赌渣,并且用 getXXX() 方法獲取值魏铅。

前提:

存儲過程

DELIMITER $$

DROP PROCEDURE IF EXISTS `hello_mybatis`.`getNameById` $$
CREATE PROCEDURE `hello_mybatis`.`getNameById` 
   (IN USER_ID INT, OUT USER_NAME varchar(64))
BEGIN
   SELECT NAME INTO USER_NAME
   FROM t_user
   WHERE ID = USER_ID;
END $$

DELIMITER ;

示例:

public class JDBCTest3 {

    private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";

    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/hello_mybatis";

    private static final String USER = "root";

    private static final String PASSWORD = "123456";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //STEP 2: Register JDBC driver
        Class.forName(JDBC_DRIVER);
        Connection conn = null;
        CallableStatement cstmt= null;
        try {
            //STEP 3: Open a connection
            //conn = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);

            //使用數(shù)據(jù)庫 URL 和 Properties 對象
            Properties info = new Properties();
            info.put("user", USER);
            info.put("password", PASSWORD);
            conn = DriverManager.getConnection(DATABASE_URL, info);
            //注意,不管是入?yún)⒊鰠⒍加杏屑嵛撸空嘉环婪迹駝t會執(zhí)行失敗
            String sql ="{call getNameById (?,?)}";
            cstmt = conn.prepareCall(sql);
            cstmt.setInt("USER_ID",2);
            cstmt.registerOutParameter("USER_NAME",Types.VARCHAR);
            //也可以根據(jù)列的索引,1鸿竖,2這樣進行設(shè)置參數(shù)
            cstmt.execute();
            String userName = cstmt.getString("USER_NAME");
            System.out.println("CallableStatement userName: "+userName);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            cstmt.close();
            conn.close();
        }
        System.out.println("over !");
    }
}

輸出:

CallableStatement userName: 艾米哈珀
over !

報錯:

java.sql.SQLException: Can't find local placeholder mapping for parameter named "USER_NAME".

這是因為執(zhí)行存儲過程時沧竟,不管是入?yún)⒊鰠⒍加杏校空嘉环坑牵駝t會執(zhí)行失敗

//錯誤
 String sql ="{call getNameById (?)}";
//正確
String sql ="{call getNameById (?,?)}";

CallableStatement參考

JDBC 結(jié)果集

SQL 語句從數(shù)據(jù)庫查詢中獲取數(shù)據(jù)悟泵,并將數(shù)據(jù)返回到結(jié)果集中。SELECT 語句是一種標準的方法闪水,它從一個數(shù)據(jù)庫中選擇行記錄糕非,并顯示在一個結(jié)果集中。 java.sql.ResultSet 接口表示一個數(shù)據(jù)庫查詢的結(jié)果集球榆。

一個 ResultSet 對象控制一個光標指向當(dāng)前行的結(jié)果集朽肥。術(shù)語“結(jié)果集”是指包含在 ResultSet 對象中的行和列的數(shù)據(jù)。

ResultSet 接口的方法可細分為三類-

  • 導(dǎo)航方法:用于移動光標持钉。
  • 獲取方法:用于查看當(dāng)前行被光標所指向的列中的數(shù)據(jù)衡招。
  • 更新方法:用于更新當(dāng)前行的列中的數(shù)據(jù)。這些更新也會更新數(shù)據(jù)庫中的數(shù)據(jù)每强。

光標的移動基于 ResultSet 的屬性始腾。用相應(yīng)的語句生成 ResultSet 對象時州刽,同時生成 ResultSet 的屬性。

JDBC 提供了連接方法通過下列創(chuàng)建語句來生成你所需的 ResultSet 對象:

  • createStatement(int RSType, int RSConcurrency);
  • prepareStatement(String SQL, int RSType, int RSConcurrency);
  • prepareCall(String sql, int RSType, int RSConcurrency);

第一個參數(shù)表示 ResultSet 對象的類型浪箭,第二個參數(shù)是兩個 ResultSet 常量之一穗椅,該常量用于判斷該結(jié)果集是只讀的還是可修改的。

ResultSet 的類型

可能的 RSType 如下所示山林。如果你不指定 ResultSet 類型房待,將自動獲得的值是 TYPE_FORWARD_ONLY。

類型 描述
ResultSet.TYPE_FORWARD_ONLY 光標只能在結(jié)果集中向前移動驼抹。
ResultSet.TYPE_SCROLL_INSENSITIVE 光標可以向前和向后移動桑孩。當(dāng)結(jié)果集創(chuàng)建后,其他人對數(shù)據(jù)庫的操作不會影響結(jié)果集的數(shù)據(jù)框冀。
ResultSet.TYPE_SCROLL_SENSITIVE. 光標可以向前和向后移動流椒。當(dāng)結(jié)果集創(chuàng)建后,其他人對數(shù)據(jù)庫的操作會影響結(jié)果集的數(shù)據(jù)明也。

ResultSet 的并發(fā)性

RSConcurrency 的值如下所示宣虾,如果你不指定并發(fā)類型,將自動獲得的值是 CONCUR_READ_ONLY温数。

并發(fā)性 描述
ResultSet.CONCUR_READ_ONLY 創(chuàng)建一個只讀結(jié)果集绣硝,這是默認的值。
ResultSet.CONCUR_UPDATABLE 創(chuàng)建一個可修改的結(jié)果集撑刺。
//默認初始化一個 Statement 對象來創(chuàng)建一個只能前進鹉胖,而且只讀的 ResultSet 對象。
conn.createStatement();

在 ResultSet 接口中包括如下幾種方法涉及移動光標:

S.N. 方法 & 描述
1 public void beforeFirst() throws SQLException將光標移動到第一行之前够傍。
2 public void afterLast() throws SQLException將光標移動到最后一行之后甫菠。
3 public boolean first() throws SQLException將光標移動到第一行。
4 public void last() throws SQLException將光標移動到最后一行冕屯。
5 public boolean absolute(int row) throws SQLException將光標移動到指定的第 row 行寂诱。
6 public boolean relative(int row) throws SQLException將光標移動到當(dāng)前指向的位置往前或往后第 row 行的位置。
7 public boolean previous() throws SQLException將光標移動到上一行安聘,如果超過結(jié)果集的范圍則返回 false痰洒。
8 public boolean next() throws SQLException將光標移動到下一行,如果是結(jié)果集的最后一行則返回 false搞挣。
9 public int getRow() throws SQLException返回當(dāng)前光標指向的行數(shù)的值带迟。
10 public void moveToInsertRow() throws SQLException將光標移動到結(jié)果集中指定的行,可以在數(shù)據(jù)庫中插入新的一行囱桨。當(dāng)前光標位置將被記住。
11 public void moveToCurrentRow() throws SQLException如果光標處于插入行嗅绰,則將光標返回到當(dāng)前行舍肠,其他情況下搀继,這個方法不執(zhí)行任何操作。

查看結(jié)果集

ResultSet接口中含有幾十種從當(dāng)前行獲取數(shù)據(jù)的方法翠语。

每個可能的數(shù)據(jù)類型都有一個 get 方法叽躯,并且每個 get 方法有兩個版本-

  • 一個需要列名。
  • 一個需要列的索引肌括。

例如点骑,如果你想查看的列包含一個 int 類型,你需要在 ResultSet 中調(diào)用 getInt()方法-

S.N. 方法 & 描述
1 public int getInt(String columnName) throws SQLException返回當(dāng)前行中名為 columnName 的列的 int 值谍夭。
2 public int getInt(int columnIndex) throws SQLException返回當(dāng)前行中指定列的索引的 int 值黑滴。列索引從 1 開始,意味著行中的第一列是 1 紧索,第二列是 2 袁辈,以此類推。

同樣的珠漂,在 ResultSet 接口中還有獲取八個 Java 原始類型的 get 方法晚缩,以及常見的類型。

更新的結(jié)果集(一般不這樣更新媳危,了解)

ResultSet 接口包含了一系列的更新方法荞彼,該方法用于更新結(jié)果集中的數(shù)據(jù)。

updateString 方法可以有兩個更新方法來更新任一數(shù)據(jù)類型-

  • 一個需要列名待笑。
  • 一個需要列的索引鸣皂。

例如,要更新一個結(jié)果集的當(dāng)前行的 String 列滋觉,你可以使用任一如下所示的 updateString()方法-

S.N. 方法 & 描述
1 public void updateString(int columnIndex, String s) throws SQLException將指定列的字符串的值改為 s签夭。
2 public void updateString(String columnName, String s) throws SQLException類似于前面的方法,不同之處在于指定的列是用名字來指定的椎侠,而不是它的索引第租。

八個原始數(shù)據(jù)類型都有其更新方法,比如 String我纪,Object慎宾,URL,和在 java.sql 包中的 SQL 數(shù)據(jù)類型浅悉。

更新結(jié)果集中的行將改變當(dāng)前行的列中的 ResultSet 對象趟据,而不是基礎(chǔ)數(shù)據(jù)庫中的數(shù)據(jù)。要更新數(shù)據(jù)庫中一行的數(shù)據(jù)术健,你需要調(diào)用以下的任一方法汹碱。

S.N. 方法 & 描述
1 public void updateRow()通過更新數(shù)據(jù)庫中相對應(yīng)的行來更新當(dāng)前行。
2 public void deleteRow()從數(shù)據(jù)庫中刪除當(dāng)前行荞估。
3 public void refreshRow()在結(jié)果集中刷新數(shù)據(jù)咳促,以反映數(shù)據(jù)庫中最新的數(shù)據(jù)變化稚新。
4 public void cancelRowUpdates()取消對當(dāng)前行的任何修改。
5 public void insertRow()在數(shù)據(jù)庫中插入一行跪腹。本方法只有在光標指向插入行的時候才能被調(diào)用褂删。

當(dāng)使用ResultSet的時候,當(dāng)查詢出來的數(shù)據(jù)集記錄很多冲茸,有一千萬條的時候屯阀,那rs所指的對象是否會占用很多內(nèi)存,如果記錄過多轴术,那程序會不會把系統(tǒng)的內(nèi)存用光呢 ?

不會难衰,ResultSet表面看起來是一個記錄集,其實這個對象中只是記錄了結(jié)果集的相關(guān)信息膳音,具體的記錄并沒有存放在對象中召衔,具體的記錄內(nèi)容直到你通過next方法提取的時候,再通過相關(guān)的getXXXXX方法提取字段內(nèi)容的時候才能從數(shù)據(jù)庫中得到祭陷,這些并不會占用內(nèi)存苍凛,具體消耗內(nèi)存是由于你將記錄集中的數(shù)據(jù)提取出來加入到你自己的集合中的時候才會發(fā)生,如果你沒有使用集合記錄所有的記錄就不會發(fā)生消耗內(nèi)存厲害的情況兵志。

JDBC 數(shù)據(jù)類型

JDBC 驅(qū)動程序在將 Java 數(shù)據(jù)類型發(fā)送到數(shù)據(jù)庫之前醇蝴,會將其轉(zhuǎn)換為相應(yīng)的 JDBC 類型。對于大多數(shù)數(shù)據(jù)類型都采用了默認的映射關(guān)系想罕。例如悠栓,一個 Java int 數(shù)據(jù)類型轉(zhuǎn)換為 SQL INTEGER。通過默認的映射關(guān)系來提供驅(qū)動程序之間的一致性按价。

ResultSet 對象為任一數(shù)據(jù)類型提供相應(yīng)的 getXXX()方法惭适,該方法可以獲取任一數(shù)據(jù)類型的列值。上述任一方法的使用需要列名或它的順序位置楼镐。

SQL JDBC/Java setXXX getXXX
VARCHAR java.lang.String setString getString
CHAR java.lang.String setString getString
LONGVARCHAR java.lang.String setString getString
BIT boolean setBoolean getBoolean
NUMERIC java.math.BigDecimal setBigDecimal getBigDecimal
TINYINT byte setByte getByte
SMALLINT short setShort getShort
INTEGER int setInt getInt
BIGINT long setLong getLong
REAL float setFloat getFloat
FLOAT float setFloat getFloat
DOUBLE double setDouble getDouble
VARBINARY byte[ ] setBytes getBytes
BINARY byte[ ] setBytes getBytes
DATE java.sql.Date setDate getDate
TIME java.sql.Time setTime getTime
TIMESTAMP java.sql.Timestamp setTimestamp getTimestamp
CLOB java.sql.Clob setClob getClob
BLOB java.sql.Blob setBlob getBlob
ARRAY java.sql.Array setARRAY getARRAY
REF java.sql.Ref SetRef getRef
STRUCT java.sql.Struct SetStruct getStruct

處理 NULL 值

SQL 使用 NULL 值和 Java 使用 null 是不同的概念癞志。那么,你可以使用三種策略來處理 Java 中的 SQL NULL 值-

  • 避免使用返回原始數(shù)據(jù)類型的 getXXX()方法框产。
  • 使用包裝類的基本數(shù)據(jù)類型凄杯,并使用 ResultSet 對象的 wasNull()方法來測試收到 getXXX()方法返回的值是否為 null,如果是 null秉宿,該包裝類變量則被設(shè)置為 null戒突。
  • 使用原始數(shù)據(jù)類型和 ResultSet 對象的 wasNull()方法來測試通過 getXXX()方法返回的值,如果是 null描睦,則原始變量應(yīng)設(shè)置為可接受的值來代表 NULL膊存。

JDBC 事務(wù)

如果你的 JDBC 連接是處于自動提交模式下,該模式為默認模式,那么每句 SQL 語句都是在其完成時提交到數(shù)據(jù)庫膝舅。

對簡單的應(yīng)用程序來說這種模式相當(dāng)好嗡载,但有三個原因你可能想關(guān)閉自動提交模式窑多,并管理你自己的事務(wù)-

  • 為了提高性能
  • 為了保持業(yè)務(wù)流程的完整性
  • 使用分布式事務(wù)

JDBC 驅(qū)動程序默認使用的自動提交模式 仍稀,需要conn.setAutoCommit(false);關(guān)閉自動提交模式 .

提交和回滾
conn.commit( );
conn.rollback( );
還原點

Connection 對象有兩個新的方法來管理還原點-

  • setSavepoint(String savepointName): 定義了一個新的還原點。它也返回一個 Savepoint 對象埂息。
  • releaseSavepoint(Savepoint savepointName): 刪除一個還原點技潘。請注意,它需要一個作為參數(shù)的 Savepoint 對象千康。這個對象通常是由 setSavepoint() 方法生成的一個還原點享幽。

有一個 rollback (String savepointName) 方法,該方法可以回滾到指定的還原點拾弃。

示例:

public class JDBCTranctionTest {

    private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";

    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/hello_mybatis";

    private static final String USER = "root";

    private static final String PASSWORD = "123456";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //STEP 2: Register JDBC driver
        Class.forName(JDBC_DRIVER);
        ResultSet rs = null;
        Connection conn = null;
        PreparedStatement pstmt = null;
        PreparedStatement pstmtUp = null;
        Savepoint savepoint = null;
        try {
            //STEP 3: Open a connection
            //conn = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);

            //使用數(shù)據(jù)庫 URL 和 Properties 對象
            Properties info = new Properties();
            info.put("user", USER);
            info.put("password", PASSWORD);
            conn = DriverManager.getConnection(DATABASE_URL, info);
            //關(guān)閉自動提交模式
            conn.setAutoCommit(false);
            pstmtUp = conn.prepareStatement("UPDATE t_user SET phone = ? WHERE id = ?");
            pstmtUp.setString(1,"22025555");
            pstmtUp.setInt(2,2);
            int execute = pstmtUp.executeUpdate();
            System.out.println("execute update1 :"+execute);
            //想回滾到還原點值桩,那么之前執(zhí)行的sql要提交事務(wù),否則這個還原點就沒有意義豪椿,因為一整個事務(wù)都沒有提交
            conn.commit();
            //創(chuàng)建還原點
            savepoint = conn.setSavepoint("updateSavepoint1");

            pstmtUp.setString(1,"22026666");
            pstmtUp.setInt(2,2);
            execute = pstmtUp.executeUpdate();
            System.out.println("execute update2 :"+execute);
            //報錯
            //System.out.println(1/0);

            //STEP 4: Execute a query
            pstmt = conn.prepareStatement("SELECT id, name, dept, phone FROM t_user WHERE id = ? and name = ?");
            pstmt.setInt(1,2);
            pstmt.setString(2,"艾米哈珀");
            rs = pstmt.executeQuery();
            while (rs.next()) {
                //Retrieve by column name 通過列名獲取值
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String dept = rs.getString("dept");
                String phone = rs.getString("phone");
                //Display values
                System.out.println("id: " + id + "name: " + name + "dept: " + dept + "phone: " + phone);
            }
            //提交事務(wù)
            conn.commit();
            //當(dāng)我們設(shè)置不提交時奔坟,雖然查出來的是最新更改的值,但數(shù)據(jù)庫其實并沒有更改搭盾,因為我們沒有手動提交
            //查出來的是最新更改的值是事務(wù)隔離類型的原因
        } catch (SQLException e) {
            //回滾事務(wù)
            //conn.rollback();
            //回滾到還原點
            e.printStackTrace();
            conn.rollback(savepoint);
        } finally {
            //關(guān)閉 PreparedStatement 對象
            pstmt.close();
            pstmtUp.close();
            conn.close();
        }
        System.out.println("over !");
    }
}

注意:還原點使用特別注意咳秉。

 //想回滾到還原點,那么之前執(zhí)行的sql要提交事務(wù)鸯隅,否則這個還原點就沒有意義澜建,因為一整個事務(wù)都沒有提交
 conn.commit();
 //創(chuàng)建還原點
 savepoint = conn.setSavepoint("updateSavepoint1");
 
  //回滾到還原點
conn.rollback(savepoint);

JDBC 批處理

Statement不能執(zhí)行帶參數(shù)占位符?的sql語句蝌以。

批處理注意用于增刪改炕舵,因為返回的不是對象。

批處理是指你將關(guān)聯(lián)的 SQL 語句組合成一個批處理跟畅,并將他們當(dāng)成一個調(diào)用提交給數(shù)據(jù)庫咽筋。

當(dāng)你一次發(fā)送多個 SQL 語句到數(shù)據(jù)庫時,可以減少通信的資源消耗碍彭,從而提高了性能晤硕。

  • JDBC 驅(qū)動程序不一定支持該功能。你可以使用 DatabaseMetaData.supportsBatchUpdates() 方法來確定目標數(shù)據(jù)庫是否支持批處理更新庇忌。如果你的JDBC驅(qū)動程序支持此功能舞箍,則該方法返回值為 true。
  • Statement皆疹,PreparedStatement 和 CallableStatement 的 addBatch() 方法用于添加單個語句到批處理疏橄。
  • executeBatch() 方法用于啟動執(zhí)行所有組合在一起的語句。
  • executeBatch() 方法返回一個整數(shù)數(shù)組,數(shù)組中的每個元素代表了各自的更新語句的更新數(shù)目捎迫。
  • 正如你可以添加語句到批處理中晃酒,你也可以用 clearBatch() 方法刪除它們。此方法刪除所有用 addBatch() 方法添加的語句窄绒。但是贝次,你不能有選擇性地選擇要刪除的語句。

批處理和 Statement 對象

注意可以不手動控制事務(wù)彰导。

使用 Statement 對象來使用批處理所需要的典型步驟如下所示-

  • 使用 createStatement() 方法創(chuàng)建一個 Statement 對象蛔翅。
  • 使用 setAutoCommit() 方法將自動提交設(shè)為 false。
  • 被創(chuàng)建的 Statement 對象可以使用 addBatch() 方法來添加你想要的所有SQL語句位谋。
  • 被創(chuàng)建的 Statement 對象可以用 executeBatch() 將所有的 SQL 語句執(zhí)行山析。
  • 最后,使用 commit() 方法提交所有的更改掏父。

批處理和 PrepareStatement 對象

使用 prepareStatement 對象來使用批處理需要的典型步驟如下所示-

  • 使用占位符創(chuàng)建 SQL 語句笋轨。
  • 使用任一 prepareStatement() 方法創(chuàng)建 prepareStatement 對象。
  • 使用 setAutoCommit() 方法將自動提交設(shè)為 false赊淑。
  • 被創(chuàng)建的 Statement 對象可以使用 addBatch() 方法來添加你想要的所有 SQL 語句爵政。
  • 被創(chuàng)建的 Statement 對象可以用 executeBatch() 將所有的 SQL 語句執(zhí)行。
  • 最后膏燃,使用 commit() 方法提交所有的更改茂卦。

示例:

public class JDBCBatchTest {

    private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";

    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/hello_mybatis";

    private static final String USER = "root";

    private static final String PASSWORD = "123456";

    public static void main1(String[] args) throws ClassNotFoundException, SQLException {
        //STEP 2: Register JDBC driver
        Class.forName(JDBC_DRIVER);
        Connection conn = null;
        PreparedStatement pstmtUp = null;
        try {
            //STEP 3: Open a connection
            //conn = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);

            //使用數(shù)據(jù)庫 URL 和 Properties 對象
            Properties info = new Properties();
            info.put("user", USER);
            info.put("password", PASSWORD);
            conn = DriverManager.getConnection(DATABASE_URL, info);
            //關(guān)閉自動提交模式
            //conn.setAutoCommit(false);
            pstmtUp = conn.prepareStatement("UPDATE t_user SET phone = ? WHERE id = ?");
            pstmtUp.setString(1,"22025555");
            pstmtUp.setInt(2,2);
            pstmtUp.addBatch();

            pstmtUp.setString(1,"22026666");
            pstmtUp.setInt(2,1);
            pstmtUp.addBatch();

            pstmtUp.setString(1,"22027777");
            pstmtUp.setInt(2,3);
            pstmtUp.addBatch();
            int[] ints = pstmtUp.executeBatch();
            System.out.println(Arrays.toString(ints));
            //提交事務(wù)
            //conn.commit();
            //當(dāng)我們設(shè)置不提交時,雖然查出來的是最新更改的值组哩,但數(shù)據(jù)庫其實并沒有更改等龙,因為我們沒有手動提交
            //查出來的是最新更改的值是事務(wù)隔離類型的原因
        } catch (Exception e) {
            //回滾事務(wù)
            //conn.rollback();
            //回滾到還原點
            e.printStackTrace();
        } finally {
            //關(guān)閉 PreparedStatement 對象
            pstmtUp.close();
            conn.close();
        }
        System.out.println("over !");
    }

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //STEP 2: Register JDBC driver
        Class.forName(JDBC_DRIVER);
        Connection conn = null;
        Statement stmt = null;
        try {
            //STEP 3: Open a connection
            //conn = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);

            //使用數(shù)據(jù)庫 URL 和 Properties 對象
            Properties info = new Properties();
            info.put("user", USER);
            info.put("password", PASSWORD);
            conn = DriverManager.getConnection(DATABASE_URL, info);
            //關(guān)閉自動提交模式
            //conn.setAutoCommit(false);
            //Statement只能執(zhí)行不帶參數(shù)的sql
            stmt = conn.createStatement();
            String sql;
            sql = "UPDATE t_user SET phone = '123456' WHERE id = 1";
            stmt.addBatch(sql);
            sql = "UPDATE t_user SET phone = '123456' WHERE id = 2";
            stmt.addBatch(sql);
            sql = "UPDATE t_user SET phone = '123456' WHERE id = 3";
            stmt.addBatch(sql);
            //批量處理
            int[] ints = stmt.executeBatch();
            System.out.println(Arrays.toString(ints));
            //提交事務(wù)
            //conn.commit();
            //當(dāng)我們設(shè)置不提交時,雖然查出來的是最新更改的值伶贰,但數(shù)據(jù)庫其實并沒有更改蛛砰,因為我們沒有手動提交
            //查出來的是最新更改的值是事務(wù)隔離類型的原因
        } catch (Exception e) {
            //回滾事務(wù)
            //conn.rollback();
            //回滾到還原點
            e.printStackTrace();
        } finally {
            //關(guān)閉 PreparedStatement 對象
            stmt.close();
            conn.close();
        }
        System.out.println("over !");
    }
}

JDBC 流數(shù)據(jù)

PreparedStatement 對象必須具備使用輸入和輸出流來提供參數(shù)數(shù)據(jù)的能力。這使你能夠?qū)⒄麄€文件存儲到數(shù)據(jù)庫列中黍衙,這樣數(shù)據(jù)庫就能存儲大型數(shù)據(jù)泥畅,例如 CLOB 和 BLOB 數(shù)據(jù)類型。

用于流數(shù)據(jù)有下列幾種方法-

  • setAsciiStream(): 該方法是用來提供較大的 ASCII 值琅翻。
  • setCharacterStream(): 該方法是用來提供較大的 UNICODE 值位仁。
  • setBinaryStream(): 該方法是用來提供較大的二進制值。

setXXXStream()方法需要一個額外的參數(shù)方椎,該參數(shù)是除了參數(shù)占位符的文件大小聂抢。這個參數(shù)通知驅(qū)動程序通過使用流有多少數(shù)據(jù)被發(fā)送到數(shù)據(jù)庫中。

一般不會用這個棠众,都是直接把數(shù)據(jù)轉(zhuǎn)為String類型直接存到text字段里吧琳疏?

JDBC 創(chuàng)建數(shù)據(jù)庫實例

stmt = conn.createStatement();
String sql = "CREATE DATABASE hello_spring";
stmt.executeUpdate(sql);

JDBC 刪除數(shù)據(jù)庫實例

使用的用戶必須擁有刪除數(shù)據(jù)庫的權(quán)限有决,這也側(cè)面反映當(dāng)我們給項目設(shè)置數(shù)據(jù)庫訪問用戶時,要注意權(quán)限的賦予空盼,像數(shù)據(jù)庫操作书幕、DDL這樣的權(quán)限是不應(yīng)該有的。

stmt = conn.createStatement();
String sql = "DROP DATABASE STUDENTS";
stmt.executeUpdate(sql);

JDBC 創(chuàng)建表實例

stmt = conn.createStatement();
String sql = "CREATE TABLE REGISTRATION " +
"(id INTEGER not NULL, " +
" first VARCHAR(255), " + 
" last VARCHAR(255), " + 
" age INTEGER, " + 
" PRIMARY KEY ( id ))"; 
stmt.executeUpdate(sql);
System.out.println("Create

JDBC 刪除表實例

stmt = conn.createStatement();
String sql = "DROP TABLE REGISTRATION ";
stmt.executeUpdate(sql);

JDBC 插入記錄實例

下面這個示例在項目運用中其實應(yīng)該使用PreparedStatement來插入數(shù)據(jù)揽趾,用占位符的形式來創(chuàng)建sql台汇。

 pstmtUp = conn.prepareStatement("INSERT INTO Registration " +
"VALUES (?, ?, ?, ?)");
stmt = conn.createStatement();
String sql = "INSERT INTO Registration " +
"VALUES (100, 'Zara', 'Ali', 18)";
stmt.executeUpdate(sql);
sql = "INSERT INTO Registration " +
"VALUES (101, 'Mahnaz', 'Fatma', 25)";
stmt.executeUpdate(sql);
sql = "INSERT INTO Registration " +
"VALUES (102, 'Zaid', 'Khan', 30)";
stmt.executeUpdate(sql);
sql = "INSERT INTO Registration " +
"VALUES(103, 'Sumit', 'Mittal', 28)";
stmt.executeUpdate(sql);

JDBC 查詢記錄實例

pstmt = conn.prepareStatement("SELECT id, name, dept, phone FROM t_user WHERE id = ? and name = ?");
pstmt.setInt(1,2);
pstmt.setString(2,"艾米哈珀");
rs = pstmt.executeQuery();
while (rs.next()) {
    //Retrieve by column name 通過列名獲取值
    int id = rs.getInt("id");
    String name = rs.getString("name");
    String dept = rs.getString("dept");
    String phone = rs.getString("phone");
    //Display values
    System.out.println("id: " + id + "name: " + name + "dept: " + dept + "phone: " + phone);
}

JDBC 更新記錄實例

pstmtUp = conn.prepareStatement("UPDATE t_user SET phone = ? WHERE id = ?");
pstmtUp.setString(1,"22025555");
pstmtUp.setInt(2,2);
int execute = pstmtUp.executeUpdate();
System.out.println("execute update1 :"+execute);

JDBC 刪除記錄實例

應(yīng)該使用PreparedStatement來刪除數(shù)據(jù),用占位符的形式來創(chuàng)建sql但骨。

stmt = conn.createStatement();
String sql = "DELETE FROM Registration " +
"WHERE id = 101";
stmt.executeUpdate(sql);

JDBC LIKE 子句實例

LIKE查詢推薦使用:%放在占位符中或使用concat函數(shù)励七。

1:%放在占位符中
parameters.add("%"+familyMemberQueryBean.getFullName()+"%");
sql+=" and t.full_name like ?";
2:使用concat函數(shù)
parameters.add(familyMemberQueryBean.getFullName());
sql+=" and t.full_name like concat('%',?,'%')";
3:使用轉(zhuǎn)義字符\,百分號直接寫在sql語句中
parameters.add(familyMemberQueryBean.getFullName());
sql+=" and t.full_name like "%"?"%"";

  直接查詢的SQL語句如下:SELECT id,full_name,email,phone,remark FROM family_member t WHERE 1 = 1
                                            AND t.full_name LIKE "%"'李'"%" AND t.email LIKE "%"'qq'"%"

4:直接拼接SQL
sql+=" and t.full_name like '%"+familyMemberQueryBean.getFullName()+"%'";

參考:JDBC模糊查詢的4種方式

示例:

public class JDBCTranctionTest {

    private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";

    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/hello_mybatis";

    private static final String USER = "root";

    private static final String PASSWORD = "123456";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //STEP 2: Register JDBC driver
        Class.forName(JDBC_DRIVER);
        ResultSet rs = null;
        Connection conn = null;
        PreparedStatement pstmt = null;
        PreparedStatement pstmtUp = null;
        Savepoint savepoint = null;
        try {
            //STEP 3: Open a connection
            //conn = DriverManager.getConnection(DATABASE_URL, USER, PASSWORD);

            //使用數(shù)據(jù)庫 URL 和 Properties 對象
            Properties info = new Properties();
            info.put("user", USER);
            info.put("password", PASSWORD);
            conn = DriverManager.getConnection(DATABASE_URL, info);
            //關(guān)閉自動提交模式
            conn.setAutoCommit(false);
            pstmtUp = conn.prepareStatement("UPDATE t_user SET phone = ? WHERE id = ?");
            pstmtUp.setString(1,"22025555");
            pstmtUp.setInt(2,2);
            int execute = pstmtUp.executeUpdate();
            System.out.println("execute update1 :"+execute);
            //想回滾到還原點奔缠,那么之前執(zhí)行的sql要提交事務(wù),否則這個還原點就沒有意義吼野,因為一整個事務(wù)都沒有提交
            conn.commit();
            //創(chuàng)建還原點
            savepoint = conn.setSavepoint("updateSavepoint1");

            pstmtUp.setString(1,"22026666");
            pstmtUp.setInt(2,2);
            execute = pstmtUp.executeUpdate();
            System.out.println("execute update2 :"+execute);
            //報錯
            //System.out.println(1/0);

            //STEP 4: Execute a query
            //pstmt = conn.prepareStatement("SELECT id, name, dept, phone FROM t_user WHERE id = ? and name = ?");

            //JDBC模糊查詢的4種方式
            //1校哎、%放在占位符中
    /*        pstmt = conn.prepareStatement("SELECT id, name, dept, phone FROM t_user WHERE id = ? and name like ? ");
            pstmt.setInt(1,1);
            String userName = "青";
            pstmt.setString(2,"%" + userName + "%");
            rs = pstmt.executeQuery();*/

            //2、使用concat函數(shù)
           /* pstmt = conn.prepareStatement("SELECT id, name, dept, phone FROM t_user WHERE id = ? and name like concat('%',?,'%')");
            pstmt.setInt(1,1);
            String userName = "青";
            pstmt.setString(2,userName);
            rs = pstmt.executeQuery();*/

            //3瞳步、使用轉(zhuǎn)義字符\闷哆,百分號直接寫在sql語句中
            /*pstmt = conn.prepareStatement("SELECT id, name, dept, phone FROM t_user WHERE id = ? and name like  \"%\"?\"%\"");
            pstmt.setInt(1,1);
            String userName = "青";
            pstmt.setString(2,userName);
            rs = pstmt.executeQuery();*/

            //4、直接拼接SQL
            Statement stmt = conn.createStatement();
            String sql ="SELECT id, name, dept, phone FROM t_user WHERE id = 1 and name like '%青%' ";
            rs = stmt.executeQuery(sql);
            while (rs.next()) {
                //Retrieve by column name 通過列名獲取值
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String dept = rs.getString("dept");
                String phone = rs.getString("phone");
                //Display values
                System.out.println("id: " + id + "name: " + name + "dept: " + dept + "phone: " + phone);
            }
            //提交事務(wù)
            conn.commit();
            //當(dāng)我們設(shè)置不提交時单起,雖然查出來的是最新更改的值抱怔,但數(shù)據(jù)庫其實并沒有更改,因為我們沒有手動提交
            //查出來的是最新更改的值是事務(wù)隔離類型的原因
        } catch (Exception e) {
            //回滾事務(wù)
            //conn.rollback();
            //回滾到還原點
            e.printStackTrace();
            conn.rollback(savepoint);
        } finally {
            //關(guān)閉 PreparedStatement 對象
            pstmt.close();
            pstmtUp.close();
            conn.close();
        }
        System.out.println("over !");
    }
}

JDBC 排序?qū)嵗?/h4>

sql語句加入 order by column desc 或 order by column asc;

  stmt = conn.createStatement();
String sql = "SELECT id, first, last, age FROM Registration" +
" ORDER BY first ASC";
ResultSet rs = stmt.executeQuery(sql);

其他

execute與executeUpdate的區(qū)別

相同點
execute與executeUpdate的相同點:都可以執(zhí)行增加嘀倒,刪除屈留,修改.

不同點
不同1:
execute可以執(zhí)行查詢語句
然后通過getResultSet,把結(jié)果集取出來
executeUpdate不能執(zhí)行查詢語句

不同2:
execute返回boolean類型测蘑,true表示執(zhí)行的是查詢語句灌危,false表示執(zhí)行的是insert,delete,update等等
executeUpdate返回的是int,表示有多少條數(shù)據(jù)受到了影響.

略讀

三層架構(gòu)詳解碳胳,JDBC在數(shù)據(jù)訪問層:

img

UI(表現(xiàn)層): 主要是指與用戶交互的界面勇蝙。用于接收用戶輸入的數(shù)據(jù)和顯示處理后用戶需要的數(shù)據(jù)。

BLL:(業(yè)務(wù)邏輯層): UI層和DAL層之間的橋梁挨约。實現(xiàn)業(yè)務(wù)邏輯味混。業(yè)務(wù)邏輯具體包含:驗證、計算诫惭、業(yè)務(wù)規(guī)則等等翁锡。

DAL:(數(shù)據(jù)訪問層): 與數(shù)據(jù)庫打交道。主要實現(xiàn)對數(shù)據(jù)的增贝攒、刪盗誊、改、查。將存儲在數(shù)據(jù)庫中的數(shù)據(jù)提交給業(yè)務(wù)層哈踱,同時將業(yè)務(wù)層處理的數(shù)據(jù)保存到數(shù)據(jù)庫荒适。(當(dāng)然這些操作都是基于UI層的。用戶的需求反映給界面(UI)开镣,UI反映給BLL刀诬,BLL反映給DAL,DAL進行數(shù)據(jù)的操作邪财,操作后再一一返回陕壹,直到將用戶所需數(shù)據(jù)反饋給用戶)

img

使用三層架構(gòu)的目的:解耦 。

疑問:

Q:ResultSet 這個結(jié)果集是個怎么樣的結(jié)構(gòu)树埠,為什么要這樣設(shè)計糠馆,內(nèi)部是什么容器?

ResultSet rs = stmt.executeQuery(sql);

Q:PreparedStatement計劃多次使用 SQL 語句的時候使用是什么意思怎憋?每條sql都會被多次使用坝致怠?

Q:conn.createStatement();默認初始化一個 Statement 對象來創(chuàng)建一個只能前進绊袋,而且只讀的 ResultSet 對象毕匀。那為什么可以使用這些前后移動的方法呢?

rs.previous();
rs.absolute(1);

Q:JDBC 流數(shù)據(jù)還有用到么癌别?什么場景里會用皂岔?一般不會用這個,都是直接把數(shù)據(jù)轉(zhuǎn)為String類型直接存到text字段里吧展姐?

參考

JDBC教程——W3Cschool

三層架構(gòu)詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末躁垛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子诞仓,更是在濱河造成了極大的恐慌缤苫,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件墅拭,死亡現(xiàn)場離奇詭異活玲,居然都是意外死亡,警方通過查閱死者的電腦和手機谍婉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門舒憾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人穗熬,你說我怎么就攤上這事镀迂。” “怎么了唤蔗?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵探遵,是天一觀的道長窟赏。 經(jīng)常有香客問我,道長箱季,這世上最難降的妖魔是什么涯穷? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮藏雏,結(jié)果婚禮上拷况,老公的妹妹穿的比我還像新娘。我一直安慰自己掘殴,他們只是感情好赚瘦,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布杨蛋。 她就那樣靜靜地躺著狂魔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪变屁。 梳的紋絲不亂的頭發(fā)上服爷,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天杜恰,我揣著相機與錄音,去河邊找鬼仍源。 笑死,一個胖子當(dāng)著我的面吹牛舔涎,可吹牛的內(nèi)容都是我干的笼踩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼亡嫌,長吁一口氣:“原來是場噩夢啊……” “哼嚎于!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挟冠,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤于购,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后知染,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肋僧,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年控淡,在試婚紗的時候發(fā)現(xiàn)自己被綠了嫌吠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡掺炭,死狀恐怖辫诅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涧狮,我是刑警寧澤炕矮,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布么夫,位于F島的核電站,受9級特大地震影響肤视,放射性物質(zhì)發(fā)生泄漏档痪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一钢颂、第九天 我趴在偏房一處隱蔽的房頂上張望钞它。 院中可真熱鬧,春花似錦殊鞭、人聲如沸遭垛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锯仪。三九已至,卻和暖如春趾盐,著一層夾襖步出監(jiān)牢的瞬間庶喜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工救鲤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留久窟,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓本缠,卻偏偏與公主長得像斥扛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丹锹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

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

  • 以下我是歸納的JDBC知識點圖: 圖上的知識點都可以在我其他的文章內(nèi)找到相應(yīng)內(nèi)容稀颁。 JDBC常見面試題 JDBC操...
    Java3y閱讀 1,732評論 0 15
  • 本文主要內(nèi)容 1、JDBC 2楣黍、DBUtils 01JDBC概念和數(shù)據(jù)庫驅(qū)動程序 A: JDBC概念和數(shù)據(jù)庫驅(qū)動程...
    勝浩_ae28閱讀 397評論 0 0
  • JDBC簡介 SUN公司為了簡化匾灶、統(tǒng)一對數(shù)據(jù)庫的操作,定義了一套Java操作數(shù)據(jù)庫的規(guī)范租漂,稱之為JDBC阶女。JDBC...
    奮斗的老王閱讀 1,518評論 0 51
  • 我今年九歲了,我的爸爸媽媽不是我親生的爸爸媽媽驾孔,確切地說他們是我的舅舅和舅媽芍秆。 因為我舅舅和舅媽結(jié)婚多年沒有孩子惯疙,...
    燭影搖紅_06ba閱讀 346評論 6 6
  • 一.你本周都做了什么事情?周目標是什么妖啥?完成情況如何霉颠? 1.《槍炮,病菌與鋼鐵》(10h)《曾國藩》唐浩明版(4h...
    kidII閱讀 105評論 0 1