Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

DAOクラスの設計について

初めて質問いたします。

只今、DBアクセスを伴う簡易な良くあるWebアプリを設計中です。
ロジック層とデータアクセス層を分割するため
DAOクラスを用いて、データベースアクセスを行わせようと考えています。

突然ですが、ここで質問なのですが、

DAOクラスはシングルトンにして、アプリ内で共有するのが一般的なのでしょうか?

以下の記事等いくつかのサイトで、共有するべき(一般的?)・・・という感じで書かれていました。
参考)http://oshiete1.goo.ne.jp/qa324356.html

今まで自分が作成してきた設計では、
1トランザクションで、複数のDAOやDAOのメソッドを実行する必要があったので(こちらが一般的だと思いますが)、daoクラスのインスタンス生成時などに
コネクションやDataSource(またはそれらをラップしたもの)等を渡し、当然はそれぞれのリクエスト(スレッド)で固有である必要があるため、各リクエスト毎に、インスタンスを生成するようにしておりました。

シングルトンにした場合でも、ThreadLocal等を用いれば、共有することが可能だと思いますが、これが一般的な手法とは思えないのですが、実際のところは同なのでしょうか?

実装方法は自由ですし、どんな方法でも実現可能だとは思いますが、経験豊かな皆様はどのようにお考えでしょうか?
ご意見を伺えればと思います。

P.S 「一般的」という言葉に引っかかり過ぎでしょうか。。。

19

回答

18965

閲覧

19件の回答

評価

0

Webアプリに置いて、
DAOをSingletonにすると、
何度もインスタンスを生成する処理が働かないので、実行速度を上げれる事が期待出来そうですが、
Singletonにしたインスタンスは永続インスタンスとなりますから、
場合によってはメモリリークの原因になります。

例えば、多くの使用頻度の低いDAOが常時メモリを確保しつづけるような状況です。

永続インスタンスのメモリ使用量が増えると、それに伴いフルガーベジコレクションの実行コストも増加します。

使用頻度の極端に高いDAOならSingletonにする事によって平均実行速度向上が期待できますが、
よくわからない場合は、パフォーマンスチューニングの段階まで、Singletonにせずにおいた方が、安定すると思いますよ。

また、パフォーマンスに関係なくSingleton以外の方法でスマートに解決出来ないロジックの場合は、
Singletonにして良いと思います。

Webアプリにおいて、安易にSingletonを使うのは、あまりお勧め出来ないので、よく検討して使用してください。
特に大きなプロジェクトだと、DAOの数も半端じゃなかったりしますからね。

評価

0

DAOだからシングルトンにすべきとか、ありえない。
すべてはクラスの設計次第。

共有できない状態を持つなら都度生成、
生成コストが低くても都度生成、
共有できる状態があり、かつ生成コストが高いなら、
シングルトンにする価値はあるんじゃないかな。

評価

0

やんちさん、bさん
早速の回答ありがとうございます。

まずは、「一般的である」の呪縛より開放された気がします。

ご回答を踏まえ、改めてDAOのクラス設計を考えたいのですが

シングルトンでDAOを設計する場合、
DAOクラスにおける、スレッドに対して固有なコネクションの取得はどのように実装するのが良い(お勧め)でしょうか。

ThreadLocalなコネクションを取得する方法もありでしょうし、メソッドの引数に渡す(←これはお洒落じゃないなぁ〜。。)のもありかと思いますが、
経験上お勧めの方法などあれば、ご教示いただければ幸いです。

以上です。
皆様宜しくお願いいたします。





評価

0

一般的ではないかもしれませんが、シングルトンのインスタンスを生成するときに、コンストラクタなどでDataSourceを取得して保持しておく。
DAOの処理では、保持しておいたDataSourceからコネクションを取得して処理するようにしておく。

インスタンス生成後、DataSourceを切り替える必要がある場合は、それが可能なように作る必要があります。

評価

0

kjさん
回答ありがとうございます。

引用-------------------------------
一般的ではないかもしれませんが、シングルトンのインスタンスを生成するときに、コンストラクタなどでDataSourceを取得して保持しておく。
DAOの処理では、保持しておいたDataSourceからコネクションを取得して処理するようにしておく。
--------------------------------ここまで

ご教示いただいた実装にした場合、DAOメソッド内で、DataSourceからコネクションを取得する度に異なるコネクションが取得されてしまいませんでしょうか?

メソッド単位のトランザクションであればこれで良いのですが、前述の通り複数のDAOメソッドとDAOクラスを横断的に使用し、上位でトランザクション管理を行いたいと思っておりますが、いかがでしょうか?

評価

0

>ご教示いただいた実装にした場合、DAOメソッド内で、DataSourceからコネクションを取得する度に異なるコネクションが取得されてしまいませんでしょうか?

そうなります。

>メソッド単位のトランザクションであればこれで良いのですが、前述の通り複数のDAOメソッドとDAOクラスを横断的に使用し、上位でトランザクション管理を行いたいと思っておりますが、いかがでしょうか?

上位でコネクションを取得するようにすればいいですが、そうすると、上位がコネクションに依存するようになります。
私の場合、DAO内でトランザクション管理ができるように
設計します。(DAO内から複数のメソッドを横断する。)

以下、蛇足です。
本で読んだことがあるんだが、データモデル(データベース)とオブジェクトモデル(Java)がミスマッチを起こす場合が多々あります。これはどっちが悪いんじゃなくて設計思想が違うからです。
どちらかにあわせないといけない場合があります。通常は、オブジェクトモデルの方を直すのではないでしょうか。

評価

0

質問の答えになってなかった。

別途データソースを保持するシングルトンクラスを作る。
DAOのメソッドをオーバーロードする。

<例>

class DAO1{
  method1(){
    シングルトンからデータソース取得;
    Conncetion con = ds.getConnection();
    トランザクション開始;
    method1(con);
    トランザクション終了;
  }
  method1(Connection con){
    データベースアクセス処理;
  }
}

class DAO2{
  method1(){
    シングルトンからデータソース取得;
    Conncetion con = ds.getConnection();
    トランザクション開始;
    method1(con);
    トランザクション終了;
  }
  method1(Connection con){
    データベースアクセス処理;
  }
}

// 複数のDAOを使用する。
class DAO3{
  method1(){
    シングルトンからデータソース取得;
    Conncetion con = ds.getConnection();
    トランザクション開始;
    DAO1を取得;
    dao1.method1(con);
    DAO2を取得;
    dao2.method1(con);
    トランザクション終了;
  }
}

のようにすれば、複数DAOでもトランザクション管理できると思います。

アクセス識別子、引数、戻り値などは省きました。

評価

0

kjさん
ご丁寧な回答ありがとうございます。
(&返信遅くなりました)

確かにこういうつくりもありですよね。
参考になります。

ただこの設計だと、トランザクションを管理したい単位でDAO内に似通ったメソッドがたくさんできてしまいますよね。

トランザクション管理を行う単位は、ほぼユーザにサービスを提供するレベルで切り分けれれますので、このトランザクション管理レベルをDAOまで落としてしまいますと、DAOがビジネスロジックを持ってしまいますので、自分としては、できれば避けたいと思っています。

とはいっても、アプリの規模が小さい場合等ではこういう設計が良い場合もあるかと思います。

ありがとうございました。

評価

0

私見です。
データベースに対するトランザクション管理はDAOでやっておき、もし上位で必要ならば、DAOから例外を投げるなどして対応するんじゃないでしょうか?

ビジネスロジックがコネクションに依存するべきではないと私は思うので、DAOで、ユーザー定義例外を投げてデータベースに対するトランザクション管理の結果を上位に伝えればいいと思います。

私の認識では、DAOとはデータベースだけでなくDAOを交換することにより、ファイルなども扱えるようにデータアクセスを隠ぺいするものだと思っています。

もちろん、このような設計にするとまずい場合もあるかもしれません。

評価

0

kjさん
ご見解頂きありがとうございます。

おそらくDAOに対する狙いは、私も認識に違いはないと思います。データベースコネクションを上位層に持ち出すべきではないと思いますし、データアクセスを隠蔽するものが役割かと思います。

ただ、「コネクション管理≠トランザクション管理」だと思っていますので、コネクションはDAOで。トランザクションは上位(レイヤーは設計によると思いますが)で管理できると思い、最適な方法を検討しています。

評価

0

トランザクションの意味合いが違っていると思います。
私の意見はデータベースアクセス時のトランザクションについて述べています。

mame豆さんは、もっと、大きい意味でのトランザクションを述べているのではないでしょうか?

それによって、回答は異なります。

評価

0

興味深く拝見させて頂きました。
私ならば・・・

