# Question08 - データアクセス・データ受け渡し専用の部品の導入(基本CRUD一式) 本資料の練習問題は、複数のプログラミング言語(Java / PHP / C# / Python)で共通して利用できるよう記述されています。使用するデータベース接続の仕組み(JDBC、PDO、ADO.NET、DB-API 2.0準拠のドライバ など)やSQLへのパラメータの指定方法、自動採番された値の取得方法など、言語・環境固有の仕様に基づき、問題文の表現をご使用の環境の仕様に適宜読み替えて進めてください。 [共通の前提](../00.共通前提.md)を参照してください。 ## この問題のねらい - データベースへのアクセス処理を専門に行う部品(DAOに相当する仕組み)を作り、呼び出し元と分離できること - テーブルの1行分のデータを保持する、データ受け渡し専用の入れ物(DTOに相当する仕組み)を作り、データアクセス部品と呼び出し元の間のデータのやり取りに使えること - 検索・登録・更新・削除という基本的なデータ操作一式を、同じデータアクセス部品にまとめられること 以下の設問1で作成する部署データ・部署データアクセス部品は、設問2以降でもそのまま使用・拡張します。 ## ファイル名・フォルダ構成 - 設問ごとに1つのフォルダを作成し、そのフォルダの直下にプログラムのファイルを配置すること。 - フォルダ名は、設問番号に対応した連番(`question01`、`question02`、…)とすること(全言語共通・小文字)。 - ファイル名は、設問番号に対応した連番(`Question01`、`Question02`、…)を基本とし、言語の慣習に合わせて次のとおりとすること。 - 本問題(Question08-DaoDtoCrud)の構成は次のとおり。 ``` Question08-DaoDtoCrud/ ├── question01/         ← 設問1 │ ├── Question01.<拡張子> │ ├── common/ │ │ └── DbUtil.<拡張子>    ← Question07の共通部品 │ ├── dto/ │ │ └── DepartmentDTO.<拡張子> ← 部署データ │ └── dao/ │ └── DepartmentDAO.<拡張子> ← 部署データアクセス部品 ├── question02/         ← 設問2 │ ├── Question02.<拡張子> │ ├── common/ │ │ └── DbUtil.<拡張子>    ← Question07の共通部品 │ ├── dto/ │ │ └── DepartmentDTO.<拡張子> ← 部署データ │ └── dao/ │ └── DepartmentDAO.<拡張子> ← 部署データアクセス部品 ├── question03/         ← 設問3 │ ├── Question03.<拡張子> │ ├── common/ │ │ └── DbUtil.<拡張子>    ← Question07の共通部品 │ ├── dto/ │ │ └── DepartmentDTO.<拡張子> ← 部署データ │ └── dao/ │ └── DepartmentDAO.<拡張子> ← 部署データアクセス部品 ├── question04/         ← 設問4 │ ├── Question04.<拡張子> │ ├── common/ │ │ └── DbUtil.<拡張子>    ← Question07の共通部品 │ ├── dto/ │ │ └── DepartmentDTO.<拡張子> ← 部署データ │ └── dao/ │ └── DepartmentDAO.<拡張子> ← 部署データアクセス部品 └── question05/         ← 設問5 ├── Question05.<拡張子> ├── common/ │ └── DbUtil.<拡張子>    ← Question07の共通部品 ├── dto/ │ └── DepartmentDTO.<拡張子> ← 部署データ └── dao/ └── DepartmentDAO.<拡張子> ← 部署データアクセス部品 ``` - Java: `Question01.java`(公開クラス名もファイル名と同じ `Question01` とする) - C#: `Question01.cs` - PHP: `Question01.php` - Python: `question01.py`(モジュール名は小文字とする慣習のため、ファイル名のみ小文字) - 上記ツリー中の `Question01.<拡張子>` は、使用する言語に応じて上記のファイル名に読み替えること。 - プロジェクトファイルが必要な場合など、言語・開発環境の規則上やむを得ない場合は、その規則を優先してよい。その場合も、設問ごとのフォルダ分けと連番の命名は維持すること。 - 部品は、各設問のフォルダの中に次のフォルダを作成し、その中に配置すること。ファイル名は言語の慣習に合わせて次のとおりとすること(クラス名を付ける言語では、クラス名もファイル名の拡張子を除いた部分と同じ `DbUtil`・`DepartmentDTO`・`DepartmentDAO` とする)。 | 部品 | フォルダ | Java | C# | PHP | Python | | --- | --- | --- | --- | --- | --- | | 共通部品(接続・切断。`Question07`で作成したもの) | `common` | `DbUtil.java` | `DbUtil.cs` | `DbUtil.php` | `db_util.py` | | 部署データ | `dto` | `DepartmentDTO.java` | `DepartmentDTO.cs` | `DepartmentDTO.php` | `department_dto.py` | | 部署データアクセス部品 | `dao` | `DepartmentDAO.java` | `DepartmentDAO.cs` | `DepartmentDAO.php` | `department_dao.py` | - 部署データ・部署データアクセス部品は設問2以降で拡張していくため、各設問のフォルダには、その設問の時点の内容を配置すること(各フォルダ単独で実行できるようにする)。共通部品は、`Question07`で作成したものと同じ内容を配置すること。 --- ## 設問1 部署の1件分のデータ(部署ID・部署名)を保持する入れ物(以下「部署データ」とする。`dto` フォルダの `DepartmentDTO`)を作成してください。 `Question07`で作成した共通部品(`common` フォルダの `DbUtil`)を使用して、データベースへのアクセス処理を専門に行う部品(以下「部署データアクセス部品」とする。`dao` フォルダの `DepartmentDAO`)を作成してください。 まず、部署データアクセス部品に、次の全件取得処理を追加してください。 - 部署テーブルから全件のデータを取得し、1件ずつ部署データに変換したうえで、部署データの一覧(リスト)として返す処理 呼び出し元のプログラムでは、部署データアクセス部品の全件取得処理を呼び出し、取得した一覧を先頭から順に取り出して、それぞれの部署ID・部署名をタブ区切りで出力してください。 ### 要件 - 実際にデータベースへ接続してSQLを実行する処理は、すべて部署データアクセス部品の内部で行うこと - 取得したデータは、部署データという入れ物を経由して呼び出し元に渡すこと ### ヒント - 部品の役割分担は次のとおりです。呼び出し元のプログラムにはSQLや接続・切断の処理が出てこず、部署データアクセス部品を呼び出すだけになるのが完成形です。 ``` 呼び出し元(Question01) │ ① 全件取得処理を呼び出す ▼ 部署データアクセス部品(DepartmentDAO) │ ② 共通部品(DbUtil)で接続し、SQLを実行する ▼ データベース │ ③ 結果を1行ずつ部署データ(DepartmentDTO)に詰めて一覧にし、呼び出し元へ返す ▼ 呼び出し元(Question01) ④ 一覧から部署データを1つずつ取り出し、部署ID・部署名を出力する ``` - **部署データ(DepartmentDTO)**:部署ID・部署名の2つの項目と、それぞれの値を設定する手段・取り出す手段だけを持たせます。データベースへのアクセスや出力の処理は持たせません。 - **部署データアクセス部品(DepartmentDAO)の全件取得処理**:取得結果を1行ずつ取り出すたびに部署データを1つ作って部署ID・部署名を設定し、一覧に追加していきます。最後に、その一覧を戻り値として返します。接続の取得・切断には共通部品を使います。 - **呼び出し元**:全件取得処理の戻り値(部署データの一覧)を先頭から順に取り出し、部署データから部署ID・部署名を取り出して出力します。 ### 実行例 ``` 1 営業部 2 人事部 3 開発部 4 東京支店 5 大阪支店 ``` --- ## 設問2 設問1の部署データアクセス部品に、次の1件取得処理を追加してください。 - 部署IDを1つ受け取り、部署テーブルから該当する部署のデータを検索し、見つかった場合は部署データに変換して返す処理。該当するデータが存在しない場合は、値が無いことを示す特別な値(以下「値なし」とする。Java・C#・PHP: `null`/Python: `None`)を返すものとする 呼び出し元のプログラムでは、「検索する部署IDを入力してください」と表示して標準入力から部署IDを受け取り、部署データアクセス部品の1件取得処理を呼び出してください。 ### 要件 - 実際にデータベースへ接続してSQLを実行する処理は、すべて部署データアクセス部品の内部で行うこと - 取得結果が「値なし」かどうかを明示的に判定し、次のとおり出力すること - 見つかった場合:部署ID・部署名をタブ区切りで出力する - 見つからなかった場合:「一致する部署は見つかりませんでした」と出力する ### ヒント - **部署データアクセス部品の1件取得処理**:受け取った部署IDをパラメータとして渡してSQLを実行します。結果が1行あれば部署データを1つ作って値を設定して返し、結果が無ければ「値なし」を返します。戻り値は、設問1の一覧ではなく部署データ1つ(または「値なし」)です。 - **呼び出し元**:戻り値を受け取ったら、まず「値なし」かどうかを判定し、値がある場合だけ部署データから部署ID・部署名を取り出して出力します。「値なし」のまま中身を取り出そうとすると、多くの言語でエラーになります。 ### 実行例 - 入力が `2` の場合 - 出力:`2 人事部` - 入力が `99`(存在しない部署ID)の場合 - 出力:`一致する部署は見つかりませんでした` --- ## 設問3 設問1の部署データアクセス部品に、次の登録処理を追加してください。 - 部署名を1つ受け取り、部署IDを指定せず(自動的に採番される値を使用して)、部署テーブルに新しいデータとして登録する処理 呼び出し元のプログラムでは、「登録する部署名を入力してください」と表示して標準入力から部署名を受け取り、部署データアクセス部品の登録処理を呼び出して登録を行ってください。 ### 要件 - 実際にデータベースへ接続してSQLを実行する処理は、すべて部署データアクセス部品の内部で行い、呼び出し元はその部品を呼び出すだけにすること - 部署名の登録には、SQLに値を直接埋め込むのではなく、パラメータとして値を渡す仕組みを使用すること - 登録が完了したら、「登録完了」と出力すること - 登録完了の出力に続けて、設問1で作成した全件取得処理を呼び出し、取得した部署データの一覧を部署IDの昇順で、1件につき1行、部署ID・部署名をタブ区切りで出力すること(登録内容がデータベースに反映されていることを、検索によって確認できるようにする) ### ヒント - **部署データアクセス部品の登録処理**:部署名を受け取り、パラメータとして渡して登録のSQLを実行します。部署IDは自動的に採番されるため、受け取る必要はありません。 - **呼び出し元**:登録処理を呼び出して「登録完了」と出力した後、設問1の全件取得処理を呼び出します。一覧を出力する処理は設問1の呼び出し元と同じ書き方で実装できます。 - 「登録完了」などのメッセージは呼び出し元で出力し、部署データアクセス部品には出力の処理を持たせないようにすると、役割分担がはっきりします。 ### 実行例 - 「登録する部署名を入力してください」に対して `広報部` と入力した場合 - 出力: ``` 登録完了 1 営業部 2 人事部 3 開発部 4 東京支店 5 大阪支店 6 広報部 ``` - 上記の部署ID `6` は、テーブル作成用SQLを実行して部署テーブルを作り直した直後に実行した場合の値です。自動採番される値は登録したデータを削除しても元に戻らないため、登録を繰り返した後などは、より大きい部署IDが採番されます。部署IDの数字だけが異なる場合は、実装の誤りではありません([共通の前提](../00.共通前提.md)を参照)。 --- ## 設問4 設問1の部署データアクセス部品に、次の更新処理を追加してください。 - 部署IDと新しい部署名を受け取り、その部署IDに一致する部署の部署名を更新する処理 呼び出し元のプログラムでは、「更新対象の部署IDを入力してください」と表示して標準入力から部署IDを、続けて「新しい部署名を入力してください」と表示して標準入力から新しい部署名を受け取り、部署データアクセス部品の更新処理を呼び出して更新を行ってください。 ### 要件 - 実際にデータベースへ接続してSQLを実行する処理は、すべて部署データアクセス部品の内部で行い、呼び出し元はその部品を呼び出すだけにすること - 部署名の更新には、SQLに値を直接埋め込むのではなく、パラメータとして値を渡す仕組みを使用すること - 部署データアクセス部品の更新処理は、処理結果として、実際に更新されたレコード件数(整数値)を返すこと - 更新の結果、実際に更新された件数を確認し、次のとおり出力すること - 1件以上更新された場合:「更新完了」 - 該当する部署IDが存在せず、1件も更新されなかった場合:「一致する部署は見つかりませんでした」 - 更新完了の場合は、続けて設問2で作成した1件取得処理を使って更新対象の部署IDのデータを取得し、部署ID・部署名をタブ区切りで出力すること(更新内容がデータベースに反映されていることを、検索によって確認できるようにする)。更新が行われなかった場合、この確認のための取得は行わない ### ヒント - **部署データアクセス部品の更新処理**:部署IDと新しい部署名を受け取り、パラメータとして渡して更新のSQLを実行します。多くの言語・環境では、更新のSQLを実行すると、実際に更新された件数を取得できます。その件数をそのまま戻り値として返します。 - **呼び出し元**:更新処理の戻り値(更新件数)を見て、「更新完了」か「一致する部署は見つかりませんでした」かを判断して出力します。件数による判断と出力は部署データアクセス部品ではなく、呼び出し元で行います。 - 更新完了の場合の確認は、設問2の1件取得処理と、その戻り値から部署ID・部署名を取り出して出力する書き方をそのまま使えます。 ### 実行例 - 「更新対象の部署IDを入力してください」に対して `2`、「新しい部署名を入力してください」に対して `人材開発部` と入力した場合 - 出力: ``` 更新完了 2 人材開発部 ``` - 「更新対象の部署IDを入力してください」に対して `99`(存在しない部署ID)、「新しい部署名を入力してください」に対して `広報部` と入力した場合 - 出力:`一致する部署は見つかりませんでした` --- ## 設問5 設問1の部署データアクセス部品に、次の削除処理を追加してください。 - 部署IDを1つ受け取り、部署テーブルから該当する部署のデータを削除する処理 呼び出し元のプログラムでは、「削除対象の部署IDを入力してください」と表示して標準入力から削除対象の部署IDを受け取り、部署データアクセス部品の削除処理を呼び出して削除を行ってください。 なお、部署ID `1`〜`3` の部署には社員テーブルの社員が所属しているため、削除しようとするとエラーになります。動作確認では、所属する社員がいない部署ID `4`・`5` の部署を削除の対象としてください([共通の前提](../00.共通前提.md)を参照)。削除したデータは元に戻らないため、この設問を実行する前(2回以上実行する場合も含む)には、テーブル作成用SQLを実行し直して、データベースを初期データの状態に戻してください。 ### 要件 - 実際にデータベースへ接続してSQLを実行する処理は、すべて部署データアクセス部品の内部で行い、呼び出し元はその部品を呼び出すだけにすること - 部署データアクセス部品の削除処理は、処理結果として、実際に削除されたレコード件数(整数値)を返すこと - 削除の結果、実際に削除された件数を確認し、次のとおり出力すること - 1件以上削除された場合:「削除完了」 - 該当する部署IDが存在せず、1件も削除されなかった場合:「一致する部署は見つかりませんでした」 - 削除完了の場合は、続けて設問2で作成した1件取得処理を使って削除対象の部署IDのデータを取得すること(削除内容がデータベースに反映されていることを、検索によって確認できるようにする)。取得結果が「値なし」であれば「一致する部署は見つかりませんでした」と出力する。削除が行われなかった場合、この確認のための取得は行わない ### ヒント - **部署データアクセス部品の削除処理**:部署IDを受け取り、パラメータとして渡して削除のSQLを実行します。設問4の更新処理と同じく、SQLの実行で得られる実際に削除された件数を、そのまま戻り値として返します。 - **呼び出し元**:削除処理の戻り値(削除件数)を見て、「削除完了」か「一致する部署は見つかりませんでした」かを判断して出力します。 - 削除完了の場合の確認では、設問2の1件取得処理を呼び出し、戻り値が「値なし」であることを判定します。削除が正しく行われていれば、1件取得処理は「値なし」を返します。 ### 実行例 - 「削除対象の部署IDを入力してください」に対して `4` と入力した場合 - 出力: ``` 削除完了 一致する部署は見つかりませんでした ``` - 「削除対象の部署IDを入力してください」に対して `99`(存在しない部署ID)と入力した場合 - 出力:`一致する部署は見つかりませんでした`