簡單的O-R映射.....轉

簡化版的O-R映射。
2007-11-26 19:55

首先要明確任務:簡單的說就是將數據庫中的記錄封裝爲一個對象.
由上一層傳入一條查詢的SQL語句,和一個代表該對象的class對象來創建出一個該對象的實例返回給上一層.
爲了簡化,這條SQL語句明確指明瞭查找的記錄。比如:select * from user where id='1'
傳入的class對象可以由上一層使用Class.forName(Str)來得到。
查詢數據庫中的列名必須與對象的屬性名一致。(別名也可以)

一、總體思路:
    1。首先要利用傳進來的class對象創建出該對象的實例。但還沒有對其屬性賦值。
   
    2。然後要根據SQL語句去查詢數據庫,將所有結果都拿到。
   
    3。既然前提是列名與該對象的屬性名相同,那麼列名對應的屬性肯定有一個設置該屬性的setXXX(obj)方法。
    根據列名調用所有的set方法把查到的結果賦給對象的屬性。
   
    4。將此對象返回給上一層。

二、問題與解決:
    1。怎麼樣根據class對象獲得該類的實例:調用class的newInstance()方法,雖然返回的是一個object類型,但確實是傳入的class所代表的類的實例。
   
    2。怎麼樣獲得連接:使用DBCP或者直接用DriverManager獲得都可以。
   
    3。使用Statement 還是PrepareStatement:使用PrepareStatement。原因有3,略。
   
    4。怎麼樣獲得列名和列數:使用PrepareStatement類的getMetaData()方法可以獲得表信息,然後再分別調用getColumnCount()獲得列數;getColumnLabel(i)獲得列的別名。
   
    5。怎麼樣獲得結果集:PreparedStatement ps = conn.prepareStatement(sql) 先設置要執行SQL語句,用PrepareStatement對象來執行。
       ResultSet rs = ps.executeQuery(); 就得到結果集。
   
    6。怎麼樣調用setXXX(obj)方法:找到傳入的class的方法(也就是得到Method對象)後,調用Method對象的invoke("要調用該方法的實例對象", "該方法參數的對象")方法;
   ①。怎麼樣得到Method對象:要得到這個方法對象,首先得到該類的所有public方法的一個集合(clazz.getMethods()),然後,用"set"+列名進行字符串的拼接後,
      與方法集合中每一個方法的名字進行忽略大小寫的比較,如果有找到了這個方法,就調用他,沒有找到,就不調用。
   ②。調用方法時,"要調用該方法的實例對象"是什麼:就是一開始用class的newInstance()方法創建出來的Object對象。表示是要調用這個對象的方法。
    ③。調用方法時,"該方法參數的對象"是什麼:因爲setXXX(obj)方法中的參數obj是要爲這個屬性設置的值,這個值也就是從數據庫中按照列名取出來的結果值.
         獲得結果集後,由於不知道結果值是什麼類型,所以直接調用ResultSet對象的getObject("列名");得到該值所代表的對象,然後將他傳入invoke方法中。
        (關於這個對象是什麼類型,可以去查文檔中的“MySQL Type And Java Type”或者直接用obj.getClass().getName()打出來看,但這裏我們沒必要知道它是什麼類型的。)

三、提醒與注意:
    1。就算是已經知道結果集中只有一條記錄,還是要調用rs.next()才能讓結果集遊標指向該記錄,也就是才能調用獲得列值的那些方法,不然會有空指針異常。
   
    2。調用setXXX(obj)方法 和 尋找class類中有沒有該set方法 應該被提取出來做成單獨的方法,不然會讓程序難以閱讀。
  
    3。尋找class所代表的類有沒有相應的set方法時,要使用忽略大小寫(equalsIgnoreCase)的方式來比較。因爲一般set後面的屬性名首字母是大寫的,而數據庫中不區分大小寫。
  
    4。我們剛開始的時候使用的是int id = getInt("列名"),來取得一個類型爲int的id對象。也可以使用Object id = getObject("列名"),來獲得,其實是同一個東西,用Object來獲得更靈活。
   
四、關於異常:
    1。關於“可以自己處理的異常”和“不能自己處理的異常”:
    ① 如果一個異常在你這個程序裏,可以被你處理,那麼你可以catch住,並且做一些簡單處理;
    ② 如果這個異常是你自己無法處理,或者你無法判斷這個異常能被誰處理,那麼你可以把他拋爲一個運行時異常,並且在類中申明,這樣你的上一層如果能處理則處理,
       不能處理可以不用管,異常將被帶到更上一層,當所有異常集中在頂層的時候,可以統一的來管理它們,一般是分別轉向不同的功能模塊。
   
    2。關於運行時異常 : 上一層碰到運行時異常可以catch住處理也可以不catch,不處理就往上上層繼續拋了。
   
五、測試:
    1。寫了一個User類, 擁有id,name,password,email屬性,相應的get/set方法,重寫toString()方法.
   
    2。使用Class clazz = Class.forName("User");得到這個類的class對象。
   
    3。數據庫中創建了相應的表和字段,SQL語句爲:select * from user where id='1'
   
    4。將SQL語句和class對象傳入方法既可以得到該對象:User user = (User)createUser(SQL, clazz);
   
    5。打印出該對象的toString方法:id : 1       name : yzj      password : 123     email : [email protected]

六、總結:
    這樣就簡單的實現了O-R映射,如果要將對象中的屬性持久化到數據庫的表中,有一定的限制條件也可以用上面的思想來實現。
    J2EE三層架構的思想是要讓每一層都相對的獨立,通過接口來互相調用。
    老師的例子是加入了一個集合並查詢多條記錄,將每一條記錄轉化成一個對象放入集合後把集合返回給上一層。

代碼:

package cn.yzj;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import cn.yzj.user.User;

/*
* 題目:簡化版的O-R映射。
*/
public class Test4
{

/*
* main方法中模擬了上一層使用.
*/
public static void main(String[] args) throws Exception
{
   Class clazz = Class.forName("cn.yzj.user.User");
   User user = (User)new Test4().createUser("select * from user where id='1'", clazz);
   System.out.println(user.toString());
}

/*
*主方法,創建對象.
*/
public Object createUser(String sql,Class clazz)
{
   Connection conn = null;
   PreparedStatement ps = null;
   ResultSet rs = null;
   Object obj;
   try
   {
    obj = clazz.newInstance();                   //得到要返回對象的實例
   } catch (Exception e){
    throw new RuntimeException(e.getMessage(),e);
   }                                    
   try
   {
    conn = JyUtilThree.getConnection();                  //獲得連接. 可以自行替換
    ps = conn.prepareStatement(sql);                   //定義使用PreparedStatement來執行SQL.
    rs = ps.executeQuery();                                           //執行查詢SQL.並返回結果集.
    ResultSetMetaData rsmd = ps.getMetaData();                     //獲得表的信息.
    int col = rsmd.getColumnCount();                                      //獲得表中的列數.
    while(rs.next())
    {
     for(int i = 0;i<col;i++)
     {
      String cloname = rsmd.getColumnLabel(i+1);                   //獲得表中列名
      Object obj1 = rs.getObject(cloname);                                //獲得列的值.
      invokeMeth(cloname,obj,obj1,clazz);                               //調用相應的set方法.
     }
    }
   } catch (SQLException e){
    e.printStackTrace();
    throw new RuntimeException(e.getMessage(),e);
   }
   JyUtilThree.release(rs, ps, conn);               //將資源釋放
   return obj;                                                                                    //將對象返回.
}

/*
* 調用所有setXXX(obj)的方法
*/
public void invokeMeth(String cloname,Object instance,Object obj,Class clazz)
{
   try
   {
    Method m = findMethod( "set"+cloname,clazz);
    if(m!=null)
    m.invoke(instance, obj);
   } catch (Exception e)
   {
    e.printStackTrace();
    throw new RuntimeException(e.getMessage(),e);
   }
}

/*
* 查找是否存在該列名對應的setXXX(obj)的方法.
*/
public Method findMethod(String name ,Class clazz)
{
   Method[] m= clazz.getMethods();
   for(int i = 0;i<m.length;i++)
   {
    if(name.equalsIgnoreCase(m[i].getName()))
     return m[i];
   }
   return null;
}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章