2010-10-07 105 views
61

我正在設計一個基於JPA/Hibernate,Spring和Wicket的新應用程序。雖然DAO和服務層之間的區別並不是很明顯。根據維基百科,DAO是DAO和服務層(JPA/Hibernate + Spring)

,提供了一個抽象 接口到某種類型的數據庫或 持久性機制的,而不暴露數據庫的 細節提供一些 特定的操作的對象。

我想知道一個DAO是否可以包含實際上不需要做太多數據訪問的方法,但使用查詢更容易執行?例如「獲取在某一機場上運營的所有航空公司的列表」?這聽起來更像是一種服務層方法,但我不確定在服務層中使用JPA EntityManager是否是良好實踐的例子?

回答

51

DAO應該提供對單個相關數據源的訪問權限,並且根據業務模型的複雜程度,將返回完整的Business對象或簡單數據對象。無論哪種方式,DAO方法都應該反映數據庫的有點緊密。

服務可以提供更高層次的接口,不僅可以處理您的業務對象,還可以首先訪問它們。如果我從服務中獲得業務對象,那麼可以從不同的數據庫(以及不同的DAO)創建該對象,它可以用來自HTTP請求的信息進行修飾。它可能具有某些業務邏輯,可將多個數據對象轉換爲單個強健的業務對象。

我通常會創建一個DAO,認爲它將被任何使用該數據庫或一組業務相關數據的人使用,它實際上是數據庫中除觸發器,函數和存儲過程之外的最低級代碼。

特定問題的答案:

我想知道一個DAO是否能夠 包含真的沒有 做很多與數據訪問方法,但使用查詢更容易執行 方式?

在大多數情況下沒有,你想在你的服務層的更復雜的業務邏輯,從單獨的查詢數據的組裝。但是,如果您擔心處理速度,服務層可能會將操作委託給DAO,即使它破壞了模型的美感,就像C++程序員編寫彙編代碼以加快某些操作的方式一樣。

這聽起來我要多 服務層的方法,但我不知道如果 在 服務層使用JPA的EntityManager好 實踐的例子嗎?

如果您打算在您的服務中使用您的實體管理器,那麼請將實體管理器視爲您的DAO,因爲這正是它的原因。如果你需要刪除一些冗餘的查詢構建,不要在你的服務類中這樣做,將它提取到一個使用實體管理器的類中,並將它作爲你的DAO。如果你的使用情況是非常簡單的,你可以完全跳過服務層,並使用你的實體管理器,或DAO的控制器,因爲所有的服務要做的是調用轉嫁給getAirplaneById()到DAO的findAirplaneById()

UPDATE - 要關於下面的討論澄清,在大多數情況下,在服務中使用實體管理器可能不是最佳決策,因爲在評論中突出強調的各種原因,還有DAO層。但在我看來,這將是完全合理的假設:

  1. 的服務需要不同的數據集進行交互
  2. 至少一個數據集已經有一個DAO
  3. 服務類駐留在一個模塊中這需要一些足夠簡單的持久性,以至於不能保證它自己的DAO

示例。

//some system that contains all our customers information 
class PersonDao { 
    findPersonBySSN(long ssn) 
} 

//some other system where we store pets 
class PetDao { 
    findPetsByAreaCode() 
    findCatByFullName() 
} 

//some web portal your building has this service 
class OurPortalPetLostAndFoundService { 

    notifyOfLocalLostPets(Person p) { 
     Location l = ourPortalEntityManager.findSingle(PortalUser.class, p.getSSN()) 
     .getOptions().getLocation(); 
     ... use other DAO's to get contact information and pets... 
    } 
} 
+1

感謝您的詳細解答。我只是想知道:是否可以同時擁有DAO集合並在服務層中使用EntityManager? – 2010-10-07 14:02:31

+0

我不認爲這有什麼問題,請記住Bohzo關於服務層是持久性不可知的說法。如果事情變得微不足道,我只需要一個使用實體管理器並處理所有實體的DAO。我從來沒有發現任何DAO特定於實體或表的常見模式的用法,我認爲DAO應該綁定到數據庫,如果該類變大,重構時顯然是多餘的 – walnutmon 2010-10-07 14:09:49

