更新時(shí)間:2020-01-15 來源:黑馬程序員 瀏覽量:
1、什么是JDBC?
在web開發(fā)中,不可避免的地要使用數(shù)據(jù)庫來存儲和管理數(shù)據(jù)。為了在java語言中提供數(shù)據(jù)庫訪問的支持,Sun公司于1996年提供了一套訪問數(shù)據(jù)的標(biāo)準(zhǔn)Java類庫,即JDBC。
JDBC的全稱是Java數(shù)據(jù)庫連接(Java Database connect),它是一套用于執(zhí)行SQL語句的Java API。應(yīng)用程序可通過這套API連接到關(guān)系數(shù)據(jù)庫,并使用SQL語句來完成對數(shù)據(jù)庫中數(shù)據(jù)的查詢、更新和刪除等操作。應(yīng)用程序使用JDBC訪問數(shù)據(jù)庫的方式如下圖所示。
從上圖可以看出,應(yīng)用程序使用JDBC訪問特定的數(shù)據(jù)庫時(shí),需要與不同的數(shù)據(jù)庫驅(qū)動進(jìn)行連接。由于不同數(shù)據(jù)庫廠商提供的數(shù)據(jù)庫驅(qū)動不同,因此,為了使應(yīng)用程序與數(shù)據(jù)庫真正建立連接,JDBC不僅需要提供訪問數(shù)據(jù)庫的API,還需要封裝與各種數(shù)據(jù)庫服務(wù)器通信的細(xì)節(jié)為了幫助大家更好地理解應(yīng)用程序如何通過JDBC訪問數(shù)據(jù)庫,下面通過一張圖來描述JDBC的具體實(shí)現(xiàn)細(xì)節(jié),如下圖。
從上圖中可以看出,JDBC的實(shí)現(xiàn)包括三部分。
(1)JDBC驅(qū)動管理器:負(fù)責(zé)注冊特定的JDBC驅(qū)動器,主要通過java.sql. Driver Manager類實(shí)現(xiàn)。
(2)JDBC驅(qū)動器API:由Sun公司負(fù)責(zé)制定,其中最主要的接口是java.sql. Driver接口。
(3)JDBC驅(qū)動器:它是一種數(shù)據(jù)庫驅(qū)動,由數(shù)據(jù)庫廠商創(chuàng)建,也稱為JDBC驅(qū)動程序JDBC驅(qū)動器實(shí)現(xiàn)了JDBC驅(qū)動器API,負(fù)責(zé)與特定的數(shù)據(jù)庫連接,以及處理通信細(xì)節(jié)。
2、JDBC常用API
在開發(fā)JDBC程序前,首先了解一下JDBC常用的API。JDBC API主要位于java.sql包中,該包定義了一系列訪問數(shù)據(jù)庫的接口和類,具體如下。
1. Driver接口
Driver接口是所有JDBC驅(qū)動程序必須實(shí)現(xiàn)的接口,該接口專門提供給數(shù)據(jù)庫廠商使用。在編寫JDBC程序時(shí),必須要把指定數(shù)據(jù)庫驅(qū)動程序或類庫加載到項(xiàng)目的classpath中。
2. DriverManager類
Driver Manager類用于加載JDBC驅(qū)動并且創(chuàng)建與數(shù)據(jù)庫的連接。在Driver Manager類中,定義了兩個(gè)比較重要的靜態(tài)方法。如表所示:
registerDriver(Driver driver)
該方法用于向 DriverManager中注冊給定的JDBC驅(qū)動程程序
getConnection(String url,String user,String pwd)
該方法用于建立和數(shù)據(jù)庫的連接,并返回表示連接的 Connection對象
3、Connection接口
Connection接口代表Java程序和數(shù)據(jù)庫的連接,在Connection接口中,定義了一系列方法,具體如表所示。
getMetaData()
該方法用于返回表示數(shù)據(jù)庫的元數(shù)據(jù)的 DatabaseMetaData對象
createStatement()
用于創(chuàng)建一個(gè)Statement對象來將SQL語句發(fā)送到數(shù)據(jù)庫
prepareStatement(String sql)
用于創(chuàng)建一個(gè)PreparedStatement對象來將參數(shù)化的SQL語句發(fā)送到數(shù)據(jù)庫
prepareCall(String sql)
用于創(chuàng)建一個(gè)CallableStatement對象來調(diào)用數(shù)據(jù)庫存儲過程
4、Statement接口
Statement接口用于向數(shù)據(jù)庫發(fā)送SQL語句,在Statement接口中,提供了三個(gè)執(zhí)行SQL語句的方法,具體如表所示。
execute(String sql)
用于執(zhí)行各種SQL語句,該方法返回一個(gè)boolean類型的值,如果為true,表示所執(zhí)行的SQL語句具備查詢結(jié)果,可通過Statement的getResultSet方法獲得查詢結(jié)果。
executeUpdate(String sql)
用于執(zhí)行SQL中的Insert、update和delete語句。該方法返回一個(gè)int類型的值,表示數(shù)據(jù)庫中受該SQL語句影響的記錄的數(shù)目。
executeQuery(String sql)
用于執(zhí)行SQL中的select語句,該方法返回一個(gè)表示查詢結(jié)果的ResultSet對象
5. PreparedStatement接口
PreparedStatement是Statement的子接口,用于執(zhí)行預(yù)編譯的SQL語句。在PreparedStatement接口中,提供了一些基本操作的方法,具體如表下所示。
executeUpdate()
在此PreparedStatement對象中執(zhí)行SQL語句,該語句必須是個(gè)DML語句或者是無返回內(nèi)容的SQL語句,比如DDL語句。
executeQuery()
在此PreparedStatement對象中執(zhí)行SQL查詢,該方法返回的ResultSet對象
setInt(int parameterIndex, int x)
將指定參數(shù)設(shè)置為給定的int值
setFloat(int parameterIndex, float x)
指定參數(shù)設(shè)置為給定的float值
setString(int parameterIndex, String x)
將指定參數(shù)設(shè)置為給定的String值
setDate(int parameterIndex, Date x)
將指定參數(shù)設(shè)置為給定的Date值
addBatch()
將一組參數(shù)添加到此PreparedStatement對象的批處理命令中
setCharacterStream(parameterIndex, reader, length)
將指定的輸入流寫入數(shù)據(jù)庫的文本字段
setBinaryStream(parameterIndex, x, length)
將二進(jìn)制的輸入流數(shù)據(jù)寫入到二進(jìn)制字段中
需要注意的是,上表中的setDate()方法可以設(shè)置日期內(nèi)容,但參數(shù)Date的類型是java.sq.Date,而不是java.util.Date。
6、CallableStatement接口
CallableStatement是PreparedStatement的子接口,用于執(zhí)行SQL存儲過程。在Callablestatement按接口中,提供了一些基本操作的方法,具體下表所示:
registerOutParameter(int parameterIndex,int sqlType)
按順序位置將OUT參數(shù)注冊為SQL類型。其中,parameterIndex表示順序位置,sqlType表示SQL類型
setNull(String parameter Name, int sqlType)
將指定參數(shù)設(shè)置為SQL類型的NULL
setString(String parameterName, String x)
查詢最后一個(gè)讀取的OUT參數(shù)是否為SQL類型的NULL
wasNull()
查詢最后一個(gè)讀取的OUT參數(shù)是否為SQL類型的NULL
getlnt(int parameterIndex)
以Java語言中int值的形式獲取指定的數(shù)據(jù)庫中INTEGER類型參數(shù)的值
需要注意的是,由于 CallableStatement接口繼承PreparedStatement,PreparedStatement接口又繼承了 Statement,因此CallableStatement接口中除了擁有自己特有的方法,也同時(shí)擁有了這兩個(gè)父接口中的方法。
7、ResultSet接口
ResultSet接口表示 select查詢語句得到的結(jié)果集,該結(jié)果集封裝在一個(gè)邏輯表格中。在 ResultSet接口內(nèi)部有一個(gè)指向表格數(shù)據(jù)行的游標(biāo),ResultSet對象初始化時(shí),游標(biāo)在表格的第一行之前。下表中列舉了ResultSet接口中的常用方法。
getString(int columnIndex)
用于獲取指定字段的String類型的值,參數(shù)columnIndex代表字段的索引
getString(String columnName)
用于獲取指定字段的String類型的值,參數(shù)column Name代表字段的名稱
getInt(int columnIndex)
用于獲取指定字段的int類型的值,參數(shù)columnIndex代表字段的索引
getInt(String columnName)
用于獲取指定字段的int類型的值,參數(shù)columnName代表字段的名稱
getDate(int columnIndex)
用于獲取指定字段的Date類型的值,參數(shù)columnIndex代表字段的索引
getDate(String columnName)
用于獲取指定字段的Date類型的值,參數(shù)column Name代表字段的名稱
next()
將游標(biāo)從當(dāng)前位置向下移一行
absolute(int row)
將游標(biāo)移動到此Resultset對象的指定行
afterLast()
將游標(biāo)移動到此ResultSet對象的末尾,即最后一行之后
beforeFirst()
將游標(biāo)移動到此Resultset對象的開頭,即第一行之前
previous()
將游標(biāo)移動到此ResultSet對象的上一行
last()
將游標(biāo)移動到此ResultSet對象的最
從上表中可以看出,ResultSet接口中定義了大量的getXxx()方法,采用哪種getXxx()方法取決于字段的數(shù)據(jù)類型。程序既可以通過字段的名稱來獲取指定數(shù)據(jù),也可以通過字段的索引來獲取指定的數(shù)據(jù),字段的索引是從1開始編號的。
3、實(shí)現(xiàn)第一個(gè)JDBC程序
通過前面的學(xué)習(xí),我們對JDBC及其常用API有了大致的了解,接下來就開始介紹JDBC編程,JDBC編程大致按照以下幾個(gè)步驟進(jìn)行。
(1) 加載并注冊數(shù)據(jù)庫驅(qū)動,具體方式如下。
DriverManager.registerDriver(Driver driver);
(2) 通過Driver Manager獲取數(shù)據(jù)庫連接,具體方式如下。
Connection conn= DriverManager.getConnection(String url, String user, String pass);
從上述方式可以看出,getConnection()方法中有三個(gè)參數(shù),它們分別表示數(shù)據(jù)庫url、登錄數(shù)據(jù)庫的用戶名和密碼。數(shù)據(jù)庫山通常遵循如下形式的寫法。
jdbc:subprotocol:subname
上面的URL寫法中jdbc部分是固定的,subprotocol指定鏈接達(dá)到特定數(shù)據(jù)庫的驅(qū)動程序,而subname部分則很不固定,也沒有什么規(guī)律,不同數(shù)據(jù)庫的形式可能存在較大差異,一Mysql數(shù)據(jù)庫為例,其形式如下:
jdbc:mysql://hostname:port/databasename
(3)通過Connection對象獲取Statement對象。Connection創(chuàng)建Statement的方式有如下三種。
① createStatement(): 創(chuàng)建基本的Statement對象
② prepareStatement(): 創(chuàng)建PreparedStatement對象。
③ preparCall(): 創(chuàng)建CallableStatement對象。
以創(chuàng)建基本的Statement對象為例,具體方式如下。
Statement stmt=conn.createStatement();
(4)使用Statement執(zhí)行SQL語句。所有的Statement都有如下三種方法來執(zhí)行語句。
①execute():可以執(zhí)行任何SQL語句。
②executeQuery():通常執(zhí)行查詢語句,執(zhí)行后返回代表結(jié)果集的Resultset對象。
③executeUpdate():主要用于執(zhí)行DML和DDL語句。執(zhí)行DML語句,如INSERT、UPDATE或 DELETE時(shí),返回受SQL語句影響的行數(shù),執(zhí)行DDL語句返回0。
以executeQuer()方法為例,具體方式如下。
//執(zhí)行SQL語句,獲取結(jié)果集ResulSet
ResultSet rs=stmt.executQuery(sql);
(5)操作ResultSet結(jié)果集。如果執(zhí)行的SQL語句是查詢語句,執(zhí)行結(jié)果將返回Resultset對象,該對象里保存了SQL語句查詢的結(jié)果。程序可以通過操作該ResultSet對象來取出查詢結(jié)果。 ResultSet對象提供的方法主要可以分為以下兩類。
①next()、previous()、first()、last()、beforeFirst()、afterLast()、absolute()等移動記錄指針的方法
②getXxx()獲取指針指向行,特定列的值。
(6)回收數(shù)據(jù)庫資源。關(guān)閉數(shù)據(jù)庫連接,釋放資源,包括關(guān)閉ResultSet、Statement和Connection等資源。
至此,JDBC編程的大致步驟已經(jīng)完成,為了幫助讀者快速學(xué)習(xí)如何開發(fā)JDBC程序,接下來,編寫第一個(gè)JDBC程序,改程序從user表中讀取數(shù)據(jù),并將結(jié)果結(jié)果打印在控制臺,具體步驟入戲所示。
1、搭建實(shí)驗(yàn)環(huán)境
CREATE DATABASE chapter01;
USE chapter01;
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(40),
password VARCHAR(40),
email VARCHAR(60),
birthday DATE
)CHARACTER SET utf8 COOLLATE utf8_genneral_ci;
數(shù)據(jù)庫和表創(chuàng)建成功后,再向users表中插入三條數(shù)據(jù),SQL語句如下所示。
INSERT INTO users(NAME,PASSWORD,email,birthday)
VALUES('zs','123456','zs@sina.com','1980-12-04');
INSERT INTO users(NAME,PASSWORD,email,birthday)
VALUES('lisi',123456,1isi@sina.com,'1981-12-04');
INSERT INTO users(NAME,PASSWORD,email,birthday)
VALUES('wangwu',123456,'wangwu@sina.com','1979-12-04');
2、導(dǎo)入數(shù)據(jù)庫驅(qū)動
新建Java工程chapter01,將要訪問的數(shù)據(jù)庫驅(qū)動文件添加到classpath中。由于應(yīng)用程序訪問的是MySQL數(shù)據(jù)庫,因此,將MySQL的數(shù)據(jù)庫驅(qū)動文件mysql-connector-java-5.0.8-bin.jar添加到classpath中即可。
3、編寫JDBC程序
在工程chapter01中,新建Java類Example01,該類用于讀取數(shù)據(jù)庫中的users表,并將結(jié)果輸出,如例下面案例所示。
package cn.itcast.jdbc.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Date;
public class Example01 {
public static void main(String[] args) throws SQLException {
//1.注冊數(shù)據(jù)庫的驅(qū)動
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.通過 DriverManager獲取數(shù)據(jù)庫連接
String url="jdbc:mysql://localhost:3306/chapter01";
String usernames="root";
String password="itcast";
Connection conn=DriverManager.getConnection(url, username, password);
//3.通過 Connection對象獲取 Statement對象
Statement stmt= conn.createStatement();
//4.使用 Statement執(zhí)行SQL語句
String sql="select * from users";
ResultSet rs=stmt.executeQuery(sql);
//5、操作 ResultSet結(jié)果集
System.out.println("id|name|password|email|birthday");
while (rs.next()) {
int id=rs.getInt("id"); //通過列名獲取指定字段的值
String name=rs.getString("name");
String psw=rs.getString("password");
String email=rs.getString("email");
Date birthday=rs.getDate("birthday");
System.out.println(id+"|"+name+"|"+psw+"|"+email+"|"+birthday);
}
//6.回收數(shù)據(jù)庫
rs.close();
stmt.close();
conn.close();
}
}
程序執(zhí)行后,會講從users表中讀取到的數(shù)據(jù)打印到控制臺。
在上面案例中演示了JDBC訪問數(shù)據(jù)庫的步驟。首先注冊MySQL的數(shù)據(jù)庫驅(qū)動器類,通過 DriverManager獲取一個(gè)Connection對象,然后使用Connection對象創(chuàng)建了一個(gè)Statement對象,Statement對象能夠通過executeQuery()方法執(zhí)行SQL語句,并返回結(jié)果集ResultSet對象。最后,通過遍歷Resultset對象便可得到最終的查詢結(jié)果需要注意的是,在實(shí)現(xiàn)第一個(gè)JDBC程序時(shí),還有兩個(gè)方面需要改進(jìn),具體如下。
1、注冊驅(qū)動
在注冊數(shù)據(jù)庫驅(qū)動時(shí),雖然DriverManager.registerDriver(new com. mysql.jdbc.Driver())方法可以完成,但會使數(shù)據(jù)庫驅(qū)動被注冊兩次。這是因?yàn)镈river類的源碼中,已經(jīng)在靜態(tài)代碼塊中完成了數(shù)據(jù)庫驅(qū)動的注冊。所以,為了避免數(shù)據(jù)庫驅(qū)動被重復(fù)注冊,只需要在程序中加載驅(qū)動類即可,具體加載方式如下所示。
Class.forName("com.mysqk.jdbc.Driver");
2、釋放資源
由于數(shù)據(jù)庫資源非常寶貴,數(shù)據(jù)庫允許的并發(fā)訪問連接數(shù)量有限,因此,當(dāng)數(shù)據(jù)庫資源使用完畢后,一定要記得釋放資源。為了保證資源的釋放,在Java程序中,應(yīng)該將最終必須要執(zhí)行的操作放在finally代碼塊中,具體方式如下。
if(rs!=null) {
try {
rs.close();
}catch (SQLException e) {
e.printStackTrace();
}
rs=null;
}
if(stmt!=null) {
try {
stmt.close();
}catch (SQLException e) {
e.printStackTrace();
}
stmt=null;
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn=null;
}