0

私は春/休止状態のWebアプリを持っています。私はHibernateを使用してほとんどすべてのDAOを実装しています。ただし、JDBCベースの実装を使用する必要がある場合があります。DAOごとに、ProductDao'などのインターフェイスがあり、ProductDaoHibImplまたはProductDaoJdbcImplの実装があります。問題は、たとえば、2つのメソッドを持つDAOがある場合、一方のメソッドはHibernateを使用して実装するのが理にかなっており、もう一方はjdbcを使用して実装するのが理にかなっているということです。最高のクラスのデザインは何ですか。

私はこれらのデザインを思いついた:

  1. 1つのインターフェイス2つの実装。各クラスに実装されていないメソッドでランタイム例外がスローされます。(つまり、hibernateクラスに実装されたメソッドが呼び出されると、jdbcの実装はランタイム例外をスローします)
  2. 両方の実装を1つのクラスにマージします
  3. 両方のクラスにすべてのメソッドを実装する

でも、

  • デザイン1は反OOの原則です。
  • デザイン2は、私のDAO実装の一貫性を台無しにし、クラス自体を読みにくくし、他の開発者を組織化せずにメソッドを追加し続けるように誘います。
  • とにかくより効率的な実装を使用するため、デザイン3は不要な作業を追加しています。

複数のDAOの不完全な実装のためのより良い設計は何ですか?

例:

public interface RxDao {

    public Rx getRxById(int rxId);

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

getRxByIdHibernate ORMを使用できるため、Hibernateを使用して実装するのは理にかなっています。

getAllRxNotificationsGroupedByFacility一方、Rx列のサブセットのみを取得しますが、より多くのデータをフェッチします。これらのデータは特定の方法でグループ化され、最終的に別のサーバーに送信される必要があるため、jdbcを使用して実装する方が理にかなっています。

4

5 に答える 5

2

アプローチ2は私には非常に良さそうに見えますが、2つの実装が統合されたものとしてなぜそれを見るのかよくわかりません。

ほとんどのメソッドに高レベルのHibernate機能を使用するDAOを作成することはかなり一般的ですが、それを必要とするメソッドには低レベルにフォールバックします。低レベルのアクセスにはHibernateAPI(ネイティブクエリ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

それでも物事を分離したい場合は、DAOインターフェースを2つに分割することができます(と言うことができますJdbcProductOperationsJpaProductOperations。ProductDAOインターフェースはメソッドを宣言しなくなりましたが、これらのインターフェースの両方から継承します。次に、JdbcProductOperations-およびJpaProductOperationsインスタンスを取得するProductDAO-Implementationを追加し、それに応じて呼び出しを委任することができます。

于 2012-12-04T22:10:48.950 に答える
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 に答える
0

HibernateとJDBCの2つの異なるモデルを使用する必要がある理由について知りたいです。

私は現在、やや似たようなものを扱っています。私のアプリケーションには2つの展開があります。1つは自分でホストするmySQLを使用し、もう1つは別の会社が別のエンドユーザーのセットをホストするMicrosoftSQLサーバーを使用します。

SQL構文の違いに対処するために、抽象化レイヤーとしてjOOQを選択しました。少し学習曲線をたどった後、操作が非常に簡単であることがわかりました。Daoオブジェクトで(またはサーブレット初期化パラメーターを使用して)SQLダイアレクトを設定するだけで、2つの異なるデータベースデプロイメント間で変更する必要がある唯一の行になります。

于 2012-12-04T15:16:46.833 に答える
0

「場合によっては」プレーンなJDBCDAO実装を提供する必要があるのは奇妙だと思います。すべてのユースケースを、Hibernateまたはより一般的にはJPAを使用してカバーできるはずです。

探求する別のアイデア:新しいデータモデルエンティティ(プロンプトの追加など)があるたびにインターフェイスと実装を追加するときに発生する、エンティティごとに1つのDAOの一般的なアンチパターンに従うのではなく、汎用DAOを再利用して純粋に再利用するインターフェイスと実装Personを追加すると、これは明らかに拡張性と保守性に大きな負担をかけます。汎用DAOを使用することで、これを完全に回避できます。たとえば、新しいデータモデルエンティティがある場合、既存の汎用DAOを単純に再利用できます。IPersonDaoPersonDaoPerson

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

このページの下部にあるその他の例。名前付きクエリをインターフェイス要素にマップするSpringGenericDAO実装によってさらに拡張できる、GenericDAOの「例による検索」機能を必ずチェックアウトしてください。

于 2012-12-05T15:14:18.363 に答える