驅(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)用程序所處的位置。
常見的 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ū)動程序僅適用于實驗用途或在沒有其他選擇的情況。
自帶 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 的開銷。
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ù)庫。
你可以將應(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)下載的。
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)入包:
注冊 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 (?,?)}";
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()+"%'";
示例:
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的相同點:都可以執(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ù)訪問層:
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ù)反饋給用戶)
使用三層架構(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字段里吧展姐?