J2EE中幾種物件導向的資料庫對映訪問策略:

gudesheng發表於2008-01-03

幾種物件導向的資料庫訪問策略:

1 JDBC
是最原始的方法,寫sql語句,維護性差


下面物件導向的方法:

例如update: 要先取出物件,更新物件,然後再儲存

OrderInfo order = orderService.getOrder(orderId);
order.setStatus(new Integer(2));
orderService.updateOrder(order);

2 Hibernate

使用Hql

3 iBatis
將查詢和更新放在maps檔案中

 
    select productid, name, descn, category from product
   
     
        lower(name) like #keywordList[]# OR lower(category) like #keywordList[]# OR lower(descn) like #keywordList[]#
     
   

4 EasyDBO:

有三種實現方法,我們只看其中採用annotation的
@Table(tableName="Customer")
public class Customer implements Serializable {
來確定表名

採用反射的方法,不需要配置檔案:
 public List getRootCustomers() {  
  return this.dao.query(Customer.class,"(parent_id is null or parent_id='')");
 }

  List list=dao.query(CustomerPrice.class, "customer_id="+cu.getId()+" and product_id="+p.getId()+" order by vdate desc");

該方法已經和ADODB很像了,但和adodb不同的是,仍然沒有實現完全自動的Plain SQL轉換到PO.

5 PHP's ADODB的j2ee移植

以jdbc查詢sql為基礎,通過反射,範型等方法自動裝載。
adodb的方法和上面的easydbo很像,不過在obj到sql的生成上更加成熟一些,驅動也更加多

對於查詢

    /////////////////////////////////////////////////////////////////////////////
    //Function: 完成ResultSet物件向ArrayList物件為集合的物件的轉化
    //Para:sql,指定的查詢Sql
   //Para:className,Sql相對應得JavaBean/FormBean類的名字
   //Return:以類className為一條記錄的結果集,完成ResultSet物件向ArrayList物件為集//合的className物件的轉化
  //////////////////////////////////////////////////////////////////////////////
  public ArrayList Select(String sql,String className){
    ArrayList paraList=new ArrayList();
    try{
      if (conn == null){
        Connection();
      }
      PreparedStatement stmt = conn.prepareStatement(sql);
      ResultSet rs = stmt.executeQuery();
      String recordValue="";
      Object c1=null;
      paraList=new ArrayList();
      ResultSetMetaData rsmd = rs.getMetaData();
      int columnCount = rsmd.getColumnCount();
      while (rs.next()){
          c1=Class.forName(className).newInstance();
          for (int i=1; i<=columnCount; i++) {
            if(rs.getString(rsmd.getColumnName(i))!=null){
              recordValue=rs.getString(rsmd.getColumnName(i));
            }else{
              recordValue="";
            }
            Method m=c1.getClass().getMethod(getSetMethodName(rsmd.getColumnName(i)),new Class[]{recordValue.getClass()});
            m.invoke (c1, new Object[]{recordValue});
          }
          paraList.add(c1);
      }
    }catch(SQLException ex){
     
    }catch(ClassNotFoundException e){
    }catch(NoSuchMethodException e) {
    }catch(InvocationTargetException e){
    }catch (IllegalAccessException e){
    }catch(InstantiationException e){
    } finaly{
        closeConnection();
    return paraList;
    }
      }