+0

好。我主要看到DAO與單個實體/表格緊密結合,認爲解耦會違反良好實踐。所以Qwerky的答案getAirlinesOperatingFrom()方法是好的? – 2010-10-07 14:20:54

0

Dao是一個數據訪問對象。它在數據庫上存儲/更新/選擇實體。實體管理器對象用於那個(至少在打開的jpa中)。您也可以使用此實體管理器運行查詢。這不是sql,而是JPQL(Java持久性查詢語言)。

簡單的例子:

emf = Persistence.createEntityManagerFactory("localDB"); 
em = emf.createEntityManager(); 

Query q = em.createQuery("select u from Users as u where u.username = :username", Users.class); 
q.setParameter("username", username); 

List<Users> results = q.getResultList(); 

em.close(); 
emf.close(); 
14

有一件事是肯定的:如果你在業務層上使用的EntityManager,你並不需要一個DAO層(只有一層應該知道實施細則)。除此之外,有不同意見:

  • 有人說了EntityManager暴露 所有需要的DAO功能,所以他們 在服務 層注入的EntityManager。
  • 其他有一個傳統的dao層 支持接口(所以服務 層不綁定到執行 細節)。

第二種方法是當它涉及到的關注點分離更優雅,也會使從一個持久化技術切換到另一個更容易(你只需要重新實現新技術的DAO接口),但如果你知道什麼都不會改變,那麼第一個會更容易。

我想說如果你有一個小項目,在服務層使用JPA,但是在一個大型的項目中使用專用的DAO層。

+0

在您的示例中,實體管理器是第一個示例中的DAO – walnutmon 2010-10-07 13:40:12

+0

,是 – 2010-10-07 13:42:46

+1

+1。事實上,在較大的項目中,服務層應該是持久性機制不可知的。 – Bozho 2010-10-07 13:46:22

4

傳統上你會編寫接口來定義服務層和數據層之間的契約。然後您編寫實現,這些是您的DAO。

回到你的例子。假設機場和航空公司之間的關係是多對多的,並且包含airport_id和airline_id的表格,您可能會有一個界面;

public interface AirportDAO 
{ 
    public List<Airline> getAirlinesOperatingFrom(Set<Airport> airports); 
} 

..你可能會提供一個Hibernate的實現;

public class HibernateAirportDAO implements AirportDAO 
{ 
    public List<Airline> getAirlinesOperatingFrom(Set<Airport> airports) 
    { 
     //implementation here using EntityManager. 
    } 
} 

您還可以考慮在您的航空公司實體上使用List,並使用@ManyToMany JPA註釋定義關係。這將消除完全使用此特定DAO方法的必要性。

您可能還想了解用於編寫DAO工廠的抽象工廠模式。例如;

public abstract class DAOFactory 
{ 
    private static HibernateDAOFactory hdf = new HibernateDAOFactory(); 

    public abstract AirportDAO getAirlineDAO(); 

    public static DAOFactory getFactory() 
    { 
     //return a concrete implementation here, which implementation you 
     //return might depend on some application configuration settings. 
    } 
} 

public class HibernateDAOFactory extends DAOFactory 
{ 
    private static EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("myPersistenceUnit"); 

    public static EntityManager getEM() 
    { 
     return emFactory.createEntityManager(); 
    } 

    public AirportDAO getAirportDAO() 
    { 
     return new HibernateAirportDAO(); 
    } 
} 

此模式允許您的HibernateDAOFactory保存單個EMF併爲EM提供單獨的DAO實例。如果你不想走低端路線,那麼Spring在處理DAO實例方面非常出色。

編輯:澄清了一些假設。

+0

是的,但不是混合了業務/服務和數據訪問層?我特指的是getAirlinesOperatingFrom()。或者這是一個好的做法?這是我在這個領域的第一個項目,所以我不太確定 – 2010-10-07 14:17:51

+1

這種工廠方法在Spring場景中沒有意義。 – 2010-10-07 14:45:51

+0

@seanizer是的,有了Spring,OP可能想要將他的DAO配置爲其應用上下文的一部分並注入它們。 – Qwerky 2010-10-07 15:10:01