2012-03-31 128 views
12

因此,让我们说我们有一些实体,我们想要使用DAO对象持久化。因此,我们实施正确的接口,这样我们结束了DAO设计模式

class JdbcUserDao implements UserDao{ 
//... 
} 

class JdbcAddressDao implements AddressDao{ 
//... 
} 

所以,如果我希望能够从JDBC切换持久化实现以JPA(例如),反之亦然,我需要有JPAUserDao和JPAAddressDao ...如果我有20个实体,并决定切换实现(使用DI容器),那么我必须用代码中的JPA切换每个Jdbc实现。

现在它可能是我误会了DAO是如何工作的,但是......如果我只是

class JdbcDaoImpl implements UserDao,AddressDao{ 
//... 
} 

我会再有所有的JDBC实现在一类,和开关实现将是一块的蛋糕。此外,DaoImpl计数等于Dao接口的数量。为什么不通过实现(jdbc,JTA,JPA ...)对它们进行分组并将所有内容都放在一个类中?

在此先感谢。

+3

同样的原因,你不编写你的应用程序到一个大的'主()'方法:分离关注。 (顺便提一句,没有人会阻止你编写一个包含公共代码的抽象'JdbcDaoBase'并将其扩展到你的'Dao') – rsp 2012-03-31 09:59:51

+0

为什么要更换一个类中的500个方法比100个类更容易? – 2014-02-01 16:09:13

回答

20

让单个类实现整个应用程序中的每个DAO接口将是一个相当糟糕的设计。

一个更典型的模式是有一个BaseDAO接口(也常被称为GenericDAO),并有一个JPABaseDAOJDBCBaseDAO等,这些基类将包含类似发现/获得/读取,保存/存储/坚持,更新/修改方法并删除/删除/清除。

UserDAO这样的具体DAO接口继承自BaseDAO,具体实现如JPAUserDAOJPABaseDAO延伸。

一个BaseDAO界面看起来是这样的:

public interface BaseDAO <T> {  
    T getByID(Long ID); 
    T save(T type); 
    T update(T type); 
    void delete(T type); 
} 

并有UserDAO接口:

@Stateless 
public class JPABaseDAO<T> implements BaseDAO<T> { 

    @PersistenceContext 
    private EntityManager entityManager; 

    private final Class<T> entityType; 

    @SuppressWarnings("unchecked") 
    public JPABaseDAO() { 
     this.entityType = ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); 
    } 

    @Override 
    public T getByID(Long ID) { 
     return entityManager.find(entityType, ID); 
    } 

    @Override 
    public T save(T type) { 
     return entityManager.persist(type);   
    } 

    @Override 
    public T update(T type) {   
     return entityManager.merge(type); 
    } 

    @Override 
    public void delete(T type) { 
     entityManager.remove(entityManager.contains(type) ? type : entityManager.merge(type)); 
    } 

} 

和一些:实现此接口一个JPABaseDAO

public interface UserDAO extends BaseDAO<User> { 
    List<User> getAllAuthorized(); 
} 

裸露的骨头例如样本UserDAO实施将继承它:

@Stateless 
public class JPAUserDAO extends JPABaseDAO<User> implements UserDAO { 

    @PersistenceContext 
    private EntityManager entityManager; 

    @Override 
    public List<User> getAllAuthorized() { 
     return entityManager.createNamedQuery("User.getAllAuthorized", User.class) 
          .getResultList(); 
    } 
} 

在实践中,基础类可以经常做一些其他的事情透明的,因为如果一个实体实现某种Auditable接口的实例检查,并自动设置,修改它的日期和用户等

当使用EJB来实现您的DAO时,改变实现的一个策略是将所有JDBC实现放在一个包中,并将所有JPA实现放在另一个包中。然后在你的构建中只包含一个实现包。

+0

非常好,非常感谢。涉及多个表格的CRUD操作如何?例如,我会执行select语句来获取对象并使用它来调用另一个DAO impl的CRUD,或者可能创建某种外来混合DAO?顺便说一句。你对我很有帮助,非常感谢。 – Mercurial 2012-03-31 12:00:35

+1

CRUD或涉及多个实体/表的任何操作通常由聚合多个DAO的服务处理。在EJB中,即使调用多个DAO(传播),它也会自动处于相同的持久化上下文中。另一种可能性是,如果实体相关联(User has-a House),则只需要用户使用DAO,JPA将从您的User对象中自动获取/保存/更新房屋。 – 2012-03-31 12:45:23

+1

好的。这种方法是我在各种项目中遵循的方法。事实证明,它工作得很好,稳定。我在这里详细描述它:http://codeblock.engio.net/?p=180 – bennidi 2014-02-01 16:08:56

1

依赖注入的关键在于使实现之间的切换更容易,并使用户与提供者分离。因此,所有的DI框架都提供了一些“分组”几种实现方式(这里是您的JDBC组和JPA组),并将它们切换到一个地方。

此外:通常消费者的数量(就您的情况而言:某些业务逻辑在用户和地址上工作)通常高于DAO的数量,DI框架无论如何都会将大部分内容解耦。假设:每个接口有50个业务bean,两个接口和两个实现(总共4个):即使基本的DI也会照顾到50个。使用分组会将剩余的一半减半。

+0

你能解释一下“另外”部分吗?非常感谢。 – Mercurial 2012-03-31 12:01:58

+0

@ user1304844:我想但我不知道什么不清楚。 – 2012-03-31 12:11:12

+0

“即使是基本的DI也会照顾到50.使用分组会将剩下的一半减半。” - 我不明白这一点。 – Mercurial 2012-03-31 12:19:37

0

绝对有可能以广泛的技术不可知的方式实现DAO模式,以便切换持久性技术或混合多种技术变得可行。本文提供了一个在github上包含源代码的实现方案。

http://codeblock.engio.net/?p=180