質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java EE

Java EE(Java Enterprise Edition)はJavaベースのテクノロジーとその相互運用の仕様をまとめたものです。サーバとクライアントのアーキテクチャを規定し、特定アプリケーションのクラス用に定義されたテクノロジー設定のプロファイルを使用します。

JDBC

JDBC(Java DataBase Connectivity)は、Javaとリーレーショナルデータベースに接続させる基本的なAPIです。Java上でSQLステートメントを発行することで、データベースの種類に影響を受ないDB操作を可能とします。

Q&A

解決済

2回答

3388閲覧

DAOのクラス設計について(重複部分をまとめられないか)

chankane

総合スコア139

Java EE

Java EE(Java Enterprise Edition)はJavaベースのテクノロジーとその相互運用の仕様をまとめたものです。サーバとクライアントのアーキテクチャを規定し、特定アプリケーションのクラス用に定義されたテクノロジー設定のプロファイルを使用します。

JDBC

JDBC(Java DataBase Connectivity)は、Javaとリーレーショナルデータベースに接続させる基本的なAPIです。Java上でSQLステートメントを発行することで、データベースの種類に影響を受ないDB操作を可能とします。

0グッド

0クリップ

投稿2018/08/13 14:36

実現したいこと

先日,WEBアプリケーションの勉強がてら,簡単なWEBショッピングを作成しました.現在は,リファクタリングの最中なのですが,DAO周りのコードに重複する部分が見つかりました.よって処理をまとめたいのですが,いいアイデアが思い浮かびません.皆様の考えを共有してはいただけないでしょうか.

該当のソースコード

Java

1// DAOInterface.java 簡単化のため省略 2package pkg.dao; 3 4import java.sql.Connection; 5import java.sql.DriverManager; 6import java.sql.SQLException; 7 8public interface DAOInterface { 9 10 static Connection createConnection() throws SQLException { 11 return DriverManager.getConnection( // 省略... 12 } 13 14 void open() throws SQLException; 15 16 void close() throws SQLException; 17} 18

Java

1// UserDAO.java 同じく簡単化のため省略 2package pkg.dao; 3 4import java.sql.Connection; 5import java.sql.ResultSet; 6import java.sql.SQLException; 7import java.sql.Statement; 8 9import pkg.entity.UserEntity; 10 11public class UserDAO implements DAOInterface{ 12 13 private Connection connection; 14 private Statement statement; 15 16 public UserEntity find(int userId) { 17     UserEntity user = null; 18 try { 19 open(); 20     user = /* ID検索でヒットしたユーザ */; 21 close(); 22 catch(SQLException e){/*例外処理*/} 23 return user; 24 } 25 26 // ほかにも上記のようにSQLを使った処理があるよ! 27 28 @Override 29 public void open() throws SQLException { 30 connection = DAOInterface.createConnection(); 31 statement = connection.createStatement(); 32 } 33 34 @Override 35 public void close() throws SQLException { 36 statement.close(); 37 connection.close(); 38 } 39}

詳細

上記UserDAO.javaのほかにもDAOがいくつかあります.結論何が言いたいかというと,それらのDAOには共通して以下の処理が書かれています.

Java

1 // 1 2 private Connection connection; 3 private Statement statement; 4 5 // 2 6 @Override 7 public void open() throws SQLException { 8 connection = DAOInterface.createConnection(); 9 statement = connection.createStatement(); 10 } 11 12 // 3 13 @Override 14 public void close() throws SQLException { 15 statement.close(); 16 connection.close(); 17 }

今はせいぜい数個のDAOクラスなのでいいのですが,(あまりないと思いますが)これが100個200個となるとまとめたくなってきます.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

リファクタリングはいいことですが「処理をまとめる」にこだわると、逆に分かりにくくなります。特に実装の継承(extends)は後々辛いことになるケースが多いです。

ソースコードを見て気になったのが、findメソッドの実装です。ConnectionとStatementをフィールドに持っていますが、findメソッドの中でオープン、クローズしているので、これらはフィールドに持つ必要はありません。グローバル変数と同じ使い方になってしまっています

たぶんまとめたいのは以下の2つだと思いますので、実装例を挙げておきます。

  1. データベース接続先(DriverManager.getConnection())の引数
  2. open/closeとかのSQL実行時の定型的作業

1.については例えば、ファクトリを使います。

ConnectionFactory.java

1package pkg.dao; 2 3import java.sql.Connection; 4import java.sql.DriverManager; 5import java.sql.SQLException; 6 7public final class ConnectionFactory { 8 public static Connection createConnection() throws SQLException { 9 return DriverManager.getConnection(System.getProperty("db.url")); 10 } 11}

2.については「SQLを実行して結果を受け取る」と「受け取った結果からUserEntityインスタンスを作る」に分けて、以下のようにします(DbUtilsを使用)。

例外処理も共通だとと思うので、IllegalStateExceptionでラップしています。

SqlService.java

