作為JavaEE5 規范一部分的JPA,已經有眾多的實現,比如:Hiberante,TopLink, EclipseLink和OpenJPA 等許多ORM框架。JPA不僅僅應用於EJB3中,你也可以應用EJB3之外的應用,比如Spring 中。甚至Gavin King Hiberante的作者 在Hiberante in Action 的第二版書中也推薦使用JPA。很明顯,JPA將被廣泛的應用。
一旦你熟悉annotation,並嘗試過Hello world之類的程序後,你會感覺,恩 JPA確實不錯。然後,當你在你的項目中開始應用JPA時,你會發現其實並不那麼簡單,你需要考慮如何處理事務,惰性加載,移除對象實例,繼承等等。這些問題會在本系列中給你答案。
首先進入第一的設計模式 DAO。
我們需要DAO嗎?
在二年前,我們已經討論了這個問題了,討論的結果是:是否使用DAO依賴於你的應用,類似於GOF的設計模式,如果是很簡單的應用程序,應用設計模式,只能增加你的復雜度。 而對於復雜應用程序,正因為其復雜,應用設計模式可以減低復雜度,提高可維護性。
應用了DAO我們可以得到如下好處:
1.避免在任何存取數據的代碼中引入EntityManager,減少了依賴。
2.對某些實體Bean操作增加限制。比如你不想對LogEntry提供刪除操作,應用DAO,你僅僅做的是不要在LogEntry DAO中不要添加remove的方法。
3.理論上,應用DAO層,你一個自由切換其他的持久機制比如純JDBC或者Ibatis.而實際上,JPA已經是一個抽象層,這種切 換是沒有意義的。
4.你可以在一個實體中集中所有的查詢避免這些查詢在其他代碼中出現。你可以使用named queries 在實體類中查詢,但是您仍然需要正確的參數被設置。對應這種查詢,你可以在DAO中設置參數,然後轉換成正確的返回類型,比如:
public List<ChangePlan> findExecutingChangePlans() {
Query query = entityManager.createQuery(
"SELECT plan FROM ChangePlan plan where plan.state = 'EXECUTING'");
return (List<ChangePlan>) query.getResultList();
}
然後,但你決定使用DAO時,如何正確合理的使用它呢? 在Spring的JpaTemplate的JavaDoc中建議,不要使用類似特殊的類,類似JpaDaoSupport。 而是你應該自己通過@PersistenceContext注釋獲取EntityManger來維護你的DAO。這種方式可以在EJB3的容器中工作,並且如果你在Spring的context 加入PersistenceAnnotationBeanpostProcessor Bean的話,在Spring2.0裡也是沒有問題的。
類型安全且泛化的DAO pattern
因為每一個DAO都包含一些相同的邏輯,所以我們應該抽取這些邏輯放入到父類中。
實體類
比如,我們想實例化OrderClass:
@Entity
@Table(name = "ORDERS")
public class Order {
@Id
@GeneratedValue
private int id;
private String customerName;
private Date date;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getCustomerName() { return customerName; }
public void setCustomerName(String customerName) { this.customerName = customerName; }
public Date getDate() { return date; }
public void setDate(Date date) { this.date = date;}
DAO 接口
首先我們定義一個泛化的DAO接口,包括最常用的方法,比如:
public interface Dao<K, E> {
void persist(E entity);
void remove(E entity);
E findById(K id);
}
K ,E為泛型,你可以加其他的方法比如:List findAll().
然後我們定義一個自接口,實現特殊的方法,比如,我們想查找一定條件的Order信息:
public interface OrderDao extends Dao<Integer, Order> {
List<Order> findOrdersSubmittedSince(Date date);
}
基本DAO的實現
第三步就是創建基本的JPA DAO的實現。它實現了Dao接口。
public abstract class JpaDao<K, E> implements Dao<K, E> {
protected Class<E> entityClass;
@PersistenceContext
protected EntityManager entityManager;
public JpaDao() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class<E>) genericSuperclass.getActualTypeArguments()[1];
}
public void persist(E entity) { entityManager.persist(entity); }
public void remove(E entity) { entityManager.remove(entity); }
public E findById(K id) { return entityManager.find(entityClass, id); }
上述實現是直接明了的,大家還要注意幾點:
JpaDao的構造方法是通過ParameterizedType獲取具體的實體類。
通過@PersistenceContext 獲取EntityManager
entityClass和EntityManager是Protected類型,子類可以直接訪問。
特殊的DAO實現
最後,我們創建特殊的DAO,它擴張了基本的DAO接口實現了OrderDAO接口
public class JpaOrderDao extends JpaDao<Integer, Order> implements OrderDao {
public List<Order> findOrdersSubmittedSince(Date date) {
Query q = entityManager.createQuery(
"SELECT e FROM " + entityClass.getName() + " e WHERE date >= :date_since");
q.setParameter("date_since", date);
return (List<Order>) q.getResultList();
DAO的使用
在EJB3中,
@EJB(name="orderDao")
private OrderDao orderDao;
在Spring中,我們使用XML bean 文件,或者通過autowiring 如下:
@Autowired
public OrderDao orderDao;
總之。當我們獲取到了DAO的引用後,我們可以如下使用:
Order o = new Order();
o.setCustomerName("Peter Johnson");
o.setDate(new Date());
orderDao.persist(o);
當然,我們可以再OrderDao接口中加入其它的查詢:
List<Order> orders = orderDao.findOrdersSubmittedSince(date);
for (Order each : orders) {
System.out.println("order id = " + each.getId());
}
總之,使用了類型安全的DAO Pattern有如下優勢:
1.從Client的角度講,不會直接依賴於JPA的API
2.通過泛型實現類型安全,避免Cast異常。
3.集中處理所有跟JPA相關的代碼。
4.可以在集中點上加入事務,log 性能測試等邏輯。
5.通過一個類可以測試數據庫訪問代碼。
希望這些可以說服你使用DAO Pattern .接下來我們將討論Bidirectional assocations 雙向關聯模式。