■コネクション・トランザクション管理
ざっくりと最上位の入口(ServletFilterやActionの基底クラスなど)でコネクション生成と、トランザクション開始を行います。
また、リクエスト単位の情報をThreadLocalで保持するコンテキストクラスを作り、トランザクション開始状態のコネクションをセットします。ユーザIDや画面IDなどリクエスト単位で利用頻度の高い共通情報なども、上記コンテキストで保持させます。
で、Business層ではコネクションは意識せず、DAO内で自作コンテキストからTheadLocalコネクションを取得してDBアクセスします。
コミットorロールバックなども、やはり最上位の出口で行うものとし、どうしても必要な機能のみ、Business層で中間コミットします。

上記で得られると思われるメリットは、
・任意のクラスで、一貫性のあるコネクションが取得できるので、引数で引き回す必要がない。
・Junitの単体を考えた場合、Setup、TearDownでコネクション・トランザクション管理すれば、本番と同等条件となる。
・何らかのエラーが画面通知された場合、ユーザは、データ更新状態を気にする事なく再試行すればよいため、運用が楽。
などと考えます。

■DAOの生成単位(Singletonか否か)
毎回生成で良いと考えます。
Singletonにすると、そのクラスで状態を保持する事が難しくなるため、私ならば、将来的にインスタンス変数を使いたくなる可能性がある箇所には採用しません。
私は、予めSingletonにするのは、文字列編集や数値計算などのUtility的なクラス類くらいでしょうか。


ご意見・ご指摘等、交流できれば幸いです。

評価

0

通りすがりさんの方法について書きます。

■コネクション・トランザクション管理
どうにでも、作れる構成ですね。(拡張性が高い。)
大規模プロジェクトには向いているかもしれません。
私も、このような構成にしたことあります。

■DAOの生成単位(Singletonか否か)
毎回生成でよいと思います。
チューニング時に考えればいいと思います。
後でSingletonとすることができるようにしておくといいと思います。(なるべくです。)

□疑問点
データベースアクセスのトランザクション管理必要ない場合はどのようにしますが?

★私見
最上位の出口でトランザクション管理するのは、いいと思います。しかし、ここでのトランザクションは画面遷移を指します。(データベースアクセスのトランザクション管理とは異なります。)
ただし、webでは同一でよい場合が多いので、データベースアクセスのトランザクション管理もどこでやるかの指針を決めておき、イレギュラーケースにも対応できるような作りがいいと思います。

評価

0

kjさん、ありがとうございます。

>後でSingletonとすることができるようにしておくといいと思います。(なるべくです。)

