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

时间:2010-10-07 13:30:17

标签: java spring architecture jpa dao

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

  

提供摘要的对象   接口到某种类型的数据库或   持久性机制,提供一些   具体操作没有暴露   数据库的详细信息。

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

5 个答案:

答案 0 :(得分:52)

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

服务可以提供更高级别的界面,不仅可以处理您的业务对象,还可以首先访问它们。如果我从服务获取业务对象,那么该对象可以从不同的数据库(和不同的DAO)创建,它可以用来自HTTP请求的信息进行修饰。它可能具有某些业务逻辑,可将多个数据对象转换为单个健壮的业务对象。

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

特定问题的答案:

  

我想知道DAO是否可以   包含实际上没有的方法   要做很多数据访问,但是   使用查询更容易执行?

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

  

听起来我更像是一个   服务层方法,但我不确定   如果在中使用JPA EntityManager   服务层是一个很好的例子   练?

如果您打算在服务中使用您的实体管理器,那么请将实体管理器视为您的DAO,因为这正是它的本质。如果您需要删除一些冗余查询构建,请不要在服务类中执行此操作,将其解压缩到使用实体管理器的类中,并将其设置为DAO。如果您的用例非常简单,您可以完全跳过服务层,并在控制器中使用您的实体管理器或DAO,因为您的所有服务都要将getAirplaneById()的调用传递给DAO的{{1} }

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

  1. 服务需要与不同的数据集进行交互
  2. 至少有一组数据已经有DAO
  3. 服务类驻留在一个需要一些持久性的模块中,这个模块很简单,不能保证它自己的DAO
  4. 例如

    findAirplaneById()

答案 1 :(得分:14)

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

  • 有人说EntityManager公开了 所有需要的dao功能,所以他们 在服务中注入EntityManager 层。
  • 其他人有传统的dao层 接口支持(所以服务 图层与实现无关 详情)。

第二种方法在关注点分离方面更优雅,它也可以更容易地从一种持久性技术切换到另一种持久性技术(你只需要用新技术重新实现dao接口),但是如果你知道什么都不会改变,第一个更容易。

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

答案 2 :(得分:5)

Adam Bien的这个article可能有用。

答案 3 :(得分: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.
   }
}

您还可以查看您的航空公司实体上的列表,并使用@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实例。

编辑:澄清了几个假设。

答案 4 :(得分:0)

Dao是一个数据访问对象。它确实在数据库上存储/更新/选择实体。实体管理器对象用于此(至少在open 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();