🧭 目次
-
1.DAO層とは?アーキテクチャ上の役割
-
2.DAOインタフェースと実装の分離(+マッピング処理の設計)
-
3.DAOの責務と粒度の考え方
-
4.DAO層でよくあるアンチパターンとその改善策
-
5.まとめ・次回予告
🧩 1. DAO層とは?アーキテクチャ上の役割
-
DTOが「データ構造」なら、DAOは「永続化ロジック」の責任を持つ
-
DB操作のロジックをActionやService層に持たせないことで、責務分離を徹底
-
主な役割:
-
SQLの発行と実行
-
DTOとのデータマッピング
-
トランザクション制御(複数DAOが絡む場合はService層へ)
-
🧩 2. DAOインタフェースと実装の分離
✅ 実装の基本構造
// UserDao.java(インタフェース)
public interface UserDao {
User findById(int userId);
List<User> findAll();
void insert(User user);
void update(User user);
void delete(int userId);
}
// UserDaoImpl.java(実装)
public class UserDaoImpl implements UserDao {
public User findById(int userId) {
// DB接続、SQL実行、マッピング処理
}
// その他実装...
}
✅ なぜインタフェースと分離するのか?
-
実装の切り替えを柔軟に(MockDao、JdbcDao、JpaDaoなど)
-
テストやDI(依存性注入)との親和性向上
✅ ResultSet → DTO のマッピング処理
public User mapToUser(ResultSet rs) throws SQLException {
User user = new User();
user.setUserId(rs.getInt("user_id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
return user;
}
-
mapToUser() のようなマッピング処理は 専用メソッドに切り出す
-
複数DAOで共通化できる処理は、BaseDao のような親クラスにまとめるのもアリ
🧩 補足:ResultSet → DTO マッピング処理は分離するのがベストプラクティス
DAO 実装クラスの中で頻出する「ResultSet → DTO への変換」処理は、専用のマッピングクラスに分離するのがベストプラクティスです。以下のように切り出すことで、責務が明確になり、再利用性も高まります。
✅ 実装例:UserMapper クラスに分離
// UserMapper.java
public class UserMapper {
public static User map(ResultSet rs) throws SQLException {
User user = new User();
user.setUserId(rs.getInt("user_id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
return user;
}
}
DAO の中ではこのように呼び出します:
// UserDaoImpl.java
public class UserDaoImpl implements UserDao {
public User findById(int id) {
try (Connection conn = ..., PreparedStatement stmt = ...) {
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return UserMapper.map(rs); // ✅ マッパークラスで変換
}
} catch (SQLException e) {
...
}
return null;
}
}
✅ なぜ分離するのか?
-
DAO の役割は「SQLを実行してDTOに渡す」までに限定したほうがよい
-
ResultSet → DTO の変換処理は、マッピングロジックとして切り離すと可読性・保守性が向上する
-
複数DAOで同じDTOを扱う場合も、共通のMapperを使い回せる
✅ より高度な設計に発展可能
-
全DAOで使える汎用的な BaseMapper インタフェースの定義
-
Java 8 以降であれば、ラムダ式 + 関数型インタフェース化も可能(例:Function)
設計の柔軟性を高めるためにも、こうしたマッピングの責務分離はぜひ検討したいポイントです。
🧩 3. DAOの責務と粒度の考え方
✅ DAOの責務
-
1DAO = 1テーブル操作が基本
-
複雑なJOINや業務ロジックはDAOではなくService層へ分離
✅ 粒度のバランス感覚
よくある誤り | 改善指針 |
---|---|
UserDaoに全てのユーザー系機能を書く |
UserDao , UserRoleDao , UserSettingDao などに分割 |
SQLが長大で可読性が悪い | ViewやStored Procedure、Repositoryパターンの活用を検討 |
🧩 4. DAO層でよくあるアンチパターンとその改善策
アンチパターン | 問題点 | 改善策 |
---|---|---|
SQLをActionクラス内に直接書く | 責務の混在、再利用性低下 | DAO層に委譲する |
ResultSetの直接操作を繰り返す | コピペ地獄、バグ温床 | マッピング共通化・専用メソッドの活用 |
例外を握りつぶす | 原因特定不能 | 適切にスロー or ログ記録 |
1DAOで複数テーブルを操作 | 単一責務原則に反する | DAOを分割し、Service層で統合制御 |
📌 まとめ
-
DAOは「DB操作に責任を持つ層」であり、DTOと密接に連携
-
実装クラスは可能な限りマッピング処理を切り出し、再利用性と保守性を高める
-
粒度設計と責務分離を意識することで、大規模化にも耐えられる設計に
▶️ 次回予告
📘 Vol.10.3:DB接続クラスの共通化とドライバ設計
-
ConnectionManager の共通化
-
トランザクション管理戦略
-
JDBCドライバの初期化と設定管理 などを扱います!