→同感です。
変数のスコープは、なるべく狭くするべきと入門書にも書いてありますし(^^


>ただし、webでは同一でよい場合が多いので(略)

→これも同感です。
メソッドにより、内部でトランザクションが完結している物・継続する物・即コミットする物・・など実装がバラける事態は避けたいと考えます。
クラス設計や実装指針を設定する際、私は「7〜8割の機能にマッチする事」を念頭において考えます。同時に、イレギュラーケースでも、一手間加えれば対応出来るよう、逃げ道も用意しておきます。


>□疑問点
>データベースアクセスのトランザクション管理必要ない場合はどのようにしますが?

→ServletNameに規則性を持たせる・リクエストに共通パラメータを持たせる、などして、該当リクエストが検索系か更新系かを判断できる材料を用意し、更新系のみDBトランザクションを開始。などの方法を考えます。
一方で、検索系でも一律DBトランザクション開始しても、大したコストにならないのでは、との意見もありました。この辺のDBの負荷って、どうなんでしょうか?


>しかし、ここでのトランザクションは画面遷移を指します。
>(データベースアクセスのトランザクション管理とは異なります。)

→複数画面を跨いで、1つの処理が成り立つ場合、DBトランザクションを引き継ぐか否か。という観点と受け止めて良いでしょうか?(この時点で勘違いならゴメンナサイ)
これは継続させるべきでないと考えます。
画面間でDBトランザクションを継続する場合、Sessionを使う事になると思いますが、画面を閉じられた場合などに、しばらくコネクションを占有されるので、これは避けたいです。(データの引継ぎはOKとして)
上記は業務設計で回避を検討すべきと思います。


TO:mame豆さん
→ナイスな設計が閃いていたら教えてください。

評価

0

>複数画面を跨いで、1つの処理が成り立つ場合、
>DBトランザクションを引き継ぐか否か。
>という観点と受け止めて良いでしょうか?

私は、通りすがりさんのいう1つの処理もトランザクションと思っています。DBトランザクションだけではなくJavaでの処理も含みます。
それで、私にはどのトランザクションの議論をしているのか分かっていません。
ちなみに、DAOでDBトランザクション管理しても上位にユーザー定義例外などで伝え、DAOよりも上位(Actionなど)で画面間のトランザクション制御(例えば、エラー画面への遷移など)は行った方がweb系ではいいと思っています。


>複数画面を跨いで、1つの処理が成り立つ場合、
>DBトランザクションを引き継ぐか否か。

私も、画面間でDBトランザクションを継続すべきではないと考えています。


■コネクションについて
以前の通りすがりさんの書き込みにあるTheadLocalコネクションとありますが、Singletonにした場合でも、TheadLocalじゃないと不具合が発生する場合があるので、書き込んだDAOはデータソースが共通でコネクションはTheadLocalなものとなっているつもりです。


mame豆さん、どういう設計になったか勉強になりますので、よろしければ教えてください。

評価

0

kjさん、ありがとうございます。

>それで、私にはどのトランザクションの議論をしているのか分かっていません。

→途中参加で申し訳ありませんが、私は、題名が「DAOクラスの設計について」なのでDBトランザクションの論議だと認識しています。(単純で申し訳ないです)

論点として、
・同一DBトランザクションのコネクションを、どのようにクラス間、メソッド間で流通させるべきか?
・そのために、コネクション生成・DBトランザクションの開始・終了は、どのレイヤが担当すべきか?
・上記もろもろを考慮すると、カッコイイDAOの設計は?
などかと勝手に思っているのですが、これはmame豆さんに聞いてみないと分かりません・・

kjさんの言われる、もう一方のトランザクション話は、サーバサイドアプリ全体の設計に広がってしまうので、このページが非常に縦長になってしまうと思われます。

実は、2008-11-10 09:40の「DAOがビジネスロジックを持つべきか」あたりも興味深く読ませて頂いておりました。

かのmartinfowlerさんは、こんな事おっしゃってましたし。
http://capsctrl.que.jp/kdmsnr/wiki/bliki/?AnemicDomainModel

評価

0

通りすがりさんご指摘ありがとうございます。

私も、DBトランザクションだと思ってたのですが、その辺があいまいなので一応指摘したわけです。

私も、カッコイイDAOの設計を知りたいです。


「martinfowlerさん情報」ありがとうございます。

いずれにしても、どうとでも作れるので他の人の意見を知りたいですね。

評価

0

通りすがりさん kjさん
議論を膨らませて頂きありがとうございます。

ぼけている間に、いつの間にか。。。。

毎度のことなのですが、そもそも簡単なアプリを作ろうとしているにも関わらず、いざ設計を始めてしまうと、ついついフレームワーク作りのようなものに目がいってしまい、中々開発が始まりません。。。

一応今回のDB周りは、通りすがりさんのとほぼ同じにしています(今のところ)。

-----------------------------------------
・DBコネクションは、ThreadLocalオブジェクトを用いることで、1リクエスト内では同一のコネクションを使用できるようにする。
・ServletFilterで、DBトランザクションの開始/終了を管理する。
・今回はTomcatで、スレッドが再利用されるため、ThreadLocalオブジェクト開放(破棄)の方法については、イカシタ方法を検討中。
・DAOクラスは、DAOクラスのインスタンス管理を行う、コンテナチックなものを作成し、シングルトンか、リクエスト毎生成かを選択できるようにする。
・DAO内部の各メソッドでは、ThreadLocalオブジェクトよりコネクション取得のみおこない、当然コミット/ロールバックは行わない(SQL例外時には、SQL例外をラップしたアプリ例外を上位にスローする)。
-----------------------------------------

大体こんな感じです。
ちなみにDAOのベースにはcommonsのDButilsを使うことにしました。

> カッコイイDAOの設計は?
そうですね。まさにこれです。
DAOに限らず「カッコイイ!」設計には興味アリです。

> どうとでも作れるので他の人の意見を知りたいですね。
これも設計中いつも感じることです。
とりあえずそれっぽい設計をするのは、何とかなりますが、「それが最適か?」という点においては、今イチ自信が持てないのです。

今回みたいに、皆さんの意見を聞けて非常に有用でした。ありがとうございました。

この掲示板でも、いろんなテーマでこの手の議論がされると嬉しいですね。。



評価

0

s

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。