0

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

我想出了这些设计:

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

然而,

  • 设计1是反OO原则。
  • 设计 2 会破坏我的 DAO 实现的一致性,并且会降低类本身的可读性,并且会邀请其他开发人员继续在没有组织的情况下向它添加方法。
  • 设计 3 增加了不必要的工作,因为无论如何我都会使用更有效的实现。

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

例子:

public interface RxDao {

    public Rx getRxById(int rxId);

    public Map<String, List<Notification>> getAllRxNotificationsGroupedByFacility();
}

getRxById使用 hibernate 实现是有意义的,因为您可以使用 hibernate ORM。

getAllRxNotificationsGroupedByFacility另一方面,仅检索 Rx 列的子集,但获取更多数据,需要以某种方式分组,并最终发送到另一台服务器,因此使用 jdbc 实现它更有意义。

4

5 回答 5

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) { ... }
}
于 2012-12-04T15:13:23.667 回答
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的你好

您不喜欢您需要实现无论如何都不支持的操作这一事实,您可以考虑拆分接口(正如我在另一篇文章中所建议的那样)。

于 2012-12-05T23:44:18.640 回答
1

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

于 2012-12-04T22:10:48.950 回答
0

我觉得奇怪的是,您“在某些情况下”需要提供一个简单的 JDBC DAO 实现,您应该能够使用 Hibernate 或更一般地使用 JPA 来涵盖所有用例。

另一个要探索的想法:重用通用 DAO并纯粹重用它,而不是遵循每个实体一个 DAO 的常见反模式,这是当你每次有一个新的数据模型实体时添加接口和实现时会发生的情况,例如添加Person提示您添加IPersonDao接口和PersonDao实现,这显然是对可扩展性和可维护性的高代价。您可以通过使用通用 DAO 完全避免这种情况,例如说您有一个新的Person数据模型实体,然后您简单地重用现有的通用 DAO:

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

本页底部有更多示例。确保检查通用 DAO 的“按示例查找”功能,该功能可以通过将命名查询映射到接口元素的 Spring 通用 DAO 实现进一步扩展。

于 2012-12-05T15:14:18.363 回答
0

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

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

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

于 2012-12-04T15:16:46.833 回答