   //Function:取得使用者列表
  //Para:
  //Return:返回使用者列表
  /////////////////////////////////////////////////////////////////////////////
  public ArrayList getUsers(){
      ArrayList ret=null;
      DatabaseManage db=new DatabaseManage();
      String sql=" select usr_id,usr_name "
          +" from users " ;  //該方法的好處是SQL可以隨便寫,需要的欄位也可以隨便寫,甚至免去了持久層的LazyLoad
      ret=db.Select(sql,"com.domain.User");
      return ret;
  }

對於單張表,用PO/Formbean來存放
如果有關聯多張表,需要一個VO/Map來存放

對於儲存和更新
    檢查物件裡面的每一個屬性,如果不是null,就組成SQL語句,
    更新的時候,先查出這個物件,如果屬性不是null,並且屬性值變了,才將該屬性組成SQL語句
   
這種方法和Hibernate/Ibatis相比可能犧牲一些效能,但是免去了大量的配置檔案,如果對欄位有特殊的要求,可以
通過annotation來定義。

Annotation可是個好東西,根據jdk手冊:Annotations can be read from source files, class files, or reflectively at run time.
所以可以充分利用反射讀取annotation來減少程式碼。
下面是一些例子
@com.acme.util.Name(first=Alfred, middle=E., last=Neuman)
@Table(tableName="Customer")
@ManyToOne(column = "parent_id", fieldType=java.util.HashSet.class,type = Customer.class,lazy=false)
讀取的方法是在反射裡面使用下面的方法
T xxx = getAnnotation(Class annotationClass) 

當然,實現必須宣告annotationClass,下面是手冊的一個簡單例子
/**
 * Describes the Request-For-Enhancement(RFE) that led
 * to the presence of the annotated API element.
   定義一個標籤記號
 */
public @interface RequestForEnhancement {
    int    id();
    String synopsis();
    String engineer() default "[unassigned]";
    String date();    default "[unimplemented]";
}

這個標籤,和它的屬性都在上面宣告瞭,下面是某個函式中需要用到這個標籤的示例
@RequestForEnhancement(
    id       = 2868724,
    synopsis = "Enable time-travel",
    engineer = "Mr. Peabody",
    date     = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }

因此我們如果想要呼叫這個標籤
for (Method m : Class.forName('假設是travelThroughTime所在的類名').getMethods()) {
         if (m.isAnnotationPresent(RequestForEnhancement.class)) {
             RequestForEnhancement rfe = m.getAnnotation(RequestForEnhancement.class);
             下面就可以取得rfe的屬性了
         }
      }

Commons Attributes也是一個替代jdk標準annotation的方案


PHP中的ADODB之所以強大,高效是在於PHP陣列的強大和弱變數定義的方便。

當然這裡的每一個物件就相當於對應資料庫的一張表,這樣也很好。
因為在業務相當複雜的時候,關聯要儘量少用,把業務精心設計在資料庫表上面而非java物件的集合的關聯上。
在大型應用中,一般1:1的關聯都多使用View來處理關聯,1:n和n:n的關聯,建議還是在DAO裡面手動儲存,裝載和更新


===================
此外還有一些方法,可以方便我們快速的將RS變成可操作的物件,簡單舉幾個例子如下

1 commons dbutils

Custom RowProcessor

java.lang.Object[] toArray(java.sql.ResultSet rs)
          Convert a ResultSet row into an Object[].
 java.lang.Object toBean(java.sql.ResultSet rs, java.lang.Class type)
          Convert a ResultSet row into a JavaBean.
 java.util.List toBeanList(java.sql.ResultSet rs, java.lang.Class type)
          Convert a ResultSet into a List of JavaBeans.
 java.util.Map toMap(java.sql.ResultSet rs)
          Convert a ResultSet row into a Map.


Custom BeanProcessor


 java.lang.Object toBean(java.sql.ResultSet rs, java.lang.Class type)
          Convert a ResultSet row into a JavaBean.
 java.util.List toBeanList(java.sql.ResultSet rs, java.lang.Class type)
          Convert a ResultSet into a List of JavaBeans.


2 commons beanutils
ResultSetDynaClass (Wraps ResultSet in DynaBeans)

  Connection conn = ...;
  Statement stmt = conn.createStatement();
  ResultSet rs = stmt.executeQuery
    ("select account_id, name from customers");
  Iterator rows = (new ResultSetDynaClass(rs)).iterator();
  while (rows.hasNext()) {
    DynaBean row = (DynaBean) rows.next();
    System.out.println("Account number is " +
                       row.get("account_id") +
                       " and name is " + row.get("name"));
  }
  rs.close();
  stmt.close();

============

總得來說,使用反射機制可以極大得方便對資料的各種操作,使操作變得更加透明,無需配置檔案 



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1504282


相關文章