用Java編程語言和JDBC開發(fā)的程序是可以跨平臺(tái)運(yùn)行的,并且是不受供應(yīng)商限制的.
4.1 JDBC的設(shè)計(jì)
JDBC由兩層組成,上面一層是JDBC API,負(fù)責(zé)與JDBC管理器驅(qū)動(dòng)程序API進(jìn)行通信,將各個(gè)不同的SQL語句發(fā)送給它;該管理器(對(duì)程序員是透明的)再與實(shí)際連接到數(shù)據(jù)庫(kù)的各個(gè)第三方驅(qū)動(dòng)程序進(jìn)行通信,并且返回查詢的信息,或者執(zhí)行由查詢規(guī)定的操作.
JDBC驅(qū)動(dòng)程序分為以下幾種類型:
類型1驅(qū)動(dòng)程序
負(fù)責(zé)將JDBC轉(zhuǎn)換為ODBC,并且使用一個(gè)ODBC驅(qū)動(dòng)程序與數(shù)據(jù)庫(kù)進(jìn)行通信
類型2驅(qū)動(dòng)程序
部分使用Java編程語言編寫的和部分使用本機(jī)代碼編寫的驅(qū)動(dòng)程序,用于與數(shù)據(jù)庫(kù)的客戶機(jī)API進(jìn)行通信
類型3驅(qū)動(dòng)程序
純粹的Java客戶程序庫(kù),它使用跨數(shù)據(jù)庫(kù)協(xié)議,將數(shù)據(jù)庫(kù)訪問請(qǐng)求傳輸給服務(wù)器組件,然后該服務(wù)器組件將訪問請(qǐng)求轉(zhuǎn)換成特定的數(shù)據(jù)庫(kù)協(xié)議
類型4驅(qū)動(dòng)程序
純粹的Java庫(kù),用于JDBC訪問請(qǐng)求直接轉(zhuǎn)換成特定數(shù)據(jù)庫(kù)協(xié)議
4.2 結(jié)構(gòu)化查詢語言
JDBC是個(gè)到SQL(結(jié)構(gòu)化查詢語言)的接口,而SQL實(shí)際上是與所有最新型的關(guān)系數(shù)據(jù)庫(kù)之間的接口.
4.3 安裝JDBC
建議最好不要使用Java2 SDK配備的JDBC/ODBC橋接器驅(qū)動(dòng)程序,更加反對(duì)將該驅(qū)動(dòng)程序用于Access這樣的桌面數(shù)據(jù)庫(kù).
4.4 JDBC編程的基本概念
1. 數(shù)據(jù)庫(kù)URL
語法: jdbc:subprotocol name:other stuff
其中subprotocol特定驅(qū)動(dòng)程序, other stuff參數(shù)的格式要根據(jù)它使用的子協(xié)議而定.
2. 建立連接
Class.forName(驅(qū)動(dòng)程序類); 注冊(cè)驅(qū)動(dòng)程序
String url = …;
String username = …;
String password = …;
Connetion conn = DriverManager.getConnection(url, username, password);
讀取屬性文件建立連接
Properties props = new Properties();
FileInputStream in = new FileInputStream(“database.properties”);
props.load(in);
in.close();
String drivers = props.getProperty(“jdbc.drivers”);
String url = props.getProperty(“jdbc.drivers”);
String username = props.getProperty(“jdbc.username”);
String password = props.getProperty(“jdbc.password”);
Connetion conn = DriverManager.getConnection(url, username, password);
3. 執(zhí)行SQL命令
Statement stat = conn.createStatement();
String sql = …;
ResultSet rs = stat.executeQuery(sql);/stat.executeUpdate(sql);
while(rs.next()){
…
}
4. 高級(jí)SQL類型
Blob b=resultSet.getBlob(1);
InputStream bin=b.getBinaryStryeam();
Clob c=resultSet.getClob(2);
Reader cReader=c.getCharacterStream();
寫入:
FileInputStream fis=new
FileInputStream(f,Connection conn);
byte[] buffer=new byte[1024];
data=null;
int sept=0;int len=0;
while((sept=fis.read(buffer))!=-1){
if(data==null){
len=sept;
data=buffer;
}else{
byte[] temp;
int tempLength;
tempLength=len+sept;
temp=new byte[tempLength];
data=temp;
len=tempLength;
}
if(len!=data.length()){
byte temp=new byte[len];
data=temp;
}
}
String sql="insert into fileData (filename,blobData) value(?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1,f.getName());
ps.setObject(2,data);
ps.executeUpdate();
讀出:
try {
Clob c=resultSet.getClob(2);
Reader reader=c.getCharacterStream():
if (reader == null) {
return null;
}
StringBuffer sb = new StringBuffer();
char[] charbuf = new char[4096];
for (int i = reader.read(charbuf); i >
0; i = reader.read(charbuf)) {
sb.append(charbuf, 0, i);
}
return sb.toString();
} catch (Exception e) {
return "";
}
4.5 執(zhí)行查詢操作
采用宿主變量方式:
String userId = 1;
String sql = “select * form user where user_id=?”;
PreparedStatement pStat = conn.prepareStatement(sql);
pStat.setString(1,userId);
ResultSet rs = pStat.executeQuery();
4.6 可滾動(dòng)的和可更新的結(jié)果集
1. 可滾動(dòng)的結(jié)果集
Statement stat = conn.createStatement(type,concurrency);
或
PreparedStatement stat = conn.prepareStatement(command,type, concurrency);
其中
Type包括:
ResultSet.TYPE_FORWARD_ONLY 不能滾動(dòng)
ResultSet.TYPE_SCROLL_INSENSITIVE 可以滾動(dòng),但變化不敏感
ResultSet.TYPE_SCROLL_SENSITIVE 可以滾動(dòng),但變化敏感
Concurrency包括:
ResultSet.CONCUR_READ_ONLY 不能更新
ResultSet.CONCUR_UPDATABLE 可以更新
常用方法:
rs.previous() 滾動(dòng)結(jié)果集
rs.relative(n) 將光標(biāo)向后或向前移動(dòng)n行
rs.absolute(n) 將光標(biāo)設(shè)置到某個(gè)特定的行號(hào)上
rs.getRow() 獲得當(dāng)前的行號(hào)
2. 可更新的結(jié)果集
常用方法:
rs.getConcurrency() 查看結(jié)果集是否可更新
rs.updateXxx() 只能用于修改行的值,不能修改數(shù)據(jù)庫(kù)
rs.updateRow() 將當(dāng)前行中所有信息更新發(fā)送給數(shù)據(jù)庫(kù)
rs.cancelRowUpdates() 撤銷對(duì)當(dāng)前行的更新
rs.moveToInsertRow() 將光標(biāo)移到一個(gè)特定的位置
rs.insertRow() 將該新行傳遞給數(shù)據(jù)庫(kù)
rs.moveToCurrentRow() 將光標(biāo)移回之前位置
rs.deleteRow() 刪除光標(biāo)下的行
例子:
rs. moveToInsertRow();
rs.updateString(“Title”,title);
rs.updateString(“Isbn”,isbn);
rs.insertRow();
rs.moveToCurrentRow();
4.7 元數(shù)據(jù)
元數(shù)據(jù)是在SQL中用于描述數(shù)據(jù)庫(kù)或者它的各個(gè)部分之一的數(shù)據(jù);分為關(guān)于數(shù)據(jù)庫(kù)的元數(shù)據(jù)和關(guān)于結(jié)構(gòu)集合的元數(shù)據(jù).
DatabaseMetaData conn.getMetaData() 用于提供關(guān)于數(shù)據(jù)庫(kù)的元數(shù)據(jù)
ResultSetMetaData rs.getMetaData() 用于提供關(guān)于結(jié)構(gòu)集合的元數(shù)據(jù)
4.8 事務(wù)
如果將各個(gè)更新命令組合成一個(gè)事務(wù),可以實(shí)現(xiàn)數(shù)據(jù)庫(kù)數(shù)據(jù)的完整性;在提交事務(wù)時(shí),如果在它中間某個(gè)位置上運(yùn)行失敗,它可以執(zhí)行回退操作,并且該數(shù)據(jù)庫(kù)將自動(dòng)撤銷提交事務(wù)以來進(jìn)行的所有更新及所產(chǎn)生的影響.
在默認(rèn)情況下,數(shù)據(jù)庫(kù)連接處于自動(dòng)提交方式,并且每個(gè)SQL命令一旦被執(zhí)行,便被提交給該數(shù)據(jù)庫(kù).一旦命令被提交,就無法進(jìn)行回退操作.
批量更新: 一序列命令將作為一個(gè)批量來集中和提交,命令不包括Select查詢.
String sql = …;
stat.addBatch(sql);
while(…){
sql = …
stat.addBatch(sql);
}
stat.executeBatch();
4.9 高級(jí)連接管理
在企業(yè)級(jí)環(huán)境中部署JDBC應(yīng)用程序時(shí),數(shù)據(jù)庫(kù)連接的管理納入了JNDI(Java命令與目錄接口)之中.一個(gè)目錄負(fù)責(zé)管理整個(gè)企業(yè)中的數(shù)據(jù)源的位置.使用目錄后,就可以對(duì)用戶名、口令、數(shù)據(jù)庫(kù)名字和JDBC URL實(shí)施集中管理.
Context jndi = …;
DataSource source = (DataSource)jndi.lookup(“jdbc/corejava”);
Connection conn = source.getConnection(username,password);