DAO和服务层(JPA / Hibernate + Spring)

我正在devise一个基于JPA / Hibernate,Spring和Wicket的新应用程序。 DAO和服务层之间的区别对我来说还不是很清楚。 根据维基百科,DAO是

一个为某种types的数据库或持久性机制提供抽象接口的对象,提供一些特定的操作而不暴露数据库的细节。

我想知道一个DAO是否可以包含对数据访问没有多大作用的方法,但使用查询更容易执行? 例如“获取在某一机场运营的所有航空公司的名单”? 这听起来更像是一个服务层方法,但我不确定在服务层中使用JPA EntityManager是否是一个良好实践的例子?

DAO应提供对单个相关数据源的访问权限,根据业务模型的复杂程度,将返回完整的业务对象或简单的数据对象。 无论哪种方式,DAO方法都应该在一定程度上反映数据库。

一个服务可以提供更高层次的接口,不仅可以处理您的业务对象,而且可以首先访问它们。 如果我从一个服务中获得一个业务对象,那么这个对象可以从不同的数据库(以及不同的DAO)创build,它可以用来自HTTP请求的信息进行修饰。 它可能有一定的业务逻辑,可以将多个数据对象转换为一个强健的业务对象。

我通常创build一个DAO,认为它将被任何将要使用该数据库的人使用,或者与业务相关的数据集合使用,它实际上是除了数据库中的触发器,函数和存储过程以外的最低级代码。

具体问题的答案:

我想知道一个DAO是否可以包含对数据访问没有多大作用的方法,但使用查询更容易执行?

对于大多数情况下不,您会希望您的服务层中有更复杂的业务逻辑,即单独查询中的数据汇编。 但是,如果您担心处理速度,则服务层可能会将操作委托给DAO,即使它破坏了模型的美感,就像C ++程序员编写汇编代码以加快某些操作一样。

这听起来更像是一个服务层方法,但我不确定在服务层中使用JPA EntityManager是否是一个良好实践的例子?

如果你打算在你的服务中使用你的实体pipe理器,那么把实体pipe理器想象成你的DAO,因为这正是它的原因。 如果你需要删除一些冗余的查询构build,不要在你的服务类中这样做,将其提取到一个使用实体pipe理器的类中,并将其作为你的DAO。 如果你的用例非常简单,那么你可以完全跳过服务层,并在控制器中使用实体pipe理器或DAO,因为你的服务要做的就是将getAirplaneById()findAirplaneById()

更新 – 为了澄清下面的讨论,在一个服务中使用一个实体pipe理器可能不是在大多数情况下最好的决定,因为在评论中突出显示的各种原因,还有一个DAO层。 但在我看来,这是完全合理的:

  1. 该服务需要与不同的数据集进行交互
  2. 至less有一组数据已经有一个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... } } 

有一点是肯定的:如果你在服务层上使用EntityManager,你不需要一个dao层(只有一层应该知道实现细节)。 除此之外,还有不同的看法:

  • 有人说EntityManager公开了所有需要的daofunction,所以他们将EntityManager注入服务层。
  • 其他人有一个传统的dao层支持接口(所以服务层不绑定到实现细节)。

第二种方法在分离问题时更加优雅,它也将使得从一个持久化技术切换到另一个更容易(只需要用新技术重新实现dao接口),但是如果你知道什么都没有会改变,第一个更容易。

我想说,如果你有一个小项目,在服务层使用JPA,但是在一个大项目中使用一个专用的DAO层。

亚当·比恩的这篇文章可能是有用的。

传统上你会编写接口来定义服务层和数据层之间的契约。 然后你写实现,这些是你的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. } } 

你也可以看看你的航空公司实体有一个列表,并定义与@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实例方面非常出色。

编辑:澄清了一些假设。

Dao是一个数据访问对象。 它在数据库上存储/更新/select实体。 实体pipe理器对象用于那个(至less在打开的jpa中)。 你也可以用这个实体pipe理器运行查询。 这不是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();