我通常倾向于创建一个基本的 dao 接口,其中定义了所有域实体共有的方法,例如:
// marker interface
public interface DomainEntity extends Serializable { }
// common dao methods
public interface DAO<T extends DomainEntity> {
public T findById(Long id);
public List<T> findAll();
public T save(T entity);
public boolean update(T entity);
public boolean delete(T entity);
}
然后根据我的要求提供一个或多个实现:
// implementation of common dao methods using JPA
public class JpaDAO<T> implements DAO<T> {
private EntityManager em;
public JpaDao(EntityManager em) { this.em = em; }
// Default implementations using JPA...
}
// implementation of common dao methods using JDBC
public class JdbcDAO<T> implements DAO<T> {
private Connection conn;
public JdbcDao(Connection conn) { this.conn = conn; }
// Default implementations using JDBC
}
现在,假设我有以下人员类:
public class Person implements DomainEntity {
private Long id;
private String firstName;
private String lastName;
// getters/setters...
}
我首先定义了一个通用的 PersonDAO 接口,如下所示:
public interface PersonDAO implements DAO<Person> {
public List<Person> findByFirstName(String name);
public List<Person> findByLastName(String name);
}
请注意,在我上面的特定实体 dao 接口中,我只包含了特定于我的域实体的额外方法。通用方法由超级接口继承,并使用泛型参数化为我的域实体。
现在剩下的最后一件事是定义我的实体特定方法的不同实现,如下所示:
package mypackage.daos.jpa;
public class PersonDAOImpl extends JpaDAO<Person> implements PersonDAO {
// here i implement only the entity specific dao methods
// defined in the last interface.
}
如果我还需要提供替代的 DAO 实现(比如基于 jdbc 而不是 JPA),它就像创建第二个类一样简单(最好在单独的包中):
package mypackage.daos.jdbc;
public class PersonDAOImpl extends JdbcDAO<Person> implements PersonDAO {
// again i only implement the entity specific DAO methods since
// defaults have been implemented in the super class...
}
这样做的好处是您可以在没有调用代码的情况下切换实现以受到影响:
// a service class that uses my dao
public class PersonService {
private PersonDAO dao;
public PersonService(PersonDAO dao) { this.dao = dao }
public void doSomethingUseful() {
// use the dao here...
}
}
通常,正确的 dao 实现(jdbc 或 jpa)将在服务创建期间通过构造函数注入。当然,如果您愿意,您只能有一个实现(即 jpa)。