Hibernate:CRUD通用DAO

36

我的Web应用程序有很多服务表/实体,例如payment_methodstax_codesprovince_codes等。

每次我添加一个新实体时,都必须编写一个DAO。问题是,基本上它们都是相同的,唯一的区别就是实体类本身

我知道Hibernate工具可以自动生成代码,但现在我不能使用它们(不要问为什么),所以我想到了一个通用DAO。有很多文献介绍这方面的内容,但我无法将其与Spring结合起来运行。

我认为这都涉及到泛型,它将具有四种基本方法:

  • listAll
  • saveOrUpdate
  • deleteById
  • getById

就是这些。


问题:

如何避免重复造轮子?难道没有现成的东西可以使用吗?

答:对于您提出的问题,有一些可用的解决方案。在Java中,有一些框架可以帮助您避免编写重复的DAO,例如Spring Data JPA和MyBatis。这些框架提供了一种通用的ORM解决方案,可以自动生成基本的CRUD(创建、读取、更新和删除)操作。如果您不想使用这些框架,您还可以编写自己的泛型DAO类,该类包含通用的CRUD方法,并将它们与您的实体类一起使用。无论哪种方式,都可以避免重复编写相同的DAO代码,并提高开发效率。

2
在谷歌的搜索结果中排名第一的是 http://code.google.com/p/hibernate-generic-dao/,它应该为您提供一个良好的基础。这是另一个链接:http://www.ibm.com/developerworks/java/library/j-genericdao/index.html - hvgotcodes
注意:各位开发者,自2011年12月起,我不再支持这个项目。从一开始,我一直是这个项目的唯一所有者,但我将不再跟进问题或发布新版本。目前没有其他人接手这个责任,所以这将不会发生。仍然有一些用户在Google群组上回应。对此给您带来的不便,我深感抱歉。... - Fabio B.
取自主页..........你认为它有多可靠? :-) - Fabio B.
3
天哪,老兄,谷歌上的第四个选项怎么办?即使它不受支持,你仍然可以将其用作指南。 - hvgotcodes
5个回答

44
这里是我的内容。
@Component
public class Dao{

    @Resource(name = "sessionFactory")
    private SessionFactory sessionFactory;

    public <T> T save(final T o){
      return (T) sessionFactory.getCurrentSession().save(o);
    }


    public void delete(final Object object){
      sessionFactory.getCurrentSession().delete(object);
    }

    /***/
    public <T> T get(final Class<T> type, final Long id){
      return (T) sessionFactory.getCurrentSession().get(type, id);
    }

    /***/
    public <T> T merge(final T o)   {
      return (T) sessionFactory.getCurrentSession().merge(o);
    }

    /***/
    public <T> void saveOrUpdate(final T o){
      sessionFactory.getCurrentSession().saveOrUpdate(o);
    }

    public <T> List<T> getAll(final Class<T> type) {
      final Session session = sessionFactory.getCurrentSession();
      final Criteria crit = session.createCriteria(type);
  return crit.list();
    }
// and so on, you shoudl get the idea

然后在服务层中您可以这样访问:

 @Autowired
    private Dao dao;