1package pkg.dao; 2 3import org.apache.commons.dbutils.QueryRunner; 4import org.apache.commons.dbutils.handlers.MapHandler; 5 6import java.sql.Connection; 7import java.sql.SQLException; 8import java.util.Map; 9 10public final class SqlService { 11 public static Map<String, Object> selectOne(String sql, Object... params) { 12 try (Connection conn = ConnectionFactory.createConnection()) { 13 QueryRunner runner = new QueryRunner(); 14 return runner.query(conn, sql, new MapHandler(), params); 15 } catch (SQLException e) { 16 throw new IllegalStateException("SQL実行時に例外が発生しました。", e); 17 } 18 } 19}

UserDaoの実装は次のようになります。

UserDao.java

1package pkg.dao; 2 3import pkg.entity.UserEntity; 4 5import java.util.Map; 6 7public class UserDAO { 8 public UserEntity find(int userId) { 9 Map<String, Object> record = SqlService.selectOne("select * from user where user_id = ?", userId); 10 11 // TODO: record -> userにマップする 12 UserEntity user = null; 13 14 return user; 15 } 16}

これなら十分シンプルかなと思いますが、どうでしょうか?

投稿2018/08/14 02:23

ikemo

総合スコア332

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

chankane

2018/08/14 03:28 編集

おはようございます ~~~ヾ(^∇^)♪ >グローバル変数と同じ使い方になってしまっています。 確かにその通りですね.ご指摘をいただくまで気づきませんでした.ありがとうございます. 以下質問2つ 1.DbUtils,便利ですねこれ (゚д゚lll) ほかにも似たようなものはありませんか?(できるだけ有名どころでお願いします!) 2.「処理をまとめる」にこだわると、逆に分かりにくくなります。特に実装の継承(extends)は後々辛いことになるケースが多いです。 (この考えは自分の脳裏にもチラチラあったのですが,実際の業務では今回のケースのような処理をそもそもまとめるのでしょうか?(executeQueryのほうがわかりやすいし普通無理してまとめないよ!という意見のほうが多数なら,今回の質問はクローズですね(;^ω^)))
ikemo

2018/08/14 04:04

1. 有名どころだとMyBatisですね。 例えば、アノテーションでSQLを書くことができます。 http://www.mybatis.org/mybatis-3/ja/ あとは「オブジェクト関係マッピング(ORM)」というライブラリが該当しますが、こちらは学習コストが高く、必要以上に抽象化されすぎているため、自分は好きじゃないです。 2. 業務アプリだとSQLを呼び出す箇所は数箇所では済まないため、自分が書いたくらいのまとめは必須です。ただ、ちょっとしたプログラムで、今後どれくらい拡張されるか分からないなら、必要なときにリファクタリングすればいいと思います。
chankane

2018/08/14 05:09

返信遅くなり申し訳ありません. いろいろ調べてみましたが,今回は DbUtils を使用してみようを思います. ありがとうございました.サンプルコード,お借りしますね(^^♪
guest

0

実装クラスから考えるに、DAOInterfaceの実装部分にて、常にopen,closeの実装をしなければならないようなので、DAOInterfaceを使って記載する処理をDAOImplに実装し、それぞれのDAOはDAOImplを継承するのが一番かんたんでしょう。

投稿2018/08/13 14:40

A-pZ

総合スコア12011

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

chankane

2018/08/13 14:48

なるほどですね! では2つだけ質問があります. 1. DAOImplでは protected Statement getStatement() といったような処理を実装する必要があるように思うのですがその解釈で会っていますか? 2. DAOInterface, DAOImpl と2つのクラスを用意する意味がないように感じました. いっそのこと AbstractDAO としてまとめるのはありでしょうか?
A-pZ

2018/08/13 14:51

1. 継承する子クラスでも参照するのであると良いでしょう。 2. DAOInterfaceを実装するクラスが単一しかありえないのであれば、AbstractDAOにしてしまい、実装部分を埋めた継承元クラスとして作るのも手ですね。
chankane

2018/08/13 14:58

ご回答ありがとうございます。 すみません 2.の部分をもう少し砕いて説明いただけますか?(理解力がなくすみません(;∀; ))
A-pZ

2018/08/13 15:11

あ、DAOInterfeceを実装して使うクラスが、DAOImplの1つしかないのであれば、DAOInterfaceはフレームワークなどが要求するのでなければ、あってもなくても一緒なので、抽象クラスのAbstraceDAOに乗せてしまって、このAbstractクラスを継承すると、Interfaceを実装したクラスを継承して使うのと変わらないので問題ありませんよ、ですかね。
chankane

2018/08/13 15:38

>DAOInterfeceを実装して使うクラスが、DAOImplの1つしかないのであれば この部分が知りたかったです.ありがとうございました. しつこいようですが最後に1つだけ.先ほど思いついた例ですが, private Connection connection; private Statement statement; これらのフィールドもその抽象クラスに含めてしまって protected Statement getStatement() このメソッドを削除する 代わりに protected void executeUpdate(String sql) protected ResultSet executeQuery(String sql) というメソッドを用意し,派生クラスからは Connection, Statement じたいを取り除いてしまう という考え方はどうでしょう? 自分的には protected void executeUpdate(String sql) protected ResultSet executeQuery(String sql) これなんだよとなってしまうため,個人的に好きではありません. A-pZ様は「これが一番いいな」と思う方法どれですか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問