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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3882108/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 06:19:40  来源:igfitidea点击:

DAO and Service layers (JPA/Hibernate + Spring)

javaspringarchitecturejpadao

提问by John Manak

I'm designing a new app based on JPA/Hibernate, Spring and Wicket. The distinction between the DAO and Service layers isn't that clear to me though. According to Wikipedia, DAO is

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

an object that provides an abstract interface to some type of database or persistence mechanism, providing some specific operations without exposing details of the database.

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

I was wondering whether a DAO could contain methods that don't really have to do much with data access, but are way easier executed using a query? For example "get a list of all airlines that operate on a certain set of airports"? It sounds to me to be more of a service-layer method, but I'm not sure if using JPA EntityManager in the service layer is an example of good practice?

我想知道 DAO 是否可以包含与数据访问无关的方法,但是使用查询执行起来更容易吗?例如“获取在一组特定机场运营的所有航空公司的列表”?在我看来,它更像是一种服务层方法,但我不确定在服务层中使用 JPA EntityManager 是否是良好实践的一个例子?

采纳答案by walnutmon

A DAO should provide access to a single relatedsource of data and, depending on how complicated your business model, will return either full fledged Business objects, or simple Data objects. Either way, the DAO methods should reflect the database somewhat closely.

DAO 应该提供对单个相关数据源的访问,并且根据您的业务模型的复杂程度,将返回完整的业务对象或简单的数据对象。无论哪种方式,DAO 方法都应该更接近地反映数据库。

A Service can provide a higher level interface to not only process your business objects, but to get access to them in the first place. If I get a business object from a Service, that object may be created from different databases (and different DAO's), it could be decorated with information made from an HTTP request. It may have certain business logic that converts several data objects into a single, robust, business object.

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

I generally create a DAO thinking that it will be used by anyone who is going to use that database, or set of business related data, it is literally the lowest level code besides triggers, functions and stored procedures within the database.

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

Answers to specific questions:

具体问题的回答:

I was wondering whether a DAO could contain methods that don't really have to do much with data access, but are way easier executed using a query?

我想知道 DAO 是否可以包含与数据访问无关的方法,但是使用查询执行起来更容易吗?

for most cases no, you would want your more complicated business logic in your service layer, the assembly of data from separate queries. However, if you're concerned about processing speed, a service layer may delegate an action to a DAO even though it breaks the beauty of the model, in much the same way that a C++ programmer may write assembler code to speed up certain actions.

在大多数情况下,不,您希望在服务层中使用更复杂的业务逻辑,即来自单独查询的数据的组装。但是,如果您关心处理速度,服务层可能会将操作委托给 DAO,即使它破坏了模型的美感,这与 C++ 程序员可能编写汇编代码以加快某些操作的方式非常相似。

It sounds to me to be more of a service-layer method, but I'm not sure if using JPA EntityManager in the service layer is an example of good practice?

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

If you're going to use your entity manager in your service, then think of the entity manager as your DAO, because that's exactly what it is. If you need to remove some redundant query building, don't do so in your service class, extract it into a class that utilized the entity manager and make that your DAO. If your use case is really simple, you could skip the service layer entirely and use your entity manager, or DAO in controllers because all your service is going to do is pass off calls to getAirplaneById()to the DAO's findAirplaneById()

如果您打算在您的服务中使用实体管理器,那么将实体管理器视为您的 DAO,因为它正是如此。如果您需要删除一些冗余的查询构建,请不要在您的服务类中这样做,将其提取到使用实体管理器的类中并将其设为您的 DAO。如果您的用例真的很简单,您可以完全跳过服务层并使用实体管理器或控制器中的 DAO,因为您的所有服务要做的就是将调用传递getAirplaneById()给 DAOfindAirplaneById()

UPDATE - To clarify with regard to the discussion below, using an entity manager in a service is likely not the best decision in most situations where there is also a DAO layer for various reasons highlighted in the comments. But in my opinion it would be perfectly reasonable given:

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

  1. The service needs to interact with different sets of data
  2. At least one set of data already has a DAO
  3. The service class resides in a module that requires some persistence which is simple enough to not warrant it's own DAO
  1. 服务需要与不同的数据集进行交互
  2. 至少一组数据已经有 DAO
  3. 服务类驻留在一个需要一些持久性的模块中,这个模块很简单,不能保证它自己的 DAO

example.

例子。

//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...
   }
}

回答by Mark Baijens

Dao is a data access object. It does storing/updating/selecting entities on the database. The entity manager object is used for that (at least in open jpa). You can also run query's with this entity manager. It's no sql but JPQL (Java persistence query language).

Dao 是一个数据访问对象。它在数据库上存储/更新/选择实体。实体管理器对象用于(至少在 open jpa 中)。您还可以使用此实体管理器运行查询。它不是 sql,而是 JPQL(Java 持久性查询语言)。

Simple example:

简单的例子:

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();

回答by Sean Patrick Floyd

One thing is certain: if you use EntityManager on the service layer, you don't need a dao layer (only one layer should know implementation details). Apart from that, there are different opinions:

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

  • Some say the EntityManager exposes all needed dao functionality, so they inject EntityManager in the service layer.
  • Others have a traditional dao layer backed by interfaces (so the service layer is not tied to implementation details).
  • 有人说 EntityManager 公开了所有需要的 dao 功能,因此他们将 EntityManager 注入服务层。
  • 其他人有一个由接口支持的传统 dao 层(因此服务层不依赖于实现细节)。

The second approach is more elegant when it comes to separation of concerns and it also will make switching from one persistence technology to the other easier (you just have to re-implement the dao interfaces with the new technology), but if you know that nothing will change, the first is easier.

当涉及到关注点分离时,第二种方法更优雅,它也将使从一种持久性技术切换到另一种更容易(您只需要使用新技术重新实现 dao 接口),但如果您不知道会改变,第一个更容易。

I'd say if you have a small project, use JPA in the service layer, but in a large project use a dedicated DAO layer.

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

回答by Qwerky

Traditionally you would write interfaces that define the contract between your service layer and data layer. You then write implementations and these are your DAOs.

传统上,您会编写接口来定义服务层和数据层之间的契约。然后您编写实现,这些就是您的 DAO。

Back to your example. Assuming the relationship between Airport and Airline is many to many with a table containing airport_id and airline_id you might have an interface;

回到你的例子。假设 Airport 和 Airline 之间的关系是多对多的,有一个包含 airport_id 和 airport_id 的表,你可能有一个接口;

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

..and you might provide a Hibernate implementation of this;

..你可能会提供一个 Hibernate 实现;

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

You could also look into having a List on your Airline entity and defining the relationship with a @ManyToMany JPA annotation. This would remove the necessity to have this particular DAO method altogether.

您还可以考虑在您的 Airline 实体上创建一个 List 并定义与 @ManyToMany JPA 注释的关系。这将完全消除使用此特定 DAO 方法的必要性。

You might also want to look into the Abstract Factory pattern for writing DAO factories. For example;

您可能还想了解用于编写 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();
   }
}

This pattern allows your HibernateDAOFactory to hold a single EMF and supply individual DAO instances with EMs. If you don't want to go down the fatory route then Spring is great at handling DAO instances for you with dependancy injection.

这种模式允许您的 HibernateDAOFactory 保存单个 EMF 并为单个 DAO 实例提供 EM。如果您不想走死路,那么 Spring 非常擅长通过依赖注入为您处理 DAO 实例。

Edit: Clarified a couple of assumptions.

编辑:澄清了几个假设。

回答by Onur

This articleby Adam Bien might be useful.

Adam Bien 的这篇文章可能有用。