多DAO不完整实现设计

时间:2012-12-04 14:31:23

标签: java spring hibernate design-patterns dao

我有spring / hibernate网络应用程序。我使用Hibernate来实现几乎所有的DAO。但是,偶尔我需要使用基于JDBC的实现。对于每个DAO,我有一个接口,比如ProductDao',我有一个ProductDaoHibImpl或ProductDaoJdbcImpl的实现。问题是,比方说,如果我有一个带有两个方法的DAO,其中一个方法有意义使用Hibernate实现,另一个方法使用jdbc实现是有意义的。什么是最好的班级设计。

我想出了这些设计:

  1. 一个接口有两个实现,在每个类中未实现的方法中抛出运行时异常。 (即,当调用hibernate类中实现的方法时,jdbc implmentation会抛出运行时异常)
  2. 在一个类中合并两个实现
  3. 实现两个类中的所有方法
  4. 然而,

    • 设计1是反OO原则。
    • 设计2将搞砸了 我的DAO实现的一致性,并将成为该类 本身不太可读,并会邀请其他开发人员继续添加 没有组织的方法。
    • 设计3添加不必要的 工作,因为无论如何我都会使用更高效的实施。

    对于多个DAO不完整的实现,什么是更好的设计?

    示例:

    public interface RxDao {
    
        public Rx getRxById(int rxId);
    
        public Map<String, List<Notification>> getAllRxNotificationsGroupedByFacility();
    }
    
    使用hibernate实现

    getRxById是有道理的,因为你可以使用hibernate ORM。

    另一方面,

    getAllRxNotificationsGroupedByFacility只检索Rx列的一个子集,但是需要获取更多数据,需要按特定方式进行分组,最终将其发送到另一个服务器,因此实现更有意义它使用jdbc。

5 个答案:

答案 0 :(得分:2)

方法2看起来对我很好,但是,我不太明白为什么你认为它是两个实现合并在一起。

创建一个在大多数方法中使用高级Hibernate功能的DAO是很常见的,但对于需要它的方法则回落到较低级别。请注意,您可以使用Hibernate API(本机查询,doWork(...)等)进行低级访问,因此将Hibernate与JDBC混合不会产生额外的复杂性。

这样的事情:

public class HibernateRxDao implements RxDao {
    ...

    public Rx getRxById(int rxId) {
        return sf.getCurrentSession().get(rxId, Rx.class);
    }

    public Map<String, List<Notification>> getAllRxNotificationsGroupedByFacility() {
        return toMap(sf.getCurrentSession().createNativeQuery(...). ... .list());
    }

    private Map<String, List<Notification>> toMap(List<Object[]> rows) { ... }
}

答案 1 :(得分:1)

如果您仍然希望将事物分开,可以将DAO接口分成两部分(例如JdbcProductOperationsJpaProductOperations)。您的ProductDAO接口不再声明任何方法,而是继承这两个接口。然后,您可以添加一个采用JdbcProductOperations-和JpaProductOperations实例的ProductDAO-Implementation,并相应地委派调用。

答案 2 :(得分:1)

除了我的其他答案,这里有一个可能的解决方案。您使用“服务质量”在实现类上注释方法,并创建自动委托给具有最高“服务质量”值的实现的代理:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;


public class DaoProxyDemo {

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface QualityOfService {
        int value();
    }

    interface ProductDao {
        String operation1();
        String operation2();
    }

    static class JpaProductDao implements ProductDao {

        @QualityOfService(1)
        public String operation1() { return "hello from jpa"; }

        @QualityOfService(0)
        public String operation2() { throw new UnsupportedOperationException(); }

    }

    static class JdbcProductDao implements ProductDao {

        @QualityOfService(0)
        public String operation1() { throw new UnsupportedOperationException(); }

        @QualityOfService(1)
        public String operation2() { return "hello from jdbc"; }

    }    

    static class QosAwareProxyFactory {

        public static <T> T createProxy(Class<T> interfaceType, T... implementations) {

            class Binding {
                private final int qos;
                private final T impl;

                public Binding(T impl, int qos) {
                    this.impl = impl;
                    this.qos = qos;
                }
            }

            final Map<Method, Binding> dispatchMap = new HashMap<Method, Binding>();
            try {
                for (Method method : interfaceType.getDeclaredMethods()) {

                    for (T impl : implementations) {

                        Method implMethod = impl.getClass().getMethod(method.getName(), 
                                method.getParameterTypes());

                        QualityOfService qos = implMethod.getAnnotation(QualityOfService.class);

                        int qosValue = qos == null ? 0 : qos.value();

                        Binding bestSoFar = dispatchMap.get(method);

                        if (bestSoFar == null || bestSoFar.qos < qosValue) {
                            dispatchMap.put(method, new Binding(impl, qos.value()));
                        }

                    }

                }
            }
            catch (NoSuchMethodException e) {
                throw new AssertionError("can never happen");
            }

            Object proxy = Proxy.newProxyInstance(QosAwareProxyFactory.class.getClassLoader(), 
                    new Class<?>[] {interfaceType}, new InvocationHandler() {

                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {

                    T impl = dispatchMap.get(method).impl;
                    return method.invoke(impl);
                }
            });

            return interfaceType.cast(proxy);

        }
    }

    public static void main(String[] args) {

        ProductDao proxy = QosAwareProxyFactory.createProxy(
                ProductDao.class, new JpaProductDao(), new JdbcProductDao());

        System.out.println(proxy.operation1());
        System.out.println(proxy.operation2());

    }

}

如果运行main方法,则会打印:

你好,来自jpa

你好,来自jdbc

你不喜欢这个事实,你需要实现不支持的操作,你可以考虑拆分接口(正如我在其他帖子中建议的那样)。

答案 3 :(得分:0)

我很好奇为什么你需要使用两种不同的模型 - Hibernate和JDBC。

我目前正在处理类似的事情。我的应用程序将有两个部署,一个使用mySQL,我将自己托管,另一个使用microsoft SQL服务器,另一家公司将托管给另一组最终用户。

为了处理SQL语法的差异,我选择jOOQ作为抽象层,经过一点学习曲线后,我发现它很容易使用。我可以在Dao对象中设置SQL方言(或使用Servlet init参数),这将是我需要在两个不同的数据库部署之间进行更改的唯一行。

答案 4 :(得分:0)

我觉得很奇怪你需要在某些情况下提供一个简单的JDBC DAO实现,你应该能够用Hibernate覆盖你的所有用例 - 或者更普遍地使用JPA。

要探索的另一个想法:重用 Generic DAO 并纯粹重用它,而不是遵循一个DAO-per-entity的公共反模式,这是添加接口时发生的情况每当你有一个新的数据模型实体时实现,例如添加Person会提示您添加IPersonDao接口和PersonDao实现,这显然是可扩展性和可维护性的一个重要因素。您可以通过使用通用DAO完全避免这种情况,例如:假设您有一个新的Person数据模型实体,那么您可以简单地重用现有的通用DAO:

 Person myGiovanni = new Person("Giovanni");  
 IGenericDao<Long, Person> myPersonDao = HibernateDaoFactory.getInstance().createDao(Person.class);
 myPersonDao.create(myGiovanni);

this page底部的更多示例。确保签出Generic DAO的“find-by-example”功能,该功能可以通过将命名查询映射到接口元素的Spring Generic DAO实现进一步扩展。