在了解JDBC基礎知識以後,我們先來寫一個數據庫操作的類(Bean)以後我們會在這個類的基礎上,隨著介紹的深入不斷提供優化的方案.要把一個數據庫操作獨立到一個類(Bean)中,至少要考慮以下幾個方面:
1.對於不同層次的應用,應該有不同的得到連結的方法,如果得到連結的方法要隨著應用層次的不同而改變,我們就應該把他獨立成一個專門的類中,而把在任何應用層次中都通用的處理方法封裝到一個(類)Bean中.
2.既然考慮到既作為javaBean使用又可以用為一個普通類調用,要考慮到JavaBean的規范和普通類的靈活性.
3.對於特定的數據庫操作不應封裝到共性的(類)Bean中,而應該成為它的擴展類.
以上幾點是充分考慮Java的面象對象的思想,經過深入的抽象形成的層次,下面我們就按這個思想來設計:
一:定義一個用於連結的Bean,以後如果要在不同的應用中,如可以在J2EE中從DataSource中得到連結,或從普通的連結池中得到連結,以及直接從DriverManager中得到
連結,只需修改本類中的得到連結的實現方法.
package com.imnamg.axman.beans;
import Java.sql.*;
import ..................
public class ConnectionFactory{
protected Connection conn;
ConnectionFactory() throws SQLException
{ //構造方法中生成連結
//無論是從DataSource還是直接從DriverManager中取得連結.
//先初始化環境,然後取得連結,本例作為初級應用,從DriverManager中取得連結,因為是封裝類,所以要把異常拋給調用它的程序處理而不要用try{}catch(){}塊自選處理了.
//因為要給業務方法的類繼承,而又不能給調用都訪問,所以conn聲明為protected
conn = DriverManager.getConnection(url,user,passwd);
}
/**
在多線程編程中,很多時候有可能在多個線程體中得到同一連結的引用,但如果在一個線程中關閉了連結,則另一個得到相同引用的線程就無法操作了,所以我們應該加一個重新建立連結的輔助方法,有人問為什麼既然有這個輔助方法不直接調用這個輔助而要在構造方法中生成連結?因為這樣可以增加效率,如果在構造時不能生成連結則就不能生成這個對象了,沒有必要在對象生成後再測試能不能生成連結.
*/
public void makeConnection(){
//此處的代碼同構造方法,無論以後如果實現連結,都將構造方法的代碼復制到此處.
conn = DriverManager.getConnection(url,user,passwd);
}
}
這個類就封裝到這裡,當然你可以在這兒增加業務方法,但如果要修改連結的實現,整個類都要重新編譯,因為業務方法和應用層次無關,代碼一經生成不易變動,所以獨立封裝.以下我們實現業務方法:
package com.imnamg.axman.beans;
import Java.sql.*;
import ..................
public class DBOperater extends ConnectionFactory{
//private Statement stmt;
//private ResultSet rs;
//為什麼要注釋成員變量stmt和rs,基礎部分已經說過,如果聲明為成員變量,在關閉conn時可以顯示地先關閉rs和stmt,別的沒有任何好處,而顯示關閉只是說明你編程風格好,但綜合考慮,我們要生成多個stmt或不是類型的stmt就不能聲明為成員方法,否則引用同一對象,所以我們要業務方法中生成stmt對象.不僅可以同時處理多個結果集,還可以提高性能和靈活性.
public ResultSet executeQuery(String sql) throws SQLException{
if(conn==null || conn.isClosed())
makeConnection();
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
//對於一般的查詢操作,我們只要生成一個可流動的結果集就行了.
//而對於在查詢時要更新記錄,我們用另一個業務方法來處理,這樣,這樣可以在普通查詢時節省回滾空間.
ResultSet rs = stmt.executeQuery(sql);
return rs;
}
public ResultSet executeUpdatabledQuery(String sql) throws SQLException{
if (con == null || con.isClosed())
makeConnection();
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLED);
//可更新的結果結要更大的回滾空間,普通查詢時不要調用這個方法
ResultSet rs = stmt.executeQuery(sql);
return rs;
}
/**
基於同上的原因,在執行更新操作是我們根本不要任何回滾空間,所以建立一個基本類型的stmt,實現如下
*/
public int executeUpdate(String sql) throws SQLException{
if (con == null || con.isClosed())
makeConnection();
Statement stmt = con.createStatement();
//這個stmt在執行更新操作時更加節省內存,永遠記住,能節省的時候要節省每一個字節的內存,雖然硬件設備可能會有很大的物理內存,但內存是給用戶用的而不是給程序員用的(!!!!!!!!!!!!!!!!!!)
int s = stmt.executeUpdate(sql);
return s;
}
//以上實現了常用功能,還有兩個通用的功能也是"共性"的,我們一起在這個封裝類中實現:
public PreparedStatement getPreparedStmt(String sql) throws SQLException{
if (con == null || con.isClosed())
makeConnection();
PreparedStatement ps = con.prepareStatement(sql);
return ps;
}
public CallableStatement getCallableStmt(String sql) throws SQLException{
if (con == null || con.isClosed())
makeConnection();
PreparedStatement ps = con.prepareCall(sql);
return ps;
}
//記住:對於封裝類而言預編譯語句和存儲過程調用應該從連結中返PreparedStatement和CallableStatement供調用者處理而不是返回它們的處理結果.也就是說封裝類只封裝了它們的連結過程.最後再次聲明,一定要有一個close()方法供調用者調用,而且告訴調用者無論如果要調用這個方法:
public void close() throws SQLException{
if(conn != null && !conn.isClosed())
conn.close();
}
//這個方法最好放在ConnectionFactory中,這樣可以直接調用來只測試連結.而不用再調用子類來關閉
}
OK,我們已經實現了數據庫常用操作的封裝,注意這些業務方法都是把異常拋給調用者而沒有用try...catch來處理,你如果在這裡處理了那麼調用者則無法調試了.對於特定的數據庫的特殊操作,不要封裝到此類中,可以再從這個類繼承,或直接從ConnectionFactory類繼承,當然最好是從這個業務類中繼承,這樣不僅可以調用特殊方法也可以調用共性的業務方法,興一個例子,我在應用Oracle時要把XML文件直接存到數據數和把數據直接讀取為XML文件,那麼這兩個方法只對Oracle才用到,所以:
package com.inmsg.axman.beans;
import Java.sql.*;
import oracle.xml.sql.query.OracleXMLQuery;
import oracle.xml.sql.dml.OracleXMLSave;
public class OracleDBOperater extends DBOperater{
public OracleXMLQuery getOXQuery(String sql,String table) throws Exception
{
OracleXMLQuery qry = new OracleXMLQuery(con,sql);
qry.setRowsetTag(table);
qry.setRowTag("RECORD");
return qry;
}
public int insertXML(String path,String table) throws Exception
{
OracleXMLSave sav = new OracleXMLSave(con,table);
URL url = sav.createURL(path);
sav.setRowTag("RECORD");
int x = sav.insertXML(url);
sav.close();
return x;
}
}