   @Transactional(readOnly = true)
    public List<MyEntity> getAll() {
      return dao.getAll(MyEntity.class);
    }

1
你能否编辑你的回答,提供一个真实世界 DAO 的 Spring XML Bean 配置? - Fabio B.
@NimChimpsky 为什么你使用了 SessionFactory 而不是 EntityManager?哪种方法更好? - Woland
如果我的所有表格中都没有长整型作为ID,该怎么办? - Jumi
@Jumi 他们可能应该这样做。 - NimChimpsky
@NimChimpsky 是的,我也试过那种方法。但我更喜欢通用存储库的想法。就像这样:public class UserRepository extends AbstractRepository<User,Long>。不过我还没有找到在weblogic容器中可行的解决方案。话说我是从php symfony转来的,现在才开始了解java和java ee。也许是symfony的约定让我认为这是更好的方式。 - Jumi

26

Spring Data JPA 是一个非常好的项目,可以为您生成 DAO 并提供更多功能!您只需要创建一个接口(无需任何实现):

interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {}

通过继承JpaRepository,这个接口将自动提供以下功能:
PaymentMethod save(PaymentMethod entity);
Iterable<PaymentMethod> save(Iterable<? extends PaymentMethod> entities);
PaymentMethod findOne(Integer id);
boolean exists(Integer id);
Iterable<PaymentMethod> findAll();
long count();
void delete(Integer id);
void delete(PaymentMethod entity);
void delete(Iterable<? extends PaymentMethod> entities);
void deleteAll();
Iterable<PaymentMethod> findAll(Sort sort);
Page<PaymentMethod> findAll(Pageable pageable);
List<PaymentMethod> findAll();
List<PaymentMethod> findAll(Sort sort);
List<PaymentMethod> save(Iterable<? extends PaymentMethods> entities);
void flush();
PaymentMethod saveAndFlush(PaymentMethods entity);
void deleteInBatch(Iterable<PaymentMethods> entities);

这个接口是强类型的(泛型),并且会自动为您实现。对于每个实体,您只需要创建一个扩展JpaRepository<T,Integer extends Serializable>的接口即可。

但是请稍等,还有更多!假设您的PaymentMethod具有namevalidSince持久字段。如果您将以下方法添加到您的接口中:

interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {

  Page<PaymentMethod> findByNameLikeAndValidSinceGreaterThan(
    String name, Date validSince, Pageable page
  );

}

该框架将解析方法名称:

findBy (Name like) And (ValidSince greater than)

创建JPA QL查询,应用分页和排序(Pageable page)并为您运行它。无需实现:

paymentMethodsDao.findByNameLikeAndValidSinceGreaterThan(
  "abc%",
  new Date(),
  new PageRequest(0, 20, Sort.Direction.DESC, "name"
);

生成的查询语句:

SELECT *  //or COUNT, framework also returns the total number of records
FROM PaymentMethods
WHERE name LIKE "abc%"
  AND validSince > ...

并且应用分页。

唯一的缺点是这个项目比较新,容易遇到问题(不过它正在积极开发中)。


你好,能否展示一下使用PaymentMethodsDao实现的代码片段?我无法想象出如何获得一个运行示例来使用这个接口...谢谢! - Mario David
@MarioDavid:你只需将PaymentMethodsDao注入到其他bean中并使用它。Spring会为你实现它。 - Tomasz Nurkiewicz

4

您可以使用通用DAO作为其他特定领域DAO类的杠杆。假设您有一个员工领域类:

  @Entity
  @Table(name="employee")
  public class Employee {

    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @Column(name="emp_name")
    private String empName;

    @Column(name="emp_designation")
    private String empDesignation;

    @Column(name="emp_salary")
    private Float empSalary;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpDesignation() {
        return empDesignation;
    }

    public void setEmpDesignation(String empDesignation) {
        this.empDesignation = empDesignation;
    }

    public Float getEmpSalary() {
        return empSalary;
    }

    public void setEmpSalary(Float empSalary) {
        this.empSalary = empSalary;
    }


}

如果需要的话,通用DAO将看起来像这样:

通用DAO接口:

 public interface GenericRepositoryInterface<T> {

    public T save(T emp);
    public Boolean delete(T emp);
    public T edit(T emp);
    public T find(Long empId);
}

通用DAO实现:

@Repository
public class GenericRepositoryImplementation<T> implements GenericRepositoryInterface<T> {

protected EntityManager entityManager;
private Class<T> type;

public GenericRepositoryImplementation() {
    // TODO Auto-generated constructor stub

}

public GenericRepositoryImplementation(Class<T> type) {
    // TODO Auto-generated constructor stub

    this.type = type;
}

public EntityManager getEntityManager() {
    return entityManager;
}

@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
    this.entityManager = entityManager;
}
@Override
public T save(T emp) {
    // TODO Auto-generated method stub
    entityManager.persist(emp);
    entityManager.flush();
    return emp;
}

@Override
public Boolean delete(T emp) {
    // TODO Auto-generated method stub
    try {
         entityManager.remove(emp);
    } catch (Exception ex) {
        return false;
    }
    return true;
}

@Override
public T edit(T emp) {
    // TODO Auto-generated method stub
    try{
       return entityManager.merge(emp);
    } catch(Exception ex) {
        return null;
    }
}

@Override
public T find(Long empId) {
    // TODO Auto-generated method stub
    return (T) entityManager.find(Employee.class, empId);
}
} 

这个通用的DAO类需要被每个特定领域的DAO类继承。特定领域的DAO类甚至可以实现另一个接口,用于不常见的操作。并且更倾向于使用构造函数发送类型信息。


为什么不使用第一个示例,然后从中继承? - Edward Kennedy
如果我理解正确的话,您的意思是继承GenericRepositoryImplementation。这就是要做的事情。现在,除了实现自己的接口之外,Employee特定的DAO还必须从GenericRepositoryImplementation类中继承通用方法。 - Shahid Yousuf

4
不要为每个实体编写特定的DAO。您可以实现一个通用的DAO,它可以为您需要的所有实体执行90%的工作。在某些情况下,您可以扩展它以对特定实体进行特殊处理。
在我目前工作的项目中,我们有这样一个DAO,它包装了Hibernate会话并提供类似于您描述的方法。此外,我们正在使用ISearch API——这是一个托管在Google code上的开源项目,为Hibernate和JPA提供非常方便的条件构建接口。

3

您可以创建一个baseDAO接口和一个baseDAO实现类。 当您需要使用不同的类类型进行特定用例时,只需创建该类的DAO,该DAO继承baseDAO类并实现额外的接口以满足该类的特定需求,例如:

IBaseDAO

