Wednesday, November 14, 2007

Generic DAO (1)

反正丟空了這麼久, 就用這個 blog 來做學習記錄吧.

在公司最近搞的 application framework 利用了 Hibernate, 在Don't Repeat DAO 這文章中找到一個 Generic DAO 的架構, 加上了自己的 extensions, 在這裡 share 想法.

顧名思義, 這個 Pattern 是希望不要再重覆又重覆的去建一大堆大同小異的 DAO. 其實每個 DAO, 主要都不外乎是 create, delete, update 和一堆 finder method. 這個 Generic DAO 的做法. 大概就是以 Hibernate 去處理掉 CRUD 中的 Create, Update, Delete 和最基本的 Read, 然後以最簡單的方法去讓 developer 去 define 其他 finder methods. 而define finder method, 也讓 developer 只要集中於 query 上, 而不用再重覆寫一些 getSession(), session.createQuery(), return query.list() 之類的東西.

當中用到的主要 technology 有 Spring (AOP 的 Interceptor 和 ProxyFactoryBean) 和 Hibernate (當然)。

先說說基本效果.
假設我寫了一個 Staff class, 然後要做 DAO. 靠這個 framework, 我要做的是:

1. 在 Staff class 中加上 OR Mapping. 我選擇了用 annotation.

@Entity
@Table(name="STAFF")
public class Staff {
_ @Id
_ @GeneratedValue(strategy=GenerationType.AUTO)
_ @Column(name="ID")
_ private Long id;

_ @Column(name="NAME")
_ private String name;

_ @Column(name="SALARY")
_ private BigDecimal salary;

_ // getters and setters and other methods
}


2. 寫 DAO 的 Interface

public interface StaffDao extends GenericDao<Staff, Long> {
}



3. 在 Spring Config 加上

<bean id="abstractDaoTarget" class="foo.GenericDaoHibernateJpaImpl" abstract="true" />

<!-- Dao Layer instances -->
<bean id="staffDao" class="org.springframework.aop.framework.ProxyFactoryBean">
_ <property name="proxyInterfaces" value="foo.StaffDao">
_ <property name="target">
___ <bean parent="abstractDaoTarget">
_____ <constructor-arg>
_______ <value>foo.Staff</value>
_____ </constructor-arg>
___ </bean>
_ </property>
</bean>
和在 persistence.xml 加上 Staff class 的名字

4. DAO 就可以用了. 對, 沒有任何 implementation class, 就已經有一個 提供基本四個 CRUD operation 的 DAO 可以用了. 或者你會說, 這個 DAO 沒有什麼用, 最重要的 finder method 都沒有, 我要用 Staff name search Staff 怎麼做? 好的, 要加 finder methods, 只要:

5. 修改一下 StaffDao, 加進你想要的 finder method
public interface StaffDao extends GenericDao<Staff, Long> {
_ public void List<Staff> findStaffByName(String name);
}

6. 在 Staff class 前面加上

@NamedQueries({
_ @NamedQuery(name="Staff.findStaffByName",
______ query="from Staff s where s.name= :name")
})

一樣毋需任何 implementation, 你的 DAO 就已經提供了 finder.

怎樣達到這個效果, 下一篇會開始解釋. 文章開首的 link 提供了大概的做法.

(由於 Blogger.com 對於 source code 的顯示非常不方便, 一直吃掉我的 space, 所以大家就自己把 source code 前的 underscore 消去吧.)

No comments: