首页 文章

DAO和服务层(JPA / Hibernate Spring)[重复]

提问于
浏览
62

这个问题在这里已有答案:

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

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

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

5 回答

  • 51

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

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

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

    Answers to specific questions:

    我想知道DAO是否可以包含对数据访问不需要做太多的方法,但使用查询更容易执行?

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

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

    如果你're going to use your entity manager in your service, then think of the entity manager as your DAO, because that'确切地说它是什么 . 如果您需要删除一些冗余查询构建,请不要在服务类中执行此操作,将其解压缩到使用实体管理器的类中,并将其设置为DAO . 如果您的用例非常简单,您可以完全跳过服务层并使用您的实体管理器或控制器中的DAO,因为您的所有服务都要将调用 getAirplaneById() 调用到DAO的 findAirplaneById()

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

    • 该服务需要与不同的数据集交互

    • 至少有一组数据已经有DAO

    • 服务类驻留在一个需要一些持久性的模块中,这个模块很简单,不能保证它自己的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...
       }
    }
    
  • 14

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

    • 有人说EntityManager公开了所有需要的dao功能,因此他们将EntityManager注入服务层 .

    • 其他人有一个由接口支持的传统dao层(因此服务层与实现细节无关) .

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

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

  • 4

    Adam Bien的这个article可能很有用 .

  • 0

    传统上,您可以编写定义服务层和数据层之间 Contract 的接口 . 然后编写实现,这些是您的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实例 .

    编辑:澄清了几个假设 .

  • 5

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

相关问题