 public interface IBaseDAO<T> {

/**
 * @Purpose :Save object of type T
 * @param transientInstance
 */
public Object persist(final T transientInstance);

/**
 * @Purpose :Delete object of type T
 * @param persistentInstance
 */
public void remove(final T persistentInstance);

/**
 * @Purpose :Update Object of type T
 * @param detachedInstance
 * @return
 */
public T merge(final T detachedInstance);

/**
 * @Purpose :Find object by 'id' of type T
 * @param identifier
 * @return
 */
public T findById(final Long identifier, Class<?> persistClass);
}

BaseDAO Class

public class BaseDAO<T> implements IBaseDAO<T> {

@Autowired
private SessionFactory sessionFactory;

public Object persist(T entity) {
    return this.getSession().save(entity);
}

@Override
public void remove(T persistentInstance) {
    this.getSession().delete(persistentInstance);
}

@SuppressWarnings("unchecked")
@Override
public T merge(T detachedInstance) {
    return (T) this.getSession().merge(detachedInstance);
}

@SuppressWarnings("unchecked")
@Override
public T findById(Long identifier, Class<?> persistClass) {
    return (T) this.getSession().get(persistClass, identifier);
}

public SessionFactory getSessionFactory() {
    return sessionFactory;
}

public Session getSession() {
    return getSessionFactory().getCurrentSession();
}

}

并具体的接口

public interface IUserDAO extends IBaseDAO<User> {

   public User getUserById(long userId);

   public User findUserByUsername(String username);

}

以及像这样的类

@Repository("userDAO")
public class UserDAO extends BaseDAO<User> implements IUserDAO {

public User getUserById(long userId) {
    return findById(userId, User.class);
}

@Override
    public User findUserByUsername(String username) {
        Criteria criteria = getSession().createCriteria(User.class);
        criteria.add(Restrictions.eq("username", username));
        return (User) criteria.uniqueResult();
    }

}

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