如何在一个很好地解耦的服务层和数据访问层中使用EntityManager?

Pet*_*ter 10 hibernate jpa data-access-layer entitymanager service-layer

与我的另一个问题有点相关应该从数据访问层或接口返回原始的Hibernate注释POJO吗?,我在创建很好的分离层方面很有经验,但没有使用Hibernate或J2EE/JPA.我一直在查看文档和教程,并对如何以优雅的方式使用EntityManger感到困惑,因为它似乎负责两个事务(我想在我的服务层执行)和持久性方法(我想要的)保持在数据访问层).我应该在服务层创建它并将其注入数据访问层,还是有更好的方法?下面的伪java大致显示了我正在考虑做的事情.

编辑:我的下面的伪代码主要取自hibernate JPA教程,并针对图层分离进行了修改,并未反映出该产品是在EJB容器(Glassfish)中运行的.在您的答案中,请提供在Glassfish中运行的代码或同等代码的最佳实践和代码示例.

MyService
{

  setup()
  {
       EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "Something" ); //is the String you pass in important?
       entityManager = entityManagerFactory.createEntityManager();
  }

  myServiceMethod()
   {
   entityManager.getTransaction().begin();
   MyDao.setEntityManager(entityManagerFactory); 
   MyDao.doSomething();
   MyDao.doSomethingElse();
   entityManager.getTransaction().commit();
   entityManager.close();
   }
 }

MyDao
{
   doSomething()
    {
     entityManager.persist(...); //etc 
    }

}
Run Code Online (Sandbox Code Playgroud)

Gon*_*gui 13

首先,你是否应该使用DAO层是一个自JPA和EntityManager出现以来一直存在的争论,许多人认为DAO本身就是这样.答案取决于您正在开发的应用程序类型,但在大多数情况下,您需要:

  • 使用JPA条件或自定义查询.在这种情况下,您可能不希望将业务逻辑与查询创建混合在一起.这将导致大量方法,并将违反单一责任原则.
  • 尽可能重复使用JPA代码.假设您创建一个条件查询,该查询检索年龄在40到65岁之间并且在公司工作超过10年的员工列表.你可能会想重新使用这种类型的查询别的地方在你的服务层,如果是这样的话,有它的服务将让这个任务变得困难.

话虽这么说,如果您的应用程序中只有CRUD操作,并且您认为您可能不需要重用任何JPA代码,那么DAO层可能有些过分,因为它将仅仅作为EntityManager的包装器,它不会听起来不错.

其次,我建议尽可能使用容器管理的事务.如果您使用的是像TomEE或JBoss这样的EJB容器,这将避免大量专用于以编程方式创建和管理事务的代码.

在使用en EJB容器的情况下,您可以利用声明式事务管理.使用DAO的一个例子是将服务层组件创建为EJB和DAO.

@Stateless
public class CustomerService {

    @EJB
    CustomerDao customerDao;

    public Long save(Customer customer) {

        // Business logic here
        return customerDao.save(customer);
    }
}

@Stateless
public class CustomerDao {

    @PersistenceContext(unitName = "unit")
    EntityManager em;

    public Long save(Customer customer) {
        em.persist(customer);
        return customer.getId();
    }

    public Customer readCustomer(Long id) {
            // Criteria query built here
    }

}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,默认事务配置是REQUIRED,这意味着如果调用者组件中没有事务,EJB将创建一个新事务.如果调用者已经创建了一个事务(CustomerService),则被调用的组件(CustomerDao)将继承该事务.这可以使用@TransactionAttribute批注进行自定义.

如果您没有使用EJB容器,我认为您上面的示例可能是等效的.

编辑:为了简单起见,我使用了上面的无接口EJB,但为这些接口使用接口以便使它们更易于测试是一种很好的做法.