|
JDBC:
|-- 數(shù)據(jù)庫(kù)互聯(lián)。 |-- 由SUN公司所制定的用來(lái)訪問(wèn)數(shù)據(jù)庫(kù)的規(guī)范。 |-- 數(shù)據(jù)庫(kù)數(shù)據(jù) <= 交互 => 應(yīng)用程序。
JDBC的驅(qū)動(dòng)程序包含以下四種類(lèi)型: |-- DriverManager [Class] 用來(lái)管理一組數(shù)據(jù)庫(kù)的驅(qū)動(dòng) |-- Connection [Interface] 與數(shù)據(jù)庫(kù)的一個(gè)連接對(duì)象 |-- Statement [Interface] 用于執(zhí)行一個(gè)靜態(tài)的SQL語(yǔ)句,并返回它所生成的結(jié)果對(duì)象 |-- ResultSet [Interface] 存放查詢結(jié)果集的對(duì)象 |-- Type [Class] |-- Blob [Interface] 二進(jìn)制大對(duì)象 |-- Clob [Interface] 字符大對(duì)象 |-- SQLException [Class] 數(shù)據(jù)庫(kù)最底層異常(已檢測(cè)異常) |-- DatabaseMetaData [Class] 基本數(shù)據(jù)的元數(shù)據(jù) |-- ResultSetMetaDate [Class] 結(jié)果集的元數(shù)據(jù)
下面就簡(jiǎn)單的分析一下JDBC的這六個(gè)步驟:
1.獲取連接Connection對(duì)象分析 a.獲取數(shù)據(jù)庫(kù)的三大要素 |-- URL:不同的數(shù)據(jù)庫(kù)系統(tǒng)的URL不相同,但是至少有如下的內(nèi)容: 主協(xié)議:次協(xié)議:HOST(主機(jī)):PORT(端口):SID(庫(kù)名) |-- USER:用戶名 |-- PASSWORD:密碼 b.DriverManager獲取連接的原理: 1
2. Statement 調(diào)用靜態(tài)SQL //驅(qū)動(dòng)管理器類(lèi)2 class DriverManager {3 static Vector<Driver> drivers = new Vector<Driver>();4 public static void registerDriver(Driver d) {5 driver.add(d);6 }7 /** *//**獲取連接的方法*/8 public static Connection getConnection(String url,String user,String pwd) throws SQLException {9 Properties info = new Properties();10 info.setProperty("user",user);11 info.setProperty("password",pwd);12 return getConnection(url,info);13 }14 public static Connection getConnection(String url,Properties info)throws SQLException {15 /**//*迭代多由的驅(qū)動(dòng),并且一次與給定的URL進(jìn)行匹配,如果成功則返回當(dāng)前驅(qū)動(dòng)的Connection對(duì)象*/16 Iterator<Driver> iter = drivers.iterator();17 while(iter.hasNext()) {18 Driver d = iter.next();19 if(匹配(url,d)) {20 return d.connect(url,info);21 }else {22 continue;23 }24 }25 /**//*到此都沒(méi)有返回,說(shuō)明沒(méi)有匹配成功,則拋出SQLException異常*/26 throw new SQLException("no suitable driver");27 }28 }PreparedStatement 來(lái)執(zhí)行動(dòng)態(tài)SQL,可以為SQL動(dòng)態(tài)綁定參數(shù)。 如:同構(gòu)SQL,SQL內(nèi)容一樣參數(shù)不同。 1
3.CallableStatement用來(lái)調(diào)用存儲(chǔ)過(guò)程[procedure] select id,first_name from s_emp2 where dept_id = ? and name like ?;3 ![]() 4 insert into t_user values(?,?,?,?);5 --6 -- 例:7 Connection con = ConnectionFactory.getConnection();8 String sql = "XXXXXXXXXXXXXXXXXXXXXXXXXX";9 PreparedStatement pstm = con.PreparedStatement(sql);10 //綁定參數(shù)11 pstm.setInt(1,XXX);12 pstm.setString(2,XXX);13 . . . 1
4.執(zhí)行SQL String sql = "{call insert_user(?,?)}"2 CallableStatement cstm = con.prepareCall(sql);3 //綁定參數(shù)4 cstm.setString(1,"user");5 cstm.setString(2,"password");6 //執(zhí)行過(guò)程,此返回值表示受影響的記錄條數(shù)7 int res = cstm.executeUpdate();8 ![]() 1
5.處理結(jié)果集 statement:2 ![]() 3 executeQuery(selectSQL); => ResultSet4 executeUpdate(dmlSQL); => int5 execute(arbiSQL); => boolean6 ![]() 7 if(stm.execute(sql)) {8 //為真,表示執(zhí)行select語(yǔ)句9 ResultSet rs = stm.getResultSet();10 }else {11 //為假,表示執(zhí)行非select語(yǔ)句12 int res = stm.getUpdateCount();13 }1
6.釋放資源 ResultSet rs:2 rs.next(); => boolean用于判斷結(jié)果集中是否還有可讀取的元素。3 rs.getXXX(); get系列方法,用于讀去結(jié)果集中的元素。1
Connection.close();2 Statement.close();3 ResultSet.close();
[JDBC Advanced Topics]異常處理、元數(shù)據(jù)以及事務(wù)。
1、異常 Exception java.sql.SQLException 對(duì)底層數(shù)據(jù)庫(kù)異常的封裝,它是一個(gè)已檢查異常。所以應(yīng)用程序必須對(duì)它做處理。 .getErrorCode(); 獲取特定于數(shù)據(jù)庫(kù)供應(yīng)商的錯(cuò)誤代碼。 java.sql.SQLWaring extends SQLExecption 提供關(guān)于數(shù)據(jù)庫(kù)訪問(wèn)警告信息的異常。 例: 1
try {2 //此處省略3 } catch(SQLException e) {4 while(e != null) {5 e.printStackTrace();6 e = e.getNextExecption();7 }8 }2、元數(shù)據(jù):用來(lái)描述其他數(shù)據(jù)的數(shù)據(jù) JDBC中的元數(shù)據(jù): ResultSetMetaDate: 結(jié)果集元數(shù)據(jù),它用來(lái)描述結(jié)果集本身。 ResultSet.getMetaDate(); 獲取當(dāng)前結(jié)果集的元數(shù)據(jù)。 DatabaseMetaDate: 數(shù)據(jù)庫(kù)元數(shù)據(jù),用來(lái)描述數(shù)據(jù)庫(kù)本身。 Connection.getMetaDate(); 獲取當(dāng)前數(shù)據(jù)庫(kù)的元數(shù)據(jù)。 例: //利用元數(shù)據(jù)打印出結(jié)果集 public void executeSQL(String sqlcommand) { Connection con = null; Statement stm = null; ResultSet rs = null; try { //這里的ConnectionFactory是寫(xiě)的一個(gè)連接工廠,這里就不做介紹了。 con = ConnectionFactory.getConnection(); stm = con.createStatement(); boolean flag = stm.execute(sqlcommand); if(flag) { rs = stm.getResultSet(); ResultSetMetaDate rsmd = rs.getMetaDate(); int columns = rsmd.getColumnCount(); for(int i=1;i<columns;i++) { String columnName = rsmd.getColumnLabel(i); System.out.print(columnName+"\t"); } System.out.println(); while(rs.next()) { for(int i=1;i<columns;i++) { Object content = rs.getObject(i); System.out.print(content+"\t"); } System.out.print(); }else { int results = stm.getUpdateCount(); System.out.println(results+" resultes was update "); } } }catch(SQLExecption e) { //此處省略若干 } }3、DML的操作 [JDBC的事務(wù)] 以事務(wù)為單位 JDBC事務(wù): 定義:一組相關(guān)的操作,不可分割,一起成功,一起失敗。 相關(guān)操作: Connection: commit(); JDBC事務(wù)提交 rollback(); JDBC事務(wù)回滾 JDBC的事務(wù)默認(rèn)情況下是自動(dòng)提交的,每執(zhí)行一次SQL命令,就提交commit一次。[AutoCommit] 如: 轉(zhuǎn)帳(){ ... update t_account1 set balance = balance-100; update t_account2 set balance = balance+100; ... } 如何讓以上兩句SQL語(yǔ)句,一起執(zhí)行,一起失敗。所以就需要把他們放在一個(gè)事務(wù)中。 如: 轉(zhuǎn)帳(){ ... con.setAutoCommit(false);//關(guān)閉自動(dòng)事務(wù)提交 update t_account1 set balance = balance-100; update t_account2 set balance = balance+100; 如果以上兩句都成功,則: con.commit();//提交事務(wù) 如果以上任意一個(gè)失敗,則: con.rollback();//事務(wù)回滾 } 獲取主鍵方式(主鍵值生成策略) 主鍵值不應(yīng)該由UI界面來(lái)收集,而應(yīng)該由應(yīng)用程序自動(dòng)生成,常見(jiàn)的策略如下: select max(id) from table_name; 此方法當(dāng)有多線程并發(fā)時(shí),就會(huì)產(chǎn)生問(wèn)題。 b. sequence 序列 select sequence.nextval from dual; 此方法只適用支持序列的數(shù)據(jù)庫(kù)產(chǎn)品,所以不是很通用。 c. 維護(hù)單行單列得表 1
create table id_table(next_id number not null);2 insert into id_table next_id values(1);3 commit;4 --使用以下SQL自動(dòng)維護(hù)5 select next_id from id_table for update;6 update id_table set next_id = next_id+1;7 commit;并發(fā)控制:多個(gè)事務(wù)同時(shí)操作同一個(gè)對(duì)象 有三種并發(fā)情況,如下: 1、臟讀 Dirty reads 讀到其他事務(wù)沒(méi)有提交的數(shù)據(jù)。注:Oracle不支持 2、不可重復(fù)讀 Repeatable reads 同一個(gè)事務(wù)在兩個(gè)不同的時(shí)刻讀取不同結(jié)果 如: 1
解決重復(fù)讀問(wèn)題: T1:select score from t_score where id= 48;2 T2:update t_score set score = score+10 where id = 48;3 T1:select score from t_score where id= 48;讓T1在查詢語(yǔ)句中加入for update子句,從而把選定的記錄加上排他鎖;那么T1事務(wù)完成之前,其他事務(wù)是不能來(lái)獲取鎖資源的。 3、幻影讀 Phanton reads 記錄數(shù)量變化,而不是行記錄的列值 1
解決幻影讀問(wèn)題: T1:elcet count(*) from t_class where classname = '******';2 T2:nsert into t_class values(![]() .);3 commit();4 T1:selcet count(*) from t_class where classname = '******';5 ![]() 鎖表:lock table table_name in mode; 事務(wù)隔離級(jí)別:SUN在JDBC規(guī)范中制定了5個(gè)級(jí)別,分別用來(lái)滿足不同的并發(fā)性。 Connection: 0 .TRANSACTION_NONE 無(wú) 1 .TRANSACTION_READ_UNCOMMITED 可以臟讀 2 .TRANSACTION_READ_COMMITED 不可以臟讀 4 .TRANSACTION_REPEATABLE 不可以重復(fù)讀 8 .TRANSACTION_SERIALIZABLE 為每個(gè)事務(wù)都開(kāi)辟一個(gè)空間 事務(wù)的隔離界別設(shè)置得越高,則并發(fā)性能越差 注:不同數(shù)據(jù)庫(kù),對(duì)隔離級(jí)別的支持也是不一樣的。 如:oracle就不支持0和1兩種。 如何設(shè)置隔離級(jí)別? 如:con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITED); 這個(gè)也是默認(rèn)事務(wù)隔離級(jí)別。 |
|
|