2007年4月19日 星期四

結合Meta Programming的OR Mapping - Active Record

像Ruby這種動態語言有個一般靜態語言較難達成的功能--Meta Programming。

它並不是什麼新技術,早在70年代的SmallTalk就有內建這玩意了,不過它的好處倒是在Ruby on Rails出現後,才紅了起來。

什麼是Meta Programming?
簡單說就是類別的定義可在執行期自由自在的修改…
可以在執行期對動態現存的類別新增、修改成員函式,也可以新增、修改成員變數...
舉例來說:
Date是一個Ruby提供的類別,而你需要在程式中判斷所指定的日期是不是假日,在一般靜態語言中你可能會新增一個全域函式,或是在你自己的Helper類別中加入一個函式,如下:



boolean isHoliday(Date theDay);



然後使用時是像這樣:

Date someDay = ...;
...
...
if (isHoliday(someDay)) {
....
}
...


嗯…較不直覺,不是嗎?
看看下列程式,如果可以這樣用呢?

if (someDay.isHoliday?()) {
...
}


是不是方便多了!!

如果你還不太了解的話,可以參考一下我之前寫的這篇Meta Programming簡介


當然Meta Programming的作用不止於此,它可以做出許多令人驚艷的應用,在Ruby on Rails中的Active Record就是使用此技術的殺手級應用之一。

Active Record是結合Meta Programming的OR Mapping框架,就像Java中的Hibernate一樣,但比它更好,在如此肯定的說他好之前,我們來回想一下Hibernate:

在資料庫中有一個Table: User,內容是使用者的資料,Schema如下:

create table products (
id int not null,
name varchar(100) not null,
age number,
primary key (id)
);


我希望使用物件的方式存取使用者的資料,使用Java+Hibernate時,我有以下幾個步驟:
1. 建立User.hbm.xml,做為OR Mapping的定義:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="richard.User" table="userinfo">

<id name="id" column="id" type="java.lang.Integer">
<generator class="assigned"/>
</id>

<property name="name" column="name" type="java.lang.String"/>

<property name="age" column="age" type="java.lang.Integer"/>

</class>

</hibernate-mapping>


2. 為物件中的欄位建立相對應的Getter和Setter:

package richard;

public class User {
private Integer id;
private String name;
private Integer age;

// 必須要有一個預設的建構方法
// 以使得Hibernate可以使用Constructor.newInstance()建立物件
public User() {
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}



3. 在Client端程式中如下列方法使用:

// Configuration 負責管理 Hibernate 配置訊息
Configuration config = new Configuration().configure();
// 根據 config 建立 SessionFactory
// SessionFactory 將用於建立 Session
SessionFactory sessionFactory = config.buildSessionFactory();

// 新增持久化的物件
User user = new User();
user.setId(new Integer(4));
user.setName("caterpillar");
user.setAge(new Integer(30));

// 開啟Session,相當於開啟JDBC的Connection
Session session = sessionFactory.openSession();
// Transaction表示一組會話操作
Transaction tx= session.beginTransaction();
// 將物件映射至資料庫表格中儲存
session.save(user);

// delete
User user2 = new User();
session.load(user2, 2);
session.delete(user2);

tx.commit();

session.close();
sessionFactory.close();




嗯!Hibernate還是不失為在Java上一個相當優良的OR Mapping工具,使用起來是很簡單易懂的,但還是有一些不方便的地方:

1. 必須定義XML檔,當Table很多,欄位也愈來愈多時會很難維護。
2. 必須為物件依照要操作的欄位定義Getter和Setter方法,同樣的,資料愈多愈難管。
3. 當物件之間有關聯時,會更難維護。
4. 每次設計有變更時,一連串要更新的地方都讓我想要離職。

這些都是目前OR Mapping難解的部分,很幸運的,如果你使用Ruby,現在就有個Active Record解決了這些問題:

Active Record是怎麼做的?

1. 你需要一個User類別,長這樣:

class User < ActiveRecord::Base
end


2. 使用方法如下:

# 可以用Active Record 新增一筆資料
user1 = User.new
user1.name="Richard"
user1.age=18
user1.save

# 也可以用Active Record來找一筆資料
user2 = User.find 1
print user2.name


我並沒有在User類別中定義屬性name, age,但就是直接可以用,我也沒有在User類別中定義類別方法(static method),但User.find()這個方法是work的。

很神奇嗎?
Active Record透過Meta Programming幫你把這些都加上去了,ORMapping變輕鬆了!!

沒有留言: