67645


現在ServletとJspでユーザー管理画面のようなものを作成しており以下の機能があります。

・ユーザー作成  
・ユーザー編集  
・ユーザー一覧表示  
・ユーザー検索  
・ユーザー削除

[code]

    Model
      |
      ----dto
      |    |
      |    ---User.java
      -----dao
      |    |
      |    ---UserDao.java
      |
      --- UserSearcher.java
    
[/code]

そしてモデルの中でDTOとしてUser.java、DAOとしてUserDao.javaがあります。  
UserDAOにCRUD処理が記述されており、それをServletクラス（コントローラー）から呼び出しております。  
検索機能は独立してモデルの中にUserSearcherクラスとして存在しており、UserSearcherのsearchメソッドで、DBにアクセスして結果を取得しております。

[code]

    public class UserSearcher {
        private Connection conn;
    
        private List<SearchCondition<?>> conditions;
    
        public UserSearcher(Connection conn, List<SearchCondition<?>> conditions) {
            this.conn = conn;
            this.conditions = conditions;
        }
    
        public List<User> execute() throws Exception {
            if (isEmpty(this.conditions)) return null;
    
            List<String> wheres = new ArrayList<>();
            for (SearchCondition<?> condition: conditions) {
                if (condition.isEmpty()) continue;
    
                String where = condition.getWhere();
                wheres.add(where);
            }
    
            String where = String.join(" AND ", wheres.toArray(new String[0]));
            String sql = "SELECT * FROM users WHERE " + where;
    
            PreparedStatement pStmt = conn.prepareStatement(sql);
            pStmt = bindParams(pStmt);
    
            ResultSet resultSet = pStmt.executeQuery();
    
            List<User> users= new ArrayList<>();
            while(resultSet.next()) {
                User user = new User();
                // userに値をset
    
                users.add(user);
            }
    
            return users;
        }
    
        private PreparedStatement bindParams(PreparedStatement pStmt) throws Exception {
            int i = 1;
            for (SearchCondition<?> condition: conditions) {
                Object value = condition.getValue();
                if (value instanceof String) {
                    pStmt.setString(i, (String) value);
                } else if (value instanceof Integer) {
                    pStmt.setInt(i, (Integer) value);
                }
    
                i++;
            }
    
            return pStmt;
        }
    }
    
    
[/code]

SearchConditionクラスは、部分一致や前方一致などの検索条件の親クラスです。  
それを継承した検索条件のクラスでは部分一致であればgetWhereで"name LIKE ?"を返し、getValueで"％hoge％"などを返します。

そしてここからが本題なのですが、DAOが存在するアプリケーションにおいて、別クラスでDBにアクセスする設計にするべきではないでしょうか？私の感覚ではDAOが存在しているのに、DAO以外でDBにアクセスすることに違和感を感じております。

ただ今後商品テーブルのようなものを作る予定であり、そちらにも検索機能をつけたいと思っているため、検索に関するロジックに関しては共通で使えるクラスとして保持したい(今はUserSearcherですがいずれはSearcherになる予定)のです。DAOに検索ロジックをもたせてしまうと、同じロジックを今後のItemDAOにも実装することになってしまいます。

となるとSearcherクラスをクエリを組み立てるクラス（SearchQueryBuilder)として作り直し、クエリとパラメータを渡せば実行できるメソッドをDAOに作るべきなのか？などと考えておりますが、なんかパッとしません。

こういう役割をどのように分担するべきなのか、教えていただきたいです。


CC BY-SA 4.0
2020-06-13T06:03:05.137
67617
2020-06-14T06:54:51.837
2020-06-13T06:18:22.500
question
java servlet
Javaプログラミングにおいて、検索とそれに関わる機能はどのクラスが持つべき責務でしょうか？


>
> そしてここからが本題なのですが、DAOが存在するアプリケーションにおいて、別クラスでDBにアクセスする設計にするべきではないでしょうか？私の感覚ではDAOが存在しているのに、DAO以外でDBにアクセスすることに違和感を感じております。

これはおっしゃるとおりだと思います。`dao.UserDao`というクラスがあるにも関わらず、実は別のパッケージの別のクラスを使ってDBアクセスする必要がある可能性がある、というのは非常に煩わしいです。

>
> 今後商品テーブルのようなものを作る予定であり、そちらにも検索機能をつけたいと思っているため、検索に関するロジックに関しては共通で使えるクラスとして保持したい

ここが理解できていないです。(一般的に考えると)ユーザーと商品の検索であまり共通化できる要素があるようには思えません。

>
> Searcherクラスをクエリを組み立てるクラス（SearchQueryBuilder)として作り直し、クエリとパラメータを渡せば実行できるメソッドをDAOに作るべきなのか？

`SearchQueryBuilder`と呼称されているようなものを作る、という方向性については私もそうであるかな、と思います。  
ただ、今回の例に挙げられているような、単純なSQLの組み立てに関する処理を想定しているのであれば(ユーザと商品の検索で共通化できる、というのはつまりそういうことでしょうか)、その`SearchQueryBuilder`は利用者(※質問文中の"Servletクラス（コントローラー）"のこと)からは隠蔽し、DAOクラスが必要に応じて利用する、という形の方が良いのではないかと思います。  
(利用者が意識するのはDAO(のメソッド呼び分け)だけで`SearchQueryBuilder`を直接使うことはない、ということになります。)

DAOに直接実装するわけではないので

> DAOに検索ロジックをもたせてしまうと、同じロジックを今後のItemDAOにも実装することになってしまいます。

というようなことも無いかと思います。


CC BY-SA 4.0
2020-06-14T06:54:51.837
67645
2020-06-14T06:54:51.837
2808
67617
answer