第二套題
一、 選擇題
1. 以下數(shù)據(jù)結(jié)構(gòu)中不屬于線性數(shù)據(jù)結(jié)構(gòu)的是____C__。
A、隊(duì)列 B、線性表 C、二叉樹 D、棧
2. 在結(jié)構(gòu)化方法中,用數(shù)據(jù)流程圖(DFD)作為描述工具的軟件開發(fā)階段是__B____。
A、可行性分析 B、需求分析 C、詳細(xì)設(shè)計(jì) D、程序編碼
3. 結(jié)構(gòu)化程序設(shè)計(jì)主要強(qiáng)調(diào)的是__B____。
A、程序的規(guī)模 B、程序的易讀性 C、程序的執(zhí)行效率 D、程序的可移植性
4. 在軟件生命周期中,能準(zhǔn)確地確定軟件系統(tǒng)必須做什么和必須具備哪些功能的階段是_D_____。
A、概要設(shè)計(jì) B、詳細(xì)設(shè)計(jì) C、可行性分析 D、需求分析
5. 下列關(guān)于棧的敘述中正確的是____D__。A、在棧中只能插入數(shù)據(jù) B、在棧中只能刪除數(shù)據(jù)
C、棧是先進(jìn)先出的線性表 D、棧是先進(jìn)后出的線性表
6. 下面不屬于軟件設(shè)計(jì)原則的是__C____。A、抽象 B、模塊化 C、自底向上 D、信息隱蔽
7. 對(duì)長度為N的線性表進(jìn)行順序查找,在最壞情況下所需要的比較次數(shù)為__B____。
A、N+1 B、N C、(N+1)/2 D、N/2
8. 視圖設(shè)計(jì)一般有3種設(shè)計(jì)次序,下列不屬于視圖設(shè)計(jì)的是__B____。
A、自頂向下 B、由外向內(nèi) C、由內(nèi)向外 D、自底向上
9. 下列有關(guān)數(shù)據(jù)庫的描述,正確的是_C_____。A、數(shù)據(jù)庫是一個(gè)DBF文件 B、數(shù)據(jù)庫是一個(gè)關(guān)系
C、數(shù)據(jù)庫是一個(gè)結(jié)構(gòu)化的數(shù)據(jù)集合 D、數(shù)據(jù)庫是一組文件
10. 下列說法中,不屬于數(shù)據(jù)模型所描述的內(nèi)容的是__C____。
A、數(shù)據(jù)結(jié)構(gòu) B、數(shù)據(jù)操作 C、數(shù)據(jù)查詢 D、數(shù)據(jù)約束
11. 在下面的Visual FoxPro表達(dá)式中,運(yùn)算結(jié)果是邏輯真的是_D_____。
A、EMPTY(.NULL.) B、LIKE('acd','ac?') C、AT('a','123abc') D、EMPTY(SPACE(2))
12. 表達(dá)式VAL(SUBS("奔騰586",5,1))*Len("visual foxpro")的結(jié)果是__D____。
A、13.00 B、14.00 C、45.00 D、65.00
13. 以下關(guān)于自由表的敘述,正確的是__C____。
A、全部是用以前版本的FOXPRO(FOXBASE)建立的表
B、可以用Visual FoxPro建立,但是不能把它添加到數(shù)據(jù)庫中
C、自由表可以添加到數(shù)據(jù)庫中,數(shù)據(jù)庫表也可以從數(shù)據(jù)庫中移出成為自由表
D、自由表可以添加到數(shù)據(jù)庫中,但數(shù)據(jù)庫表不可從數(shù)據(jù)庫中移出成為自由表
14. 下面關(guān)于數(shù)據(jù)環(huán)境和數(shù)據(jù)環(huán)境中兩個(gè)表之間的關(guān)系的陳述中,_C_____是正確的。
A、數(shù)據(jù)環(huán)境是對(duì)象,關(guān)系不是對(duì)象 B、數(shù)據(jù)環(huán)境不是對(duì)象,關(guān)系是對(duì)象
C、數(shù)據(jù)環(huán)境是對(duì)象,關(guān)系是數(shù)據(jù)環(huán)境中的對(duì)象 D、數(shù)據(jù)環(huán)境和關(guān)系均不是對(duì)象
15. 在"報(bào)表設(shè)計(jì)器"中,可以使用的控件是__A____。
A、標(biāo)簽、域控件和線條 B、標(biāo)簽、域控件和列表框
C、標(biāo)簽、文本框和列表框 D、布局和數(shù)據(jù)源
16.用二維表數(shù)據(jù)來表示實(shí)體及實(shí)體之間聯(lián)系的數(shù)據(jù)模型稱為_D_____。
A、實(shí)體--聯(lián)系模型 B、層次模型 C、網(wǎng)狀模型 D、關(guān)系模型
17. 用來指明復(fù)選框的當(dāng)前選中狀態(tài)的屬性是___C___。A、Selected B、Caption C、Value D、ControlSource
18. 使用菜單操作方法打開一個(gè)在當(dāng)前目錄下已經(jīng)存在的查詢文件zgjk.qpr后,在命令窗口生成的命令是__B__。
A、OPEN QUERY zgjk.qpr B、MODIFY QUERY zgjk.qpr
C、DO QUERY zgjk.qpr D、CREATE QUERY zgjk.qpr
19. 可以伴隨著表的打開而自動(dòng)打開的索引是____C__。
A、單一索引文件(IDX) B、復(fù)合索引文件(CDX)C、結(jié)構(gòu)化復(fù)合索引文件 D、非結(jié)構(gòu)化復(fù)合索引文件
20. 在數(shù)據(jù)庫設(shè)計(jì)器中,建立兩個(gè)表之間的一對(duì)多聯(lián)系是通過以下索引實(shí)現(xiàn)的__A____。
A、"一方"表的主索引或候選索引,"多方"表的普通索引
B、"一方"表的主索引,"多方"表的普通索引或候選索引
C、"一方"表的普通索引,"多方"表的主索引或候選索引
D、"一方"表的普通索引,"多方"表的候選索引或普通索引
21. 下列函數(shù)中函數(shù)值為字符型的是___B___。 A、DATE() B、TIME() C、YEAR() D、DATETIME()
22. 下面對(duì)控件的描述正確的是___B___。
A、用戶可以在組合框中進(jìn)行多重選擇 B、用戶可以在列表框中進(jìn)行多重選擇
C、用戶可以在一個(gè)選項(xiàng)組中選中多個(gè)選項(xiàng)按鈕 D、用戶對(duì)一個(gè)表單內(nèi)的一組復(fù)選框只能選中其中一個(gè)
23. 確定列表框內(nèi)的某個(gè)條目是否被選定應(yīng)使用的屬性是___D___。
A、Value B、ColumnCount C、ListCount D、Selected
24. 設(shè)有關(guān)系R1和R2,經(jīng)過關(guān)系運(yùn)算得到結(jié)果S,則S是_A_____。
A、一個(gè)關(guān)系 B、一個(gè)表單 C、一個(gè)數(shù)據(jù)庫 D、一個(gè)數(shù)組
25. DBAS指的是___C___。A、數(shù)據(jù)庫管理系統(tǒng)B、數(shù)據(jù)庫系統(tǒng)C、數(shù)據(jù)庫應(yīng)用系統(tǒng) D、數(shù)據(jù)庫服務(wù)系統(tǒng)
26. 設(shè)X="ABC",Y="ABCD",則下列表達(dá)式中值為.T.的是__C____。A、X=Y B、X==Y C、X$Y D、AT(X,Y)=0
27. 在表結(jié)構(gòu)中,邏輯型、日期型、備注型字段的寬度分別固定為B___。
A、3,8,10 B、1,6,4 C、1,8,任意 D、1,8,4
28. 在標(biāo)準(zhǔn)SQL中,建立視圖的命令是___C___。
A、CREATE SCHEMA命令 B、CREATE TABLE命令 C、CREATE VIEW命令 D、CREATE INDEX命令
29. 有關(guān)SCAN循環(huán)結(jié)構(gòu),敘述正確的是__B____。
A、SCAN循環(huán)結(jié)構(gòu)中的LOOP語句,可將程序流程直接指向循環(huán)開始語句SCAN,首先判斷EOF()函數(shù)的真假
B、在使用SCAN循環(huán)結(jié)構(gòu)時(shí),必須打開某一個(gè)數(shù)據(jù)庫
C、SCAN循環(huán)結(jié)構(gòu)的循環(huán)體中必須寫有SKIP語句
D、SCAN循環(huán)結(jié)構(gòu),如果省略了
子句\FOR和WHILE條件子句,則直接退出循環(huán)
30. 設(shè)有圖書管理數(shù)據(jù)庫:
圖書(總編號(hào)C(6),分類號(hào)C(8),書名C(16),作者C(6),出版單位C(20),單價(jià)N(6,2))
讀者(借書證號(hào)C(4),單位C(8),姓名C(6),性別C(2),職稱C(6),地址C(20))
借閱(借書證號(hào)C(4),總編號(hào)C(6),借書日期D(8))
對(duì)于圖書管理數(shù)據(jù)庫,要查詢所藏圖書中,各個(gè)出版社的圖書最高單價(jià)、平均單價(jià)和冊(cè)數(shù),下面SQL語句正確的是__D____。
SELECT 出版單位,______,______,______;
FROM 圖書管理!圖書 ______ 出版單位
A、MIN(單價(jià)) AVGAGE(單價(jià)) COUNT(*) GROUP BY B、MAX(單價(jià)) AVG(單價(jià)) COUNT(*) ORDER BY
C、MAX(單價(jià)) AVG(單價(jià)) SUM(*) ORDER BY D、MAX(單價(jià)) AVG(單價(jià)) COUNT(*) GROUP BY
31. 設(shè)有圖書管理數(shù)據(jù)庫:
圖書(總編號(hào)C(6),分類號(hào)C(8),書名C(16),作者C(6),出版單位C(20),單價(jià)N(6,2))
讀者(借書證號(hào)C(4),單位C(8),姓名C(6),性別C(2),職稱C(6),地址C(20))
借閱(借書證號(hào)C(4),總編號(hào)C(6),借書日期D(8))
對(duì)于圖書管理數(shù)據(jù)庫,求CIE單位借閱圖書的讀者的人數(shù)。
下面SQL語句正確的是__A____。
SELECT ______ FROM 借閱 WHERE;
借書證號(hào) _______
A、COUNT (DISTINCT 借書證號(hào))
IN (SELECT 借書證號(hào) FROM 讀者 WHERE 單位="CIE")
B、COUNT (DISTINCT 借書證號(hào))
IN (SELECT 借書證號(hào) FROM 借閱 WHERE 單位="CIE")
C、SUM (DISTINCT 借書證號(hào))
IN (SELECT 借書證號(hào) FROM 讀者 WHERE 單位="CIE")
D、SUM (DISTINCT 借書證號(hào))
IN (SELECT 借書證號(hào) FOR 借閱 WHERE 單位="CIE")
32. 查詢訂購單號(hào)(字符型,長度為4)尾字符是"1"的錯(cuò)誤命令是__C____。
A、SELECT * FROM 訂單 WHERE SUBSTR(訂購單號(hào),4)="1"
B、SELECT * FROM 訂單 WHERE SUBSTR(訂購單號(hào),4,1)="1"
C、SELECT * FROM 訂單 WHERE "1"$訂購單號(hào)
D、SELECT * FROM 訂單 WHERE RIGHT(訂購單號(hào),1)="1"
33. 在關(guān)系模型中,為了實(shí)現(xiàn)"關(guān)系中不允許出現(xiàn)相同元組"的約束應(yīng)使用___B___。
A、臨時(shí)關(guān)鍵字 B、主關(guān)鍵字 C、外部關(guān)鍵字 D、索引關(guān)鍵字
34. 根據(jù)"職工"項(xiàng)目文件生成emp_sys.exe應(yīng)用程序的命令是__B____。
A、BUILD EXE emp_sys FROM 職工 B、BUILD APP emp_sys.exe FROM 職工
C、LIKE EXE emp_sys FROM 職工 D、LIKE APP emp_sys.exe FROM 職工
35. 當(dāng)前盤當(dāng)前目錄下有數(shù)據(jù)庫:學(xué)院.dbc,其中有"教師"表和"學(xué)院"表。
"教師"表:
"學(xué)院"表:
有SQL語句:
SELECT DISTINCT 系號(hào) FROM 教師 WHERE 工資>=;
ALL (SELECT 工資 FROM 教師 WHERE 系號(hào)="02")
與如上語句等價(jià)的SQL語句是___A___。
A、SELECT DISTINCT 系號(hào) FROM 教師 WHERE工資>=;
(SELECT MAX(工資) FROM 教師 WHERE 系號(hào)="02")
B、SELECT DISTINCT 系號(hào) FROM 教師 WHERE 工資>=;
(SELECT MIN(工資) FROM 教師 WHERE 系號(hào)="02")
C、SELECT DISTINCT 系號(hào) FROM 教師 WHERE 工資>=;
ANY(SELECT 工資 FROM 教師 WHERE 系號(hào)="02")
D、SELECT DISTINCT 系號(hào) FROM 教師 WHERE 工資>=;
SOME (SELECT 工資 FROM 教師 WHERE 系號(hào)="02")
二、 填空題
36. 若按功能劃分,軟件測(cè)試的方法通常分為白盒測(cè)試方法和__黑盒____測(cè)試方法。
37. 數(shù)據(jù)庫系統(tǒng)的三級(jí)模式分別為_概念或概念級(jí)_____模式、內(nèi)部級(jí)模式與外部級(jí)模式。
38. 在最壞情況下,冒泡排序的時(shí)間復(fù)雜度為__n(n-1)/2____。
39. 在面向?qū)ο蠓椒ㄖ校畔㈦[蔽是通過對(duì)象的___封裝___性來實(shí)現(xiàn)的。
40. 關(guān)系模型的數(shù)據(jù)操縱即是建立在關(guān)系上的數(shù)據(jù)操縱,一般有__查詢____、增加、刪除和修改四種操作。
41.要把幫助文件設(shè)置為復(fù)制到硬盤上的Foxhelp.chm文件,需要在"選項(xiàng)"對(duì)話框的__文件位置____選項(xiàng)卡上設(shè)置。
42. TIME( )的返回值的數(shù)據(jù)類型是__字符 或????C____類型。
43. 在定義字段有效性規(guī)則中,在規(guī)則框中輸入的表達(dá)式中類型是___邏輯表達(dá)式_____。
44. 設(shè)計(jì)報(bào)表通常包括兩部分內(nèi)容:_數(shù)據(jù)源_____和布局。
45. __內(nèi)部聯(lián)接____是指只有滿足聯(lián)接條件的記錄才包含在查詢結(jié)果中。
46. 設(shè)有圖書管理數(shù)據(jù)庫:
圖書(總編號(hào)C(6),分類號(hào)C(8),書名C(16),作者C(6),出版單位C(20),單價(jià)N(6,2))
讀者(借書證號(hào)C(4),單位C(8),姓名C(6),性別C(2),職稱C(6),地址C(20))
借閱(借書證號(hào)C(4),總編號(hào)C(6),借書日期D(8))
檢索書價(jià)在15元至25元(含15元和25元)之間的圖書的書名、作者、書價(jià)和分類號(hào),結(jié)果按分類號(hào)升序排序。
SELECT 書名,作者,單價(jià),分類號(hào) FROM 圖書;
WHERE______;
ORDER BY______;
47. 設(shè)有如下關(guān)系表R、S和T:
R(BH,XM,XB,DWH)
S(SWH,DWM)
T(BH,XM,XB,DWH)
實(shí)現(xiàn)R∪T的SQL語句是_______。
48. 設(shè)有如下關(guān)系表R:
R(NO,NAME,SEX,AGE,CLASS)
主關(guān)鍵字是NO
其中NO為學(xué)號(hào),NAME為姓名,SEX為性別,AGE為年齡,CLASS為班號(hào)。寫出實(shí)現(xiàn)下列功能的SQL語句。
插入"95031"班學(xué)號(hào)為30,姓名為"鄭和"的學(xué)生記錄;_______。
49. 設(shè)有如下關(guān)系表R:
R(NO,NAME,SEX,AGE,CLASS)
主關(guān)鍵字是NO
其中NO為學(xué)號(hào)(數(shù)值型),NAME為姓名,SEX為性別,AGE為年齡,CLASS為班號(hào)。寫出實(shí)現(xiàn)下列功能的SQL語句。
刪除學(xué)號(hào)為20的學(xué)生記錄;______。
第二套題答案
1-5 CBBDD 6-10 CBBCC 11-15 DDCCA 16-20 DCBCA 21-25 BBDAC 26-30 CDCBD 31-35 ACBBA
36.黑盒 37.概念或概念級(jí) 38.n(n-1)/2 39.封裝 40.查詢 41.文件位置 42.字符 或 C 43.邏輯表達(dá)式
44.數(shù)據(jù)源 45.內(nèi)部聯(lián)接
46. 單價(jià) BETWEEN 15 AND 25 或 單價(jià) BETW 15 AND 25 或 單價(jià) BETWE 15 AND 25 或 單價(jià)>=15 and 單價(jià)<=25 或 單價(jià)>=15 and 單價(jià)=<25 或 單價(jià)=>15 and 單價(jià)<=25 或 單價(jià)=>15 and 單價(jià)=<25 與 分類號(hào) ASC 或 分類號(hào)
47. SELECT * FROM R UNION SELECT * FROM T 或 SELE * FROM R UNIO SELE * FROM T 或 SELECT * FROM R UNIO SELECT * FROM T 或 SELE * FROM R UNION SELE * FROM T
48. INSERT INTO R(NO,NAME,CLASS) VALUES(30,"鄭和","95031") 或 INSE INTO R(NO,NAME,CLASS) VALUES(30,"鄭和","95031")
49. DELETE FROM R WHERE NO=20 或 DELE FROM R WHERE NO=20 或 DELE FROM R WHER NO=20 或 DELETE FROM R WHER NO=20
[本講的知識(shí)要點(diǎn)]:JDBC、JDBC的工作原理,訪問數(shù)據(jù)庫的方法、Statement、PreparedStatement、CallableStatement,ResultSet等對(duì)象的編程使用
9.1 基本知識(shí)
9.1.1 JDBC:Java DataBase Connectivity(Java 數(shù)據(jù)庫連接技術(shù)),它是將Java與SQL結(jié)合且獨(dú)立于特定的數(shù)據(jù)庫系統(tǒng)的應(yīng)用程序編程接口(API--它是一種可用于執(zhí)行SQL語句的Java API,即由一組用Java語言編寫的類與接口所組成)。
? 有了JDBC從而可以使Java程序員用Java語言來編寫完整的數(shù)據(jù)庫方面的應(yīng)用程序。另外也可以操作保存在多種不同的數(shù)據(jù)庫管理系統(tǒng)中的數(shù)據(jù),而與數(shù)據(jù)庫管理系統(tǒng)中數(shù)據(jù)存儲(chǔ)格式無關(guān)。同時(shí)Java語言的與平臺(tái)的無關(guān)性,不必在不同的系統(tǒng)平臺(tái)下編寫不同的數(shù)據(jù)庫應(yīng)用程序。
9.1.2 JDBC設(shè)計(jì)的目的
(1)ODBC:微軟的ODBC是用C編寫的,而且只適用于Windows平臺(tái),無法實(shí)現(xiàn)跨平臺(tái)地操作數(shù)據(jù)庫。
(2)SQL語言:SQL盡管包含有數(shù)據(jù)定義、數(shù)據(jù)操作、數(shù)據(jù)管理等功能,但它并不是一個(gè)完整的編程語言,而且不支持流控制,需要與其它編程語言相配合使用。
(3)JDBC的設(shè)計(jì):由于Java語言具有健壯性、安全、易使用并自動(dòng)下載到網(wǎng)絡(luò)等方面的優(yōu)點(diǎn),因此如果采用Java語言來連接數(shù)據(jù)庫,將能克服ODBC局限于某一系統(tǒng)平臺(tái)的缺陷;將SQL語言與Java語言相互結(jié)合起來,可以實(shí)現(xiàn)連接不同數(shù)據(jù)庫系統(tǒng),即使用JDBC可以很容易地把SQL語句傳送到任何關(guān)系型數(shù)據(jù)庫中。
(4)JDBC設(shè)計(jì)的目的:它是一種規(guī)范,設(shè)計(jì)出它的最主要的目的是讓各個(gè)數(shù)據(jù)庫開發(fā)商為Java程序員提供標(biāo)準(zhǔn)的數(shù)據(jù)庫訪問類和接口,使得獨(dú)立于DBMS的Java應(yīng)用程序的開發(fā)成為可能(數(shù)據(jù)庫改變,驅(qū)動(dòng)程序跟著改變,但應(yīng)用程序不變)。
9.1.3 JDBC的主要功能:(1)創(chuàng)建與數(shù)據(jù)庫的連接;(2)發(fā)送SQL語句到任何關(guān)系型數(shù)據(jù)庫中;(3)處理數(shù)據(jù)并查詢結(jié)果。
編程實(shí)例:
try
{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //(1)創(chuàng)建與數(shù)據(jù)庫的連接
? Connection con=DriverManager.getConnection("jdbc:odbc:DatabaseDSN","Login","Password");
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("select * from DBTableName");//(2)發(fā)送SQL語句到數(shù)據(jù)庫中 ? ? ? ? ? ? ?
while(rs.next())
{ String name=rs.getString("Name") ; ? ? ? ? //(3)處理數(shù)據(jù)并查詢結(jié)果。
? int age=rs.getInt("age");
? float wage=rs.getFloat("wage");
}
rs.close(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //(4)關(guān)閉
stmt.close();
con.close();
}
catch(SQLException e)
{ ? System.out.println("SQLState:"+ e.getSQLState());
? System.out.println("Message:" + e.getMessage());
? System.out.println("Vendor:" + e.getErrorCode());
}
9.1.4 JDBC與ODBC的對(duì)比,從而體會(huì)JDBC的特點(diǎn)
(1)ODBC是用C語言編寫的,不是面向?qū)ο蟮模欢鳭DBC是用Java編寫的,是面向?qū)ο蟮摹?br />
(2)ODBC難以學(xué)習(xí),因?yàn)樗押唵蔚墓δ芘c高級(jí)功能組合在一起,即便是簡單的查詢也會(huì)帶有復(fù)雜的任選項(xiàng);而JDBC的設(shè)計(jì)使得簡單的事情用簡單的做法來完成。
(3)ODBC是局限于某一系統(tǒng)平臺(tái)的,而JDBC提供Java與平臺(tái)無關(guān)的解決方案。
(4)但也可以通過Java來操作ODBC,這可以采用JDBc-ODBC橋接方式來實(shí)現(xiàn)(因?yàn)镴ava不能直接使用ODBC,即在Java中使用本地C的代碼將帶來安全缺陷)。
9.1.5 JDBC驅(qū)動(dòng)程序的類型: 目前比較常見的JDBC驅(qū)動(dòng)程序可分為以下四個(gè)種類:
(1)JDBC-ODBC橋加ODBC驅(qū)動(dòng)程序
JavaSoft橋產(chǎn)品利用ODBC驅(qū)動(dòng)程序提供JDBC訪問。注意,必須將ODBC二進(jìn)制代碼(許多情況下還包括數(shù)據(jù)庫客戶機(jī)代碼)加載到使用該驅(qū)動(dòng)程序的每個(gè)客戶機(jī)上。因此,這種類型的驅(qū)動(dòng)程序最適合于企業(yè)網(wǎng)(這種網(wǎng)絡(luò)上客戶機(jī)的安裝不是主要問題),或者是用Java編寫的三層結(jié)構(gòu)的應(yīng)用程序服務(wù)器代碼。
JDBC-ODBC 橋接方式利用微軟的開放數(shù)據(jù)庫互連接口(ODBC API)同數(shù)據(jù)庫服務(wù)器通訊,客戶端計(jì)算機(jī)首先應(yīng)該安裝并配置ODBC driver 和JDBC-ODBC bridge兩種驅(qū)動(dòng)程序。
(2)本地API
這種類型的驅(qū)動(dòng)程序把客戶機(jī)API上的JDBC調(diào)用轉(zhuǎn)換為Oracle、Sybase、Informix、DB2或其它DBMS的調(diào)用。注意,象橋驅(qū)動(dòng)程序一樣,這種類型的驅(qū)動(dòng)程序要求將某些二進(jìn)制代碼加載到每臺(tái)客戶機(jī)上。
這種驅(qū)動(dòng)方式將數(shù)據(jù)庫廠商的特殊協(xié)議轉(zhuǎn)換成Java代碼及二進(jìn)制類碼,使Java 數(shù)據(jù)庫客戶方與數(shù)據(jù)庫服務(wù)器方通信。例如:Oracle用SQLNet協(xié)議,DB2用IBM 的數(shù)據(jù)庫協(xié)議。數(shù)據(jù)庫廠商的特殊協(xié)議也應(yīng)該被安裝在客戶機(jī)上。
(3)JDBC網(wǎng)絡(luò)純Java驅(qū)動(dòng)程序
這種驅(qū)動(dòng)程序?qū)DBC轉(zhuǎn)換為與DBMS無關(guān)的網(wǎng)絡(luò)協(xié)議,之后這種協(xié)議又被某個(gè)服務(wù)器轉(zhuǎn)換為一種DBMS協(xié)議。這種網(wǎng)絡(luò)服務(wù)器中間件能夠?qū)⑺募僇ava客戶機(jī)連接到多種不同的數(shù)據(jù)庫上。所用的具體協(xié)議取決于提供者。通常,這是最為靈活的JDBC驅(qū)動(dòng)程序。有可能所有這種解決方案的提供者都提供適合于Intranet用的產(chǎn)品。為了使這些產(chǎn)品也支持Internet訪問,它們必須處理Web所提出的安全性、通過防火墻的訪問等方面的額外要求。幾家提供者正將JDBC驅(qū)動(dòng)程序加到他們現(xiàn)有的數(shù)據(jù)庫中間件產(chǎn)品中。
這種方式是純Java driver。數(shù)據(jù)庫客戶以標(biāo)準(zhǔn)網(wǎng)絡(luò)協(xié)議(如HTTP、SHTTP)同數(shù)據(jù)庫訪問服務(wù)器通信,數(shù)據(jù)庫訪問服務(wù)器然后翻譯標(biāo)準(zhǔn)網(wǎng)絡(luò)協(xié)議成為數(shù)據(jù)庫廠商的專有特殊數(shù)據(jù)庫訪問協(xié)議(也可能用到ODBC driver)與數(shù)據(jù)庫通信。對(duì)Internet 和Intranet 用戶而言這是一個(gè)理想的解決方案。Java driver 被自動(dòng)的,以透明的方式隨Applets自Web服務(wù)器而下載并安裝在用戶的計(jì)算機(jī)上。
(4)本地協(xié)議純Java驅(qū)動(dòng)程序
這種類型的驅(qū)動(dòng)程序?qū)DBC調(diào)用直接轉(zhuǎn)換為DBMS所使用的網(wǎng)絡(luò)協(xié)議。這將允許從客戶機(jī)機(jī)器上直接調(diào)用DBMS服務(wù)器,是Intranet訪問的一個(gè)很實(shí)用的解決方法。
這種方式也是純Java driver。數(shù)據(jù)庫廠商提供了特殊的JDBC協(xié)議使Java數(shù)據(jù)庫客戶與數(shù)據(jù)庫服務(wù)器通信。然而,將把代理協(xié)議同數(shù)據(jù)庫服務(wù)器通信改用數(shù)據(jù)庫廠商的特殊JDBC driver。這對(duì)Intranet 應(yīng)用是高效的,可是數(shù)據(jù)庫廠商的協(xié)議可能不被防火墻支持,缺乏防火墻支持在Internet 應(yīng)用中會(huì)存在潛在的安全隱患。
9.2 JDBC的工作原理
? JDBC的設(shè)計(jì)基于X/Open SQL CLI(調(diào)用級(jí)接口)這一模型。它通過定義出一組 API對(duì)象和方法以用于同數(shù)據(jù)庫進(jìn)行交互。
在Java程序中要操作數(shù)據(jù)庫,一般應(yīng)該通過如下幾步(利用JDBC訪問數(shù)據(jù)庫的編程步驟):
(1)加載連接數(shù)據(jù)庫的驅(qū)動(dòng)程序 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
(2)創(chuàng)建與數(shù)據(jù)源的連接
String url="jdbc:odbc:DatabaseDSN";
Connection con=DriverManager.getConnection(url,"Login","Password");
(3)查詢數(shù)據(jù)庫:創(chuàng)建Statement對(duì)象并執(zhí)行SQL語句以返回一個(gè)ResultSet對(duì)象。
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("select * from DBTableName");
(4)獲得當(dāng)前記錄集中的某一記錄的各個(gè)字段的值
? String name=rs.getString("Name");
? int age=rs.getInt("age");
? float wage=rs.getFloat("wage");
(5)關(guān)閉查詢語句及與數(shù)據(jù)庫的連接(注意關(guān)閉的順序先rs再stmt最后為con)
? rs.close(); ?
? stmt.close();
? con.close();
9.3 JDBC的結(jié)構(gòu)
? JDBC主要包含兩部分:面向Java程序員的JDBC API及面向數(shù)據(jù)庫廠商的JDBC Drive API。
(1)面向Java程序員的JDBC API:Java程序員通過調(diào)用此API從而實(shí)現(xiàn)連接數(shù)據(jù)庫、執(zhí)行SQL語句并返回結(jié)果集等編程數(shù)據(jù)庫的能力,它主要是由一系列的接口定義所構(gòu)成。
java.sql.DriveManager:該接口主要定義了用來處理裝載驅(qū)動(dòng)程序并且為創(chuàng)建新的數(shù)據(jù)庫連接提供支持。
java.sql.Connection:該接口主要定義了實(shí)現(xiàn)對(duì)某一種指定數(shù)據(jù)庫連接的功能。
java.sql.Statement:該接口主要定義了在一個(gè)給定的連接中作為SQL語句執(zhí)行聲明的容器以實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的操作。它主要包含有如下的兩種子類型。
? java.sql.PreparedStatement:該接口主要定義了用于執(zhí)行帶或不帶 IN 參數(shù)的預(yù)編譯 SQL 語句。
? java.sql.CallableStatement:該接口主要定義了用于執(zhí)行數(shù)據(jù)庫的存儲(chǔ)過程的雕用。
java.sql.ResultSet:該接口主要定義了用于執(zhí)行對(duì)數(shù)據(jù)庫的操作所返回的結(jié)果集。
(2)面向數(shù)據(jù)庫廠商的JDBC Drive API:數(shù)據(jù)庫廠商必須提供相應(yīng)的驅(qū)動(dòng)程序并實(shí)現(xiàn)JDBC API所要求的基本接口(每個(gè)數(shù)據(jù)庫系統(tǒng)廠商必須提供對(duì)DriveManager、Connection、Statement、ResultSet等接口的具體實(shí)現(xiàn)),從而最終保證Java程序員通過JDBC實(shí)現(xiàn)對(duì)不同的數(shù)據(jù)庫操作。
9.4 數(shù)據(jù)庫應(yīng)用的模型
(1)兩層結(jié)構(gòu)(C/S):在此模型下,客戶端的程序直接與數(shù)據(jù)庫服務(wù)器相連接并發(fā)送SQL語句(但這時(shí)就需要在客戶端安裝被訪問的數(shù)據(jù)庫的JDBC驅(qū)動(dòng)程序),DBMS服務(wù)器向客戶返回相應(yīng)的結(jié)果,客戶程序負(fù)責(zé)對(duì)數(shù)據(jù)的格式化。
client端 ? ? ? ODBC/JDBC ? ? ? ? Server端(DBMS)
或數(shù)據(jù)庫專用協(xié)議 ?
主要的缺點(diǎn):受數(shù)據(jù)庫廠商的限制,用戶更換數(shù)據(jù)庫時(shí)需要改寫客戶程序;受數(shù)據(jù)庫版本的限制,數(shù)據(jù)庫廠商一旦升級(jí)數(shù)據(jù)庫,使用該數(shù)據(jù)庫的客戶程序需要重新編譯和發(fā)布;對(duì)數(shù)據(jù)庫的操作與處理都是在客戶程序中實(shí)現(xiàn),使客戶程序在編程與設(shè)計(jì)時(shí)較為復(fù)雜。
(2)三(或多)層結(jié)構(gòu)(B/S):在此模型下,主要在客戶端的程序與數(shù)據(jù)庫服務(wù)器之間增加了一個(gè)中間服務(wù)器(可以采用C++或Java語言來編程實(shí)現(xiàn)),隔離客戶端的程序與數(shù)據(jù)庫服務(wù)器。客戶端的程序(可以簡單為通用的瀏覽器)與中間服務(wù)器進(jìn)行通信,然后由中間服務(wù)器處理客戶端程序的請(qǐng)求并管理與數(shù)據(jù)庫服務(wù)器的連接。
客戶端程序 HTTP RMI CORBA 中間服務(wù)器 ? JDBC ? 數(shù)據(jù)庫服務(wù)器
9.5 通過JDBC 實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的訪問
(1)引用必要的包
import java.sql.*; //它包含有操作數(shù)據(jù)庫的各個(gè)類與接口 ?
(2)加載連接數(shù)據(jù)庫的驅(qū)動(dòng)程序類 ?
? 為實(shí)現(xiàn)與特定的數(shù)據(jù)庫相連接,JDBC必須加載相應(yīng)的驅(qū)動(dòng)程序類。這通常可以采用Class.forName()方法顯式地加載一個(gè)驅(qū)動(dòng)程序類,由驅(qū)動(dòng)程序負(fù)責(zé)向DriverManager登記注冊(cè)并在與數(shù)據(jù)庫相連接時(shí),DriverManager將使用此驅(qū)動(dòng)程序。
? ? Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
注意:這條語句直接加載了sun公司提供的JDBC-ODBC Bridge驅(qū)動(dòng)程序類。
(3)創(chuàng)建與數(shù)據(jù)源的連接
String url="jdbc:odbc:DatabaseDSN";
Connection con=DriverManager.getConnection(url,"Login","Password");
注意:采用DriverManager類中的getConnection()方法實(shí)現(xiàn)與url所指定的數(shù)據(jù)源建立連接并返回一個(gè)Connection類的對(duì)象,以后對(duì)這個(gè)數(shù)據(jù)源的操作都是基于該Connection類對(duì)象;但對(duì)于Access等小型數(shù)據(jù)庫,可以不用給出用戶名與密碼。
String url="jdbc:odbc:DatabaseDSN";
Connection con=DriverManager.getConnection(url);
System.out.println(con.getCatalog()); //取得數(shù)據(jù)庫的完整路徑及文件名
? JDBC借用了url語法來確定全球的數(shù)據(jù)庫(數(shù)據(jù)庫URL類似于通用的URL),對(duì)由url所指定的數(shù)據(jù)源的表示格式為
? jdbc::[ database locator]
jdbc---指出要使用JDBC
subprotocal---定義驅(qū)動(dòng)程序類型
database locator---提供網(wǎng)絡(luò)數(shù)據(jù)庫的位置和端口號(hào)(包括主機(jī)名、端口和數(shù)據(jù)庫系統(tǒng)名等) ? jdbc:odbc://host.domain.com:port/databasefile ?
主協(xié)議jdbc ? 驅(qū)動(dòng)程序類型為odbc,它指明JDBC管理器如何訪問數(shù)據(jù)庫,該例指名為采用JDBC-ODBC橋接方式;其它為數(shù)據(jù)庫的位置表示。 ?
例如:裝載mySQL JDBC驅(qū)動(dòng)程序
Class.forName("org.gjt.mm.mysql.Driver ");
String url
="jdbc:mysql://localhost/softforum?user=soft&password=soft1234&useUnicode=true&characterEncoding=8859_1"
//testDB為你的數(shù)據(jù)庫名
Connection conn= DriverManager.getConnection(url);
例如:裝載Oracle JDBC OCI驅(qū)動(dòng)程序(用thin模式)
Class.forName("oracle.jdbc.driver.OracleDriver ");
String url="jdbc:oracle:thin:@localhost:1521:orcl";
//orcl為你的數(shù)據(jù)庫的SID
String user="scott";
String password="tiger";
Connection conn= DriverManager.getConnection(url,user,password);
注意:也可以通過con.setCatalog("MyDatabase")來加載數(shù)據(jù)庫。
例如:裝載DB2驅(qū)動(dòng)程序
Class.forName("com.ibm.db2.jdbc.app.DB2Driver ")
String url="jdbc:db2://localhost:5000/sample";
//sample為你的數(shù)據(jù)庫名
String user="admin";
String password="";
Connection conn= DriverManager.getConnection(url,user,password);
例如:裝載MicroSoft SQLServer驅(qū)動(dòng)程序
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver ");
String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=pubs";
//pubs為你的數(shù)據(jù)庫的
String user="sa";
String password="";
Connection conn= DriverManager.getConnection(url,user,password);
(4)查詢數(shù)據(jù)庫的一些結(jié)構(gòu)信息
? 這主要是獲得數(shù)據(jù)庫中的各個(gè)表,各個(gè)列及數(shù)據(jù)類型和存儲(chǔ)過程等各方面的信息。根據(jù)這些信息,從而可以訪問一個(gè)未知結(jié)構(gòu)的數(shù)據(jù)庫。這主要是通過DatabaseMetaData類的對(duì)象來實(shí)現(xiàn)并調(diào)用其中的方法來獲得數(shù)據(jù)庫的詳細(xì)信息(即數(shù)據(jù)庫的基本信息,數(shù)據(jù)庫中的各個(gè)表的情況,表中的各個(gè)列的信息及索引方面的信息)。
? DatabaseMetaData dbms=con.getMetaData();
? System.out.println("數(shù)據(jù)庫的驅(qū)動(dòng)程序?yàn)?"+dbms.getDriverName());
(5)查詢數(shù)據(jù)庫中的數(shù)據(jù):
? 在JDBC中查詢數(shù)據(jù)庫中的數(shù)據(jù)的執(zhí)行方法可以分為三種類型,分別對(duì)應(yīng)Statement (用于執(zhí)行不帶參數(shù)的簡單SQL語句字符串),PreparedStatement(預(yù)編譯SQL語句)和CallableStatement(主要用于執(zhí)行存儲(chǔ)過程)三個(gè)接口。
9.5.1、實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的一般查詢Statement
1、創(chuàng)建Statement對(duì)象(要想執(zhí)行一個(gè)SQL查詢語句,必須首先創(chuàng)建出Statement對(duì)象,它封裝代表要執(zhí)行的SQL語句)并執(zhí)行SQL語句以返回一個(gè)ResultSet對(duì)象,這可以通過Connection類中的createStatement()方法來實(shí)現(xiàn)。
? Statement stmt=con.createStatement();
2、執(zhí)行一個(gè)SQL查詢語句,以查詢數(shù)據(jù)庫中的數(shù)據(jù)。Statement接口提供了三種執(zhí)行SQL語句的方法:executeQuery()、executeUpdate() 和execute()。具體使用哪一個(gè)方法由SQL語句本身來決定。
l ? ? 方法 executeQuery 用于產(chǎn)生單個(gè)結(jié)果集的語句,例如 SELECT 語句等。
l ? ? 方法 executeUpdate 用于執(zhí)行INSERT、UPDATE或DELETE 語句以及SQL DDL(數(shù)據(jù)定義語言)語句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或DELETE 語句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一個(gè)整數(shù),指示受影響的行數(shù)(即更新計(jì)數(shù))。對(duì)于 CREATE TABLE 或DROP TABLE 等不操作行的語句,executeUpdate 的返回值總為零。
l ? ? ? 方法 execute 用于執(zhí)行返回多個(gè)結(jié)果集、多個(gè)更新計(jì)數(shù)或二者組合的語句。一般不會(huì)需要該高級(jí)功能。
下面給出通過Statement類中的executeQuery()方法來實(shí)現(xiàn)的代碼段。executeQuery()方法的輸入?yún)?shù)是一個(gè)標(biāo)準(zhǔn)的SQL查詢語句,其返回值是一個(gè)ResultSet類的對(duì)象。
ResultSet rs=stmt. executeQuery ("select * from DBTableName"); ? ? ?
要點(diǎn):①JDBC在編譯時(shí)并不對(duì)將要執(zhí)行的SQL查詢語句作任何檢查,只是將其作為一個(gè)String類對(duì)象,直到驅(qū)動(dòng)程序執(zhí)行SQL查詢語句時(shí)才知道其是否正確。對(duì)于錯(cuò)誤的SQL查詢語句,在執(zhí)行時(shí)將會(huì)產(chǎn)生 SQLException。
? ? ②一個(gè)Statement對(duì)象在同一時(shí)間只能打開一個(gè)結(jié)果集,對(duì)第二個(gè)結(jié)果集的打開隱含著對(duì)第一個(gè)結(jié)果集的關(guān)閉。
? ? ③如果想對(duì)多個(gè)結(jié)果集同時(shí)操作,必須創(chuàng)建出多個(gè)Statement對(duì)象,在每個(gè)Statement對(duì)象上執(zhí)行SQL查詢語句以獲得相應(yīng)的結(jié)果集。
? ? ④如果不需要同時(shí)處理多個(gè)結(jié)果集,則可以在一個(gè)Statement對(duì)象上順序執(zhí)行多個(gè)SQL查詢語句,對(duì)獲得的結(jié)果集進(jìn)行順序操作。
import java.sql.*;
public class ResultSetTest
{ ? public static void main(String args[])
? ? { ? ? try
? ? ? ? ? { ?
? ? ? ? ? ? Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
? ? ? ? ? ? Connection con=DriverManager.getConnection("jdbc:odbc:studlist");
? ? ? ? ? ? Statement stmt=con.createStatement();
? ? ? ? ? ? ResultSet rs1=stmt.executeQuery("select name from student");
? ? ? ? ? ? ResultSet rs2=stmt.executeQuery("select age from student");
//此時(shí)rs1已經(jīng)被關(guān)閉 ? ? ? ? ? ?
? ? ? ? ? ? while(rs2.next())
? ? ? ? ? ? { ?
? ? ? ? ? ? ? System.out.println(rs2.getObject(1));
? ? ? ? ? ? }
? ? ? ? ? ? rs2.close();
? ? ? ? ? ? stmt.close();
? ? ? ? ? ? con.close();
? ? ? ? ? }
? ? ? ? ? catch(Exception e)
? ? ? ? ? {
? ? ? ? ? ? System.out.println(e);
? ? ? ? ? } ?
? }
}
注意:
此時(shí)顯示出的將是姓名還是年齡?(將顯示的是rs2的結(jié)果集的內(nèi)容,即學(xué)生的年齡,因?yàn)椴捎肑DBC-ODBC方式的驅(qū)動(dòng)程序時(shí),并且是采用同一個(gè)Statement對(duì)象,它只會(huì)保留最新的結(jié)果集,rs1中的內(nèi)容將會(huì)被新的結(jié)果集所取代)。
3、 關(guān)閉Statement對(duì)象:每一個(gè)Statement對(duì)象在使用完畢后,都應(yīng)該關(guān)閉。
? stmt.close();
9.5.2、預(yù)編譯方式執(zhí)行SQL語句PreparedStatement
? 由于Statement對(duì)象在每次執(zhí)行SQL語句時(shí)都將該語句傳給數(shù)據(jù)庫,如果需要多次執(zhí)行同一條SQL語句時(shí),這樣將導(dǎo)致執(zhí)行效率特別低,此時(shí)可以采用PreparedStatement對(duì)象來封裝SQL語句。如果數(shù)據(jù)庫支持預(yù)編譯,它可以將SQL語句傳給數(shù)據(jù)庫作預(yù)編譯,以后每次執(zhí)行該SQL語句時(shí),可以提高訪問速度;但如果數(shù)據(jù)庫不支持預(yù)編譯,將在語句執(zhí)行時(shí)才傳給數(shù)據(jù)庫,其效果類同于Statement對(duì)象。
? 另外PreparedStatement對(duì)象的SQL語句還可以接收參數(shù),可以用不同的輸入?yún)?shù)來多次執(zhí)行編譯過的語句,較Statement靈活方便(詳見后文介紹)。
1、 創(chuàng)建PreparedStatement對(duì)象:從一個(gè)Connection對(duì)象上可以創(chuàng)建一個(gè)PreparedStatement對(duì)象,在創(chuàng)建時(shí)可以給出預(yù)編譯的SQL語句。
? PreparedStatement pstmt=con.prepareStatement("select * from DBTableName");
2、 執(zhí)行SQL語句:可以調(diào)用executeQuery()來實(shí)現(xiàn),但與Statement方式不同的是,它沒有參數(shù),因?yàn)樵趧?chuàng)建PreparedStatement對(duì)象時(shí)已經(jīng)給出了要執(zhí)行的SQL語句,系統(tǒng)并進(jìn)行了預(yù)編譯。
? ResultSet rs=pstmt.executeQuery(); // 該條語句可以被多次執(zhí)行
3、關(guān)閉PreparedStatement
? pstmt.close(); //其實(shí)是調(diào)用了父類Statement類中的close()方法
9.5.3、執(zhí)行存儲(chǔ)過程CallableStatement
? CallableStatement類是PreparedStatement類的子類,因此可以使用在PreparedStatement類及Statement類中的方法,主要用于執(zhí)行存儲(chǔ)過程。
1、 創(chuàng)建CallableStatement對(duì)象:使用Connection類中的prepareCall方法可以創(chuàng)建一個(gè)CallableStatement對(duì)象,其參數(shù)是一個(gè)String對(duì)象,一般格式為:
l ? ? ? 不帶輸入?yún)?shù)的存儲(chǔ)過程“{call 存儲(chǔ)過程名()}”。
l ? ? 帶輸入?yún)?shù)的存儲(chǔ)過程“{call存儲(chǔ)過程名(?, ?)}”
l ? ? ? 帶輸入?yún)?shù)并有返回結(jié)果參數(shù)的存儲(chǔ)過程“{? = call 存儲(chǔ)過程名(?, ?, ...)}”
? CallableStatement cstmt=con.prepareCall("{call Query1()}");
2、 執(zhí)行存儲(chǔ)過程:可以調(diào)用executeQuery()方法來實(shí)現(xiàn)。
? ResultSet rs=cstmt.executeQuery(); ?
3、關(guān)閉CallableStatement
? cstmt.close(); //其實(shí)是調(diào)用了父類Statement類中的close()方法
(6)檢索記錄集以獲得當(dāng)前記錄集中的某一記錄的各個(gè)字段的值
9.5.4、ResultSet對(duì)象:
? ① 執(zhí)行完畢SQL語句后,將返回一個(gè)ResultSet類的對(duì)象,它包含所有的查詢結(jié)果。但對(duì)ResultSet類的對(duì)象方式依賴于光標(biāo)(Cursor)的類型,而對(duì)每一行中的各個(gè)列,可以按任何順序進(jìn)行處理(當(dāng)然,如果按從左到右的順序?qū)Ω髁羞M(jìn)行處理可以獲得較高的執(zhí)行效率);
ResultSet類中的Course方式主要有:
ResultSet.TYPE_FORWARD_ONLY(為缺省設(shè)置):光標(biāo)只能前進(jìn)不能后退,也就是只能從第一個(gè)一直移動(dòng)到最后一個(gè)。
ResultSet.TYPE_SCROLL_SENSITIVE:允許光標(biāo)前進(jìn)或后退并感應(yīng)到其它ResultSet的光標(biāo)的移動(dòng)情形。
ResultSet.TYPE_SCROLL_INSENSITIVE:允許光標(biāo)前進(jìn)或后退并不能感應(yīng)到其它ResultSet的光標(biāo)的移動(dòng)情形。
ResultSet類中的數(shù)據(jù)是否允許修改主要有:
ResultSet.CONCUR_READ_ONLY(為缺省設(shè)置):表示數(shù)據(jù)只能只讀,不能更改。
ResultSet.CONCUR_UPDATABLE:表示數(shù)據(jù)允許被修改。
? 可以在創(chuàng)建Statement或PreparedStatement對(duì)象時(shí)指定ResultSet的這兩個(gè)特性。
Statement stmt=con.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);
或
PreparedStatement pstmt=con.PrepareStatement("insert into bookTable values (?,?,?)",ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
② ResultSet類的對(duì)象維持一個(gè)指向當(dāng)前行的指針,利用ResultSet類的next()方法可以移動(dòng)到下一行(在JDBC中,Java程序一次只能看到一行數(shù)據(jù)),如果next()的返回值為false,則說明已到記錄集的尾部。另外JDBC也沒有類似ODBC 的書簽功能的方法。
? ③ 利用ResultSet類的getXXX()方法可以獲得某一列的結(jié)果,其中XXX代表JDBC中的Java數(shù)據(jù)類型,如 getInt()、getString()、getDate()等。訪問時(shí)需要指定要檢索的列(可以采用 int值作為列號(hào)(從1開始計(jì)數(shù))或指定列(字段)名方式,但字段名不區(qū)別字母的大小寫)。
while(rs.next())
{ String name=rs.getString("Name"); //采用“列名”的方式訪問數(shù)據(jù)
? int age=rs.getInt("age");
? float wage=rs.getFloat("wage");
? String homeAddress=rs.getString(4); //采用“列號(hào)”的方式訪問數(shù)據(jù)
}
9.5.5、數(shù)據(jù)轉(zhuǎn)換
? 利用ResultSet類的getXXX()方法可以實(shí)現(xiàn)將ResultSet中的SQL數(shù)據(jù)類型轉(zhuǎn)換為它所返回的Java數(shù)據(jù)類型。
9.5.6、NULL結(jié)果值
要確定給定結(jié)果值是否是JDBC NULL,必須先讀取該列,然后使用ResultSet.wasNull
方法檢查該次讀取是否返回JDBC NULL。
當(dāng)使用ResultSet.getXXX方法讀取JDBC NULL時(shí),方法wasNull將返回下列值之一:
(1)Javanull值
對(duì)于返回Java對(duì)象的getXXX方法(例如getString、getBigDecimal、getBytes、getDate、getTime、getTimestamp、getAsciiStream、getUnicodeStream、getBinaryStream、getObject等)。
(2)零值:對(duì)于getByte、getShort、getInt、getLong、getFloat和getDouble。
(3)false值:對(duì)于getBoolean
9.5.6、獲得結(jié)果集中的結(jié)構(gòu)信息:利用ResultSet類的getMetaData()方法來獲得結(jié)果集中的一些結(jié)構(gòu)信息(主要提供用來描述列的數(shù)量、列的名稱、列的數(shù)據(jù)類型。利用ResulSetMetaData類中的方法)。
ResultsetMetaData rsmd=rs.getMetaData();
rsmd.getColumnCount(); ? //返回結(jié)果集中的列數(shù) ? ? ? ?
rsmd.getColumnLabel(1); //返回第一列的列名(字段名)
例如:
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("select * from TableName");
for(int i=1; i<=rs.getMetaData().getColumnCount(); i++) ? //跟蹤顯示各個(gè)列的名稱
? ? { ? ? System.out.print(rs. getColumnName (i)+"\t");
? ? }
while(rs.next())
{ //跟蹤顯示各個(gè)列的值
? for(int j=1; j<=rs.getMetaData().getColumnCount(); j++)
? ? { ? ? System.out.print(rs.getObject(j)+"\t");
? ? }
}
9.6、更新數(shù)據(jù)庫
? 前面主要介紹如何實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的查詢操作,但在許多應(yīng)用中需要實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的更新,這主要涉及修改、插入和刪除等(即SQL語句中的Insert、Update、Delete、Creat、Drap等)。仍然通過創(chuàng)建Statement對(duì)象來實(shí)現(xiàn),但不再調(diào)用executeQuery()方法,而是使用executeUpdate()方法。
要點(diǎn)F:正確區(qū)分Statement類中的executeQuery()、execute()和executeUpdate()方法的用法:(1)
executeQuery() 執(zhí)行一般的SQL查詢語句(即SELECT語句)并返回Resultset對(duì)象;(2)execute()可以執(zhí)行各種SQL查詢語句,并可能返回多個(gè)結(jié)果集(這一般主要發(fā)生在執(zhí)行了返回多個(gè)結(jié)果集的存儲(chǔ)過程時(shí)),此時(shí)可以采用Resultset類的getResultSet()來獲得當(dāng)前的結(jié)果集;(3)executeUpdate()執(zhí)行對(duì)數(shù)據(jù)庫的更新的SQL語句或DDL語句。
9.6.1 對(duì)表中的記錄進(jìn)行操作
? 對(duì)一個(gè)表中的記錄可以進(jìn)行修改、插入和刪除等操作,分別對(duì)應(yīng)SQL的Update、 Insert、Delete操作;executeUpdate()方法的輸入?yún)?shù)仍然為一個(gè)String對(duì)象(即所要執(zhí)行的SQL語句),但輸出參數(shù)不是ResultSet對(duì)象,而是一個(gè)整數(shù)(它代表操作所影響的記錄行數(shù))。
Statement stmt=con.createStatement();
stmt.executeUpdate("Update bookTable set Title='Java2' where Author='zhang'");
stmt.executeUpdate("Delete from bookTable where Author='zhang'");
stmt.executeUpdate("Insert into bookTable(BookID,Author,Title) values(1,'Li Ming','Java2')"); //未給出的列,其值為NULL
程序?qū)嵗簩?duì)數(shù)據(jù)庫中的表進(jìn)行更新操作并顯示操作前后的結(jié)果
import java.sql.*;
public class DBUpdateSetTest
{ ? public static void main(String args[])
? ? { ? ? try
? ? ? ? ? { ?
? ? ? ? ? ? Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
? ? ? ? ? ? Connection con=DriverManager.getConnection("jdbc:odbc:studlist");
? ? ? ? ? ? Statement stmt=con.createStatement();
? ? ? ? ? ? ResultSet rs=stmt.executeQuery("select * from student");
? ? ? ? ? ? System.out.println("Result before executeUpdate");
? ? ? ? ? ? while(rs.next())
? ? ? ? ? ? {
? ? ? ? ? ? ? ? System.out.println(rs.getString("name"));
? ? ? ? ? ? ? ? System.out.println(rs.getString("age"));
? ? ? ? ? ? }
? ? ? ? ? ? stmt.executeUpdate("Update student set name='Yang' where id=0");
? ? ? ? ? ? stmt.executeUpdate("Delete from student where id=2");
? ? ? ? ? ? stmt.executeUpdate("Insert into student(id,name,age,sex) values(2,'zhang',30,true)");
? ? ? ? ? ? rs=stmt.executeQuery("select * from student");
? ? ? ? ? ? System.out.println("Result After executeUpdate");
? ? ? ? ? ? while(rs.next())
? ? ? ? ? ? {
? ? ? ? ? ? ? ? System.out.println(rs.getString("name"));
? ? ? ? ? ? ? ? System.out.println(rs.getString("age"));
? ? ? ? ? ? }
? ? ? ? ? ? rs.close();
? ? ? ? ? ? stmt.close();
? ? ? ? ? ? con.close();
? ? ? ? ? ? }
? ? ? ? ? catch(Exception e)
? ? ? ? ? {
? ? ? ? ? ? System.out.println(e);
? ? ? ? ? } ?
? ? }
}
9.6.2 創(chuàng)建和刪除表
? 創(chuàng)建和刪除一個(gè)表主要對(duì)應(yīng)于SQL的Create Table和Drop Table語句。這可以通過Statement對(duì)象的executeUpdate()方法來完成。
① 創(chuàng)建表
? Statement stmt=con.createStatement();
? stmt.executeUpdate("create table TableName(ID integer, Name VARCHAR(20), Age integer)");
? stmt.executeUpdate("Insert into TableName(ID, Name, Age) values(1,'Yang Ming',30)");
② 刪除表
? Statement stmt=con.createStatement();
? stmt.executeUpdate("Drop Table TableName");
9.6.3 增加和刪除表中的列
對(duì)一個(gè)表的列進(jìn)行更新操作主要是使用SQL的ALTER Table語句。對(duì)列所進(jìn)行的更新操作會(huì)影響到表中的所有的行。
① 增加表中的一列
? Statement stmt=con.createStatement();
? stmt.executeUpdate("Alter Table TableName add Column Address VarChar(50)");
? stmt.executeUpdate("Update TableName set Address='Beijing,China' where ID=1");
② 刪除表中的一列
? Statement stmt=con.createStatement();
? stmt.executeUpdate("Alter Table TableName Drop Column Address");
? stmt.executeQuery("Select * from TableName");
9.6.4 利用PreparedStatement對(duì)象實(shí)現(xiàn)數(shù)據(jù)更新
? 同SQL查詢語句一樣,對(duì)數(shù)據(jù)更新語句時(shí)也可以在PreparedStatement對(duì)象上執(zhí)行。使用PreparedStatement對(duì)象,只需傳遞一次SQL語句,可以多次執(zhí)行它,并且可以利用數(shù)據(jù)庫的預(yù)編譯技術(shù),提高執(zhí)行效率。另外也可以接受參數(shù)。
? PreparedStatement pstmt=con.prepareStatement("Update TableName set Address='Beijing,China' where ID >1");
? pstmt.executeUpdate();
9.7 參數(shù)的輸入與輸出
? 要實(shí)現(xiàn)使用SQL語句的輸入與輸出參數(shù),必須在PreparedStatement類的對(duì)象上進(jìn)行操作;同時(shí)由于CallableStatement類是PrepareStatement類的子類,所以在CallableStatemen對(duì)象上的操作也可以使用輸入與輸出參數(shù);其主要的編程原理是在生成CallableStatement或PreparedStatement類的對(duì)象時(shí),可以在SQL語句中指定輸入或輸出參數(shù),在執(zhí)行這個(gè)SQL語句之前,要對(duì)輸入?yún)?shù)進(jìn)行賦值。
(1)使用PreparedStatement類的對(duì)象
? 通過prepareStatement類的對(duì)象可以實(shí)現(xiàn)在查詢語句與數(shù)據(jù)更新語句方面都可以設(shè)置輸入?yún)?shù)。
? 具體的方法是在SQL語句中用“?”標(biāo)明參數(shù),在執(zhí)行SQL語句之前,使用setXXX方法給參數(shù)賦值,然后使用executeQuery()或executeUpdate()來執(zhí)行這個(gè)SQL語句。每次執(zhí)行SQL語句之前,可以給參數(shù)重新賦值。
? setXXX方法用于給相應(yīng)的輸入?yún)?shù)進(jìn)行賦值,其中XXX是JDBC的數(shù)據(jù)類型,如:Int、String等。setXXX方法有兩個(gè)參數(shù),第一個(gè)是要賦值的參數(shù)在SQL語句中的位置, SQL語句中的第一個(gè)參數(shù)的位置為1,第二個(gè)參數(shù)的位置為2;setXXX方法的第二個(gè)參數(shù)是要傳遞的值,如100、“Peking”等,隨XXX的不同而為不同的類型。
? PreparedStatement pstmt=con.prepareStatement("Update TableName set Name=? where ID=?");
? pstmt.setString(1,"zhang Hua"); //設(shè)置第一個(gè)參數(shù)(Name)為 “zhang Hua”
? for(int i=1;i<3;i++)
? { pstmt.setInt(2,i); //設(shè)置第二個(gè)參數(shù)(ID)為 1,2
? ? pstmt.executeUpdate();
? }
要點(diǎn):最終實(shí)現(xiàn) Update TableName set Name=zhang Hua where ID=1 與Update TableName set Name=zhang Hua where ID=2的效果。
(2)使用CallableStatement對(duì)象
? 如果要求調(diào)用數(shù)據(jù)庫的存儲(chǔ)過程,要使用CallableStatement對(duì)象。另外還有些存儲(chǔ)過程要求用戶輸入?yún)?shù),這可以在生成CallableStatement對(duì)象的存儲(chǔ)過程調(diào)用語句中設(shè)置輸入?yún)?shù)。在執(zhí)行這個(gè)存儲(chǔ)過程之前使用setXXX方法給參數(shù)賦值,然后再執(zhí)行這個(gè)存儲(chǔ)過程。
? CallableStatement cstmt=con.prepareCall("{call Query(?)}"); //Query為存儲(chǔ)過程名
? cstmt.setString(1,"輸入?yún)?shù)"); //為存儲(chǔ)過程提供輸入?yún)?shù)
? ResultSet rs=cstmt.executeQuery();
(3)接收輸出參數(shù)
? 某些存儲(chǔ)過程可能會(huì)返回輸出參數(shù),這時(shí)在執(zhí)行這個(gè)存儲(chǔ)過程之前,必須使用CallableStatement的registerOutParameter方法首先登記輸出參數(shù),在registerOutParameter方法中要給出輸出參數(shù)的相應(yīng)位置以及輸出參數(shù)的SQL數(shù)據(jù)類型。在執(zhí)行完存儲(chǔ)過程以后,必須使用getXXX方法來獲得輸出參數(shù)的值。并在getXXX方法中要指出獲得哪一個(gè)輸出參數(shù)(通過序號(hào)來指定)的值。
實(shí)例:存儲(chǔ)過程getTestData有三個(gè)輸入?yún)?shù)并返回一個(gè)輸出參數(shù),類型分別為VARCHAR。在執(zhí)行完畢后,分別使用getString()方法來獲得相應(yīng)的值。
CallableStatement cstmt = con.prepareCall(“{? = call getTestData (?,?,?)}”);
cstmt.setString(1,Value); ? ? ? ? ? ? ? ? ? ? ? //設(shè)置輸入?yún)?shù)
cstmt.setInt(2,Value);
cstmt.setFloat(3,Value);
cstmt.registerOutParameter(1,java.sql.Types.VARCHAR); ? //登記輸出參數(shù)
ResultSet rs = cstmt.executeQuery(); ? ? ? ? //執(zhí)行存儲(chǔ)過程
rs.getString(1); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //獲得第一個(gè)字段的值
String returnResult=cstmt.getString(1); ? ? ? ? ? ? ? //獲得返回的輸出參數(shù)的值
要點(diǎn):由于getXXX方法不對(duì)數(shù)據(jù)類型作任何轉(zhuǎn)換,在registerOutParameter方法中指明數(shù)據(jù)庫將返回的SQL數(shù)據(jù)類型,在執(zhí)行完存儲(chǔ)過程以后必須采用相應(yīng)匹配的getXXX方法來獲得輸出參數(shù)的值。
9.8 批量處理JDBC語句提高處理速度
有時(shí)候JDBC運(yùn)行得不夠快,這可以使用數(shù)據(jù)庫相關(guān)的存儲(chǔ)過程。當(dāng)然,作為存儲(chǔ)過程的一個(gè)替代方案,可以試試使用Statement 的批量處理特性以提高速度。
存儲(chǔ)過程的最簡單的形式就是包含一系列SQL語句的過程,將這些語句放在一起便于在同一個(gè)地方管理也可以提高速度。Statement 類可以包含一系列SQL語句,因此允許在同一個(gè)數(shù)據(jù)庫事務(wù)執(zhí)行所有的那些語句而不是執(zhí)行對(duì)數(shù)據(jù)庫的一系列調(diào)用。
使用批量處理功能涉及下面的兩個(gè)方法:
addBatch(String) 方法
executeBatch方法
如果你正在使用Statement 那么addBatch 方法可以接受一個(gè)通常的SQL語句,或者如果你在使用PreparedStatement ,那么也可以什么都不向它增加。
executeBatch 方法執(zhí)行那些SQL語句并返回一個(gè)int值的數(shù)組,這個(gè)數(shù)組包含每個(gè)語句影響的數(shù)據(jù)的行數(shù)。
注意:如果將一個(gè)SELECT語句或者其他返回一個(gè)ResultSet的SQL語句放入批量處理中就會(huì)導(dǎo)致一個(gè)SQLException異常。
關(guān)于java.sql.Statement 的簡單范例可以是:
? ? con = DriverManager.getConnection(url,"myLogin", "myPassword");
? ? con.setAutoCommit(false);
? ? stmt = con.createStatement();
? ? stmt.addBatch("INSERT INTO student " + "VALUES(4,'Yang',20,True)");
? ? stmt.addBatch("INSERT INTO student " + "VALUES(5,'li',20,True)");
? ? stmt.addBatch("INSERT INTO student " + "VALUES(6,'zhang',20,True)");
? ? stmt.addBatch("INSERT INTO student " + "VALUES(7,'wang',20,True)");
? ? stmt.addBatch("INSERT INTO student " + "VALUES(8,'liu',20,True)");
? ? int [] updateCounts = stmt.executeBatch();
? ? con.commit();
? ? con.setAutoCommit(true);
PreparedStatement 有些不同,它只能處理一部分SQL語法,但是可以有很多參數(shù),因此重寫上面的范例的一部分就可以得到下面的結(jié)果:
// 注意這里沒有刪除語句
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO student VALUES(?,?,?,?)"
);
User[ ] users = ...;
for(int i=0; i
stmt.setInt(1, users
.getID());
stmt.setString(2, users.getName());
stmt.setInt(3, users.getAge());
stmt.setBoolean(4, users.getSex());
stmt.addBatch( );
}
int[ ] counts = stmt.executeBatch();
如果你不知道你的語句要運(yùn)行多少次,那么這是一個(gè)很好的處理SQL代碼的方法。在不使用批量處理的情況下,如果添加50個(gè)用戶,那么性能就有影響,如果某個(gè)人寫了一個(gè)腳本添加一萬個(gè)用戶,程序可能變得很糟糕。添加批處理功能就可以幫助提高性能,而且在后面的那種情況下代碼的可讀性也會(huì)更好。