前言
我們知道,在進(jìn)行后端開(kāi)發(fā)的時(shí)候,既要精通PHP、Java(JSP)等語(yǔ)言,也要能熟練使用MySQL、SQL Server等數(shù)據(jù)庫(kù)系統(tǒng),那怎樣能將兩者結(jié)合起來(lái)呢?JDBC就是其中的一種方式。
簡(jiǎn)介
JDBC全稱為Java Data Base Connectivity,是一種可用于執(zhí)行SQL語(yǔ)句的Java API(Application Programming Interface,應(yīng)用程序接口),是連接數(shù)據(jù)庫(kù)和Java應(yīng)用程序的紐帶。方便起見(jiàn),本文使用的數(shù)據(jù)庫(kù)為MySQL的5.7版本。
基本操作
首先,我們登錄MySQL,創(chuàng)建數(shù)據(jù)庫(kù)t1和數(shù)據(jù)表student_info:

然后再向表中插入一些數(shù)據(jù):

連接數(shù)據(jù)庫(kù):
要對(duì)數(shù)據(jù)表中的數(shù)據(jù)進(jìn)行操作,應(yīng)該首先建立與數(shù)據(jù)庫(kù)的連接。訪問(wèn)數(shù)據(jù)庫(kù)的時(shí)候,首先要加載數(shù)據(jù)庫(kù)的驅(qū)動(dòng)程序(只需要在第一次訪問(wèn)數(shù)據(jù)庫(kù)時(shí)加載一次),然后每次訪問(wèn)的時(shí)候創(chuàng)建一個(gè)Connection類對(duì)象,接著執(zhí)行操作數(shù)據(jù)庫(kù)的SQL語(yǔ)句,完成對(duì)數(shù)據(jù)庫(kù)的操作后再銷毀Connection類對(duì)象,釋放與數(shù)據(jù)庫(kù)的連接。下面舉個(gè)例子:
package test;
import java.sql.*;//導(dǎo)入包
public class Connect { // 創(chuàng)建類Connect
Connection con; // 聲明Connection對(duì)象
public Connection getConnection() {// 定義返回值為Connection的方法
String driver = "com.mysql.jdbc.Driver"; //driver指向數(shù)據(jù)庫(kù)的驅(qū)動(dòng)程序
String url = "jdbc:mysql://127.0.0.1:3306/t1?framework?characterEncoding=utf8&useSSL=true";
// url指向要連接的數(shù)據(jù)庫(kù),這里我們?cè)L問(wèn)數(shù)據(jù)庫(kù)t1
String user = "root";// MySQL配置時(shí)的用戶名
String password = "root"; // MySQL配置時(shí)的密碼
try {// 加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)類
Class.forName(driver);
System.out.println("數(shù)據(jù)庫(kù)驅(qū)動(dòng)加載成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {// 通過(guò)訪問(wèn)數(shù)據(jù)庫(kù)的URL獲取數(shù)據(jù)庫(kù)連接對(duì)象
con = DriverManager.getConnection(url,user,password);
System.out.println("數(shù)據(jù)庫(kù)連接成功");
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
public static void main(String[] args){
Connect c = new Conn(); // 創(chuàng)建本類對(duì)象
c.getConnection(); // 調(diào)用連接數(shù)據(jù)庫(kù)方法
}
}
jdbc:mysql://127.0.0.1:3306/t2這段代碼中,127.0.0.1是本機(jī)地址,也可以用localhost代替;t1是要訪問(wèn)的數(shù)據(jù)庫(kù)名;而后面的framewor?characterEncoding=utf8&useSSL=true則是新版本MySQL的特性,即需要指明字符的編碼格式和是否進(jìn)行SSL連接,如果不指明的話程序就會(huì)報(bào)錯(cuò)。root和root分別是我登錄MySQL時(shí)的用戶名和密碼。
這里最關(guān)鍵的是兩個(gè)方法:Class.forName(驅(qū)動(dòng)程序)和DriverManager.getConnection(數(shù)據(jù)庫(kù),用戶名,密碼) 作用就是加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)類和獲取數(shù)據(jù)庫(kù)連接對(duì)象,我們知道Java是面向?qū)ο蟮恼Z(yǔ)言,因此就把數(shù)據(jù)庫(kù)的操作轉(zhuǎn)化為了對(duì)類和對(duì)象的操作,
還有一點(diǎn)就是,這里把連接數(shù)據(jù)庫(kù)這一操作定義Connect類,并以Connection對(duì)象作為直接調(diào)用Connect類的getConnection()方法或者修改方法的代碼(要連接的的數(shù)據(jù)庫(kù)名)再調(diào)用方法就行了,提高效率。
至于是否已成功連接數(shù)據(jù)庫(kù)t1呢?我們可以用接下來(lái)的幾個(gè)操作進(jìn)行檢驗(yàn)。
向數(shù)據(jù)庫(kù)發(fā)送SQL語(yǔ)句:
連接數(shù)據(jù)庫(kù)之后,要向數(shù)據(jù)庫(kù)發(fā)送SQL語(yǔ)句(即指令),才能對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行一系列操作。要執(zhí)行SQL語(yǔ)句首先要獲得Statement類對(duì)象,通過(guò)上面創(chuàng)建的Connect類對(duì)象con的createdStatement方法可獲得Statement類對(duì)象:
try{
Statement sql = con.createStatement();
}catch(SQL Exception e){
e.printStackTrace();
}
處理查詢結(jié)果集:
有了Statement類對(duì)象后,可以調(diào)用相應(yīng)的方法實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)庫(kù)的查詢和修改,并將查詢的結(jié)果集存放在ResultSet類對(duì)象中:
ResulySet result = sql.executeQuery(SELECT * FROM student_info);
SELECT * FROM student_info這條指令的含義是查詢數(shù)據(jù)表student_info中的所有數(shù)據(jù)。
ResultSet類對(duì)象一次只能顯示結(jié)果集中的一行數(shù)據(jù)(即數(shù)據(jù)表中的一條記錄),使用該類的next()方法可將查詢位置移向下一行。next()方法的返回值為boolean類型的數(shù)據(jù),當(dāng)查詢位置移動(dòng)到最后一行之后再調(diào)用next()方法會(huì)返回false.
查詢數(shù)據(jù):
下面我們來(lái)查詢數(shù)據(jù)表student_info中的數(shù)據(jù):
package test;
import java.sql.*;
public class Query { // 創(chuàng)建類
static Connection con; // 聲明Connection類對(duì)象
static Statement sql; // 聲明Statement類對(duì)象
static ResultSet result; // 聲明ResultSet類對(duì)象
public Connection getConnection() { // 連接數(shù)據(jù)庫(kù)方法
/*****連接數(shù)據(jù)庫(kù)的代碼,此處省略*****/
return con; // 返回Connection類對(duì)象
}
public static void main(String[] args) {
Query q = new Query(); // 創(chuàng)建本類對(duì)象
con = q.getConnection(); // 連接數(shù)據(jù)庫(kù)
try {
sql = con.createStatement(); // 實(shí)例化Statement類對(duì)象
result = sql.executeQuery("SELECT * FROM student_info");// 執(zhí)行SQL語(yǔ)句,返回結(jié)果集
while (result.next()) {
String id = result.getString("id"); // 獲取列名是"id"的字段值
String name = result.getString("name");// 獲取列名是"name"的字段值
String sex = result.getString("sex");// 獲取列名是"sex"的字段值
System.out.print("編號(hào):" + id); // 將數(shù)據(jù)輸出
System.out.print(" 姓名:" + name);
System.out.println(" 性別:" + sex);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制臺(tái)顯示的效果為:

可見(jiàn),通過(guò)JDBC查詢到的數(shù)據(jù)和直接登錄MySQL查詢到的數(shù)據(jù)是完全相同的。
這個(gè)例子是順序查詢數(shù)據(jù),在SQL語(yǔ)句中,也可以用LIKE操作符進(jìn)行模糊查詢,即使用%來(lái)代替0個(gè)或多個(gè)字符,使用_來(lái)代替一個(gè)字符。在使用模糊查詢之前,先插入幾條數(shù)據(jù):

如果我們想要查詢張姓同學(xué)的信息,就可以使用模糊查詢,改動(dòng)如下代碼即可:
result = sql.executeQuery("SELECT * FROM student_info WHERE name LIKE '張%'");
控制臺(tái)顯示的效果為:

預(yù)處理語(yǔ)句:
向數(shù)據(jù)庫(kù)發(fā)送一個(gè)SQL語(yǔ)句,數(shù)據(jù)庫(kù)中的SQL解釋器負(fù)責(zé)把SQL語(yǔ)句生成底層的內(nèi)部命令然后執(zhí)行,完成對(duì)數(shù)據(jù)的操作。如果不斷地向數(shù)據(jù)庫(kù)提交SQL語(yǔ)句,肯定會(huì)增加數(shù)據(jù)庫(kù)SQL解釋器的負(fù)擔(dān),影響執(zhí)行的速度。
在JDBC中,可以通過(guò)Connection類對(duì)象的prepareStatement()方法對(duì)SQL語(yǔ)句進(jìn)行預(yù)處理,生成數(shù)據(jù)庫(kù)底層的內(nèi)部命令,并將該命令封裝在PreparedStatement類對(duì)象中,通過(guò)調(diào)用該對(duì)象的相應(yīng)方法執(zhí)行底層數(shù)據(jù)庫(kù)命令。這樣能夠減輕數(shù)據(jù)庫(kù)的負(fù)擔(dān),提高訪問(wèn)數(shù)據(jù)庫(kù)的速度。
對(duì)SQL進(jìn)行預(yù)處理時(shí)可以使用通配符?來(lái)代替任何的字段值:
sql = con.prepareStatement("SELECT * FROM student_info WHERE id = ?");
在執(zhí)行預(yù)處理語(yǔ)句恰安,必須用相應(yīng)方法來(lái)設(shè)置通配符所表示的值:
sql.setInt(1,2);
上面語(yǔ)句中的1表示左數(shù)第一個(gè)通配符,2表示設(shè)置的通配符的值。將通配符的值設(shè)置為2后,功能等同于:
sql = con.prepareStatement("SELECT * FROM student_info WHERE id = 2");
需要注意的是,通過(guò)setXXX()方法為通配符設(shè)置值的時(shí)候,建議使用與通配符所代表參數(shù)匹配的方法,即上面的例子中字段id的類型為int,因此使用setInt()方法。當(dāng)然也可以使用setObject()為各種類型的參數(shù)(通配符)賦值。
下面使用預(yù)處理語(yǔ)句查找id為3的同學(xué)信息:
package test;
import java.sql.*;
public class Prepare { // 創(chuàng)建類
static Connection con; // 聲明Connection類對(duì)象
static PrepareStatement ps; // 聲明PrepareStatement類對(duì)象
static ResultSet result; // 聲明ResultSet類對(duì)象
public Connection getConnection() { // 連接數(shù)據(jù)庫(kù)方法
/*****連接數(shù)據(jù)庫(kù)的代碼,此處省略*****/
return con; // 返回Connection類對(duì)象
}
public static void main(String[] args) {
Prepare p = new Prepare(); // 創(chuàng)建本類對(duì)象
con = p.getConnection(); // 連接數(shù)據(jù)庫(kù)
try {
ps = con.prepareStatement("SELECT * FROM student_info WHERE id = ?");
ps.setInt(1, 3); // 設(shè)置參數(shù)
result = ps.executeQuery(); // 執(zhí)行預(yù)處理語(yǔ)句
while (result.next()) {
/*****查詢和輸出數(shù)據(jù)的代碼,此處省略*****/
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制臺(tái)顯示的效果為:

盡管使用預(yù)處理語(yǔ)句看起來(lái)更加繁瑣了,但卻能使程序動(dòng)態(tài)地改變SQL語(yǔ)句中關(guān)于字段值條件的設(shè)定。
添加、修改、刪除數(shù)據(jù):
通過(guò)SQL語(yǔ)句可以對(duì)數(shù)據(jù)執(zhí)行添加、修改和刪除操作,可通過(guò)PrepareStatement類的指定參數(shù)動(dòng)態(tài)地對(duì)數(shù)據(jù)表中的數(shù)據(jù)進(jìn)行操作,并使用executeUpdate()方法執(zhí)行更新語(yǔ)句的操作。
下面對(duì)數(shù)據(jù)表student_info中的數(shù)據(jù)進(jìn)行添加、修改和刪除:
package test;
import java.sql.*;
public class Test { // 創(chuàng)建類
static Connection con; // 聲明Connection類對(duì)象
static Statement sql; // 聲明Statement類對(duì)象
static ResultSet result; // 聲明ResultSet類對(duì)象
public Connection getConnection() { // 連接數(shù)據(jù)庫(kù)方法
/*****連接數(shù)據(jù)庫(kù)的代碼,此處省略*****/
return con; // 返回Connection類對(duì)象
}
public static void main(String[] args) {
Test t = new Test(); // 創(chuàng)建本類對(duì)象
con = t.getConnection(); // 連接數(shù)據(jù)庫(kù)
try {
ps = con.prepareStatement("INSERT student_info VALUES(?,?,?)");// 預(yù)處理添加數(shù)據(jù)
ps.setInt(1, 06);
ps.setString(2, "王紅");
ps.setString(3, "女");
ps.executeUpdate();
ps = con.prepareStatement("UPDATE student_info SET name = ? WHERE id = 5");
// 預(yù)處理修改數(shù)據(jù)
ps.setString(1, "張小娜");
ps.executeUpdate();
ps = con.prepareStatement("DELETE FROM student_info WHERE sex = ?");// 預(yù)處理刪除數(shù)據(jù)
ps.setString(1, "男");
ps.executeUpdate();
result = ps.executeQuery("SELECT * FROM student_info");
while (result.next()) {
/*****查詢和輸出數(shù)據(jù)的代碼,此處省略*****/
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制臺(tái)顯示的效果為:

可見(jiàn),所有對(duì)數(shù)據(jù)的操作都是正確無(wú)誤的。
后記
關(guān)于JDBC的應(yīng)用,舉個(gè)很簡(jiǎn)單的例子,當(dāng)我們用JSP做Web后臺(tái)開(kāi)發(fā)比如一個(gè)登錄系統(tǒng)的時(shí)候,可以將已經(jīng)注冊(cè)過(guò)的用戶信息(用戶名,密碼,手機(jī)號(hào)碼等)保存到數(shù)據(jù)庫(kù)中,也可以判斷正在登錄用戶是否已注冊(cè)或者用戶名或密碼是否正確。JSP是基于Java的語(yǔ)言,要在JSP中訪問(wèn)數(shù)據(jù)庫(kù),自然就要用到JDBC了。
可以看到,JDBC其實(shí)就是在Java程序中操作數(shù)據(jù)庫(kù),核心就在調(diào)用于類中的各種方法,這需要Java的基礎(chǔ),同時(shí),在對(duì)數(shù)據(jù)的各種操作中也利用到了SQL語(yǔ)句,這就要求掌握一定的數(shù)據(jù)庫(kù)知識(shí)。
總而言之,JDBC并不難,但要熟練使用各種Java方法和SQL語(yǔ)句卻不是一件易事,這就需要我們查閱官方文檔和平時(shí)的積累。
當(dāng)然,除了JDBC還有其他的后端語(yǔ)言和數(shù)據(jù)庫(kù)的連接方式,學(xué)習(xí)其他語(yǔ)言或者有興趣的小伙伴可以自行學(xué)習(xí)。