Hibernate和Spring - Dao,Services

14

我一直在阅读一些教程,发现大多数MVC实现都基于以下几个方面:

1)DAO接口,例如“IUserDao”

2)该接口的DAO实现 - “mySimpleUserDaoImpl”

3)用于持久化的服务接口:“IUserService”

4)以及它的实现 - “UserServiceImpl”

这是最佳实践吗?我的意思是,我问这个问题的原因是因为拥有30个服务,其中包含getXById()、deleteX(x)、createX(x)等方法,这似乎是多余的,而且这些服务基本上做的事情都差不多。

请注意,我正在使用Spring 3和Hibernate 4,所以我决定在开始编写代码之前先问一下这个问题。

谢谢。


2
您可以省略接口名称前的 I 前缀。用户不应该意识到它正在获取一个接口。只需称其为 UserDAOUserService 即可。 - Bart
同意,接口中的I可以省略。我个人将我的Dao类称为Repository,只是为了与Spring注释保持一致,但这并不重要。 - bh5k
1
技术教程通常不涉及业务逻辑,因此它们不是良好的参考资料。在https://dev59.com/CnA65IYBdhLWcg3wrgsa上有一个类似的问题可能对您有帮助。 - Nathan Hughes
关于服务层的另一点需要注意的是,这是您的@Transaction应该开始的地方。因此,如果您想要在多个存储库之间回滚事务,则应在服务层中执行此操作。这就是为什么服务应该与存储库呈一对多关系的原因。 - bh5k
4个回答

10
如果您刚开始进行开发,请了解Spring JPA。服务应该是一对多的存储库(DAO)。但我也不会再手动创建所有那些样板代码了。Spring JPA消除了基本的CRUD和搜索功能以及分页。 这里有一个视频,演示了Spring、JPA、Hibernate的所有配置,并最终展示了Spring Data JPA消除的所有样板代码。
要使用Spring Data JPA,您的存储库接口最终如下所示:
package com.mysampleapp.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.wcfgroup.model.Employee;

@Repository("employeeRepository")
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    Employee findBySsn(String ssn);
}

接下来是使用Spring Data JPA的XML配置:

<jpa:repositories base-package="com.mysampleapp.repository"/>

现在,所有的样板代码都已经为您处理了。您不再需要创建一个带有查找方法和基本CRUD函数的基本存储库类。 JpaRepository 接口提供了许多不错的功能,您无需进行任何实现。


1
关于服务层的另一件需要注意的事情是,那里是您的 @Transaction 应该启动的地方。因此,如果您想要在多个存储库中回滚事务,则应在服务层中执行。这就是为什么服务应该与存储库进行一对多关联的原因。 - bh5k

4

每个模型都需要有dao、daoImpl、service和serviceImpl。

  • UserDao
  • UserDaoImpl
  • UserService
  • UserServiceImpl

您可以使用一个通用的类EntityDaoImpl和接口EntityDao,如下所示:

EntityDao:

public interface EntityDao<E> {

void persist(E e) throws Exception;

void remove(Object id) throws Exception;

E findById(Object id) throws Exception;
}

实体Dao实现:

public class EntityDaoImpl<E>  implements EntityDao<E> {

@PersistenceContext(unitName="UnitPersistenceName")
protected EntityManager entityManager;

protected E instance;
private Class<E> entityClass;

@Transactional
public void persist(E e) throws HibernateException{     
    getEntityManager().persist(e);
}
    @Transactional
public void remove(Object id) throws Exception{     
    getEntityManager().remove((E)getEntityManager().find(getEntityClass(), id));
}

public E findById(Object id) throws Exception {     
    return (E)getEntityManager().find(getEntityClass(), id);    
}
    public EntityManager getEntityManager() {
    return entityManager;
}
public void setEntityManager(EntityManager entityManager) throws Exception{
    this.entityManager = entityManager;
}

    public Class<E> getEntityClass() throws Exception{      
   if (entityClass == null) {
            Type type = getClass().getGenericSuperclass();
          if (type instanceof  ParameterizedType) 
          {
              ParameterizedType paramType = (ParameterizedType) type;
              if (paramType.getActualTypeArguments().length == 2) {
                    if (paramType.getActualTypeArguments()[1] instanceof  TypeVariable) {
                       throw new IllegalArgumentException(
                          "Can't find class using reflection");
                   }
                    else {
                       entityClass = (Class<E>) paramType.getActualTypeArguments()[1];
                  }
               } else {
                  entityClass = (Class<E>) paramType.getActualTypeArguments()[0];
                }
           } else {
              throw new Exception("Can't find class using reflection");
          }
        }
       return entityClass;
   }
}

您可以这样使用:
public interface UserDao extends EntityDao<User> {

}

并且

public class UserDaoImpl extends EntityDaoImpl<User> implements UserDao{

}

1
所有这些代码本质上都是Spring Data Jpa为您提供的:http://www.springsource.org/spring-data/jpa - bh5k
1
这段代码实际上存在一些错误,你不应该在DAO/Repository层开始事务。 - bh5k

1
没有必要拥有30个服务层或30个数据访问层。你应该按照业务功能来指定层,而不是按实体来指定。对于某些功能而言,可能只涉及5或6个实体,这些实体应该放在同一层中。但是,如果这些冗余层需要getXById(),deleteX(x),createX(x)等函数,则仍然需要在这些函数中添加这些函数。

1
创建一个单独的服务来处理每个模型是一种方法,我见过真实世界中使用这种方法的应用程序,但我不推荐这样做。对于简单的CRUD案例来说,这是过度设计,对于更复杂的情况(您实际上需要事务跨越多个DAO调用的情况),也没有帮助。这会留下很多没有什么作用的代码。DAO可以使用Spring Data指定,服务将是样板文件,每个方法都包装了对DAO的调用。Spring应该有助于减少样板文件,而不是强制要求它。
如果您有完全的CRUD应用程序,则可以合法地不使用服务,在DAO上放置@Transactional,并从控制器调用DAO。如果服务没有发挥作用,请将其删除。
如果应用程序中确实有实际的业务逻辑,那么当用户调用可能涉及到对不同DAO的调用时,这些调用通常应在同一事务中进行,以便如果一件事情失败,所有事情都会被回滚。(创建事务也相对昂贵,如果您有一个控制器为不同实体调用不同服务,则速度会很慢。)这就是服务派上用场的地方,服务指定用户想要执行的低级用例,并允许指定事务行为。

对于CRUD案例,我更喜欢使用像Grails或Play或Rails这样的工具而不是编写Spring MVC的Java代码。例如,Grails会为您生成视图和控制器,通过GORM提供基本数据访问(因此无需编写DAO),并且它可以让您轻松地指定服务,以应对您需要它们的情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接