はじめに
DreamHanksの松下です。
前回は勤務時間登録画面の描画と、一覧の複数あるデータをサーバーに送る方法について解説しました。
今回は受け取った一覧のデータを使って、DBの勤務時間管理TBLを登録/更新処理を解説いたします。
ソースコード
コントローラ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
/** * 勤務時間登録画面で入力された一覧情報を使って、勤務時間管理TBLを更新/登録を行うメソッド * @param worktimeRegistForm 勤務管理登録画面で入力された一覧情報form * @param result 入力値のバリデーション結果 * @param model ControllerからViewに値を受け渡すためのパラメータ * @return 勤務時間選択画面 */ @RequestMapping(value = "/registProcess") public String registProcess(@ModelAttribute WorktimeRegistForm worktimeRegistForm, BindingResult result, Model model) { // 勤務時間登録画面で入力された値にエラーがある場合に、画面にエラーメッセージを表示する if (!memberService.checkInputValueWorktimeRegist(worktimeRegistForm)) { // エラーメッセージをを表示する model.addAttribute("message", "出社時間は退社時間以前を入力してください。"); return "regist"; } // DBを更新か登録する memberService.updateOrInsertWorktime(worktimeRegistForm); // 出社時間・退社時間を生成する List<String> worktimeList = memberService.makeWorktimeList(); // 勤務時間選択画面でjspにformを使えるようにするために(インポートのような感覚で)attributeする。 model.addAttribute("worktimeChoiceForm", new WorktimeChoiceForm()); // 勤務時間選択画面にmemberオブジェクトをマッピングして渡す model.addAttribute("member", worktimeRegistForm.getMemberId()); // 勤務時間選択画面に出社/退社時間をマッピングして渡す model.addAttribute("worktimeList", worktimeList); return "worktime_choice"; } |
今回のメインテーマである更新・登録処理はほとんどがサービスクラスで行われます。
コントローラはビジネスロジック(サービスクラス)のメソッドを呼ぶだけで、具体的な処理は切り離すものだからです。
今回も上から順に
・ヴァリデーションチェック
・更新/登録処理メソッド呼び出し
・出社/退社時間を生成するメソッド呼び出し
・複数のアトリビュート
・勤務時間選択画面のJSPをリターン
という風に構成されます。
サービス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
/** * 勤務管理TBLを更新か登録するメソッド * @param worktimeRegistForm 勤務管理登録画面で入力された一覧情報form */ public void updateOrInsertWorktime(WorktimeRegistForm worktimeRegistForm) { // 特定したmember情報のメンバーID Integer memberId = Integer.parseInt(worktimeRegistForm.getMemberId()); // 選択月の勤務時間管理Listを取得する List<Worktime> worktimeListByMemberId = getWorktimeByMemberId(memberId, worktimeRegistForm.getYear() + String.format("%02d", Integer.parseInt(worktimeRegistForm.getMonth())) + "%"); // システム日付(今月)を取得するためのカレンダー // 例えば今の日付が「20201024]ならば⇒「2020 9 24」という風になる Calendar todateCalendar = Calendar.getInstance(); // 本日を取得 int today = todateCalendar.get(Calendar.DATE); // 勤務時間選択画面で先月を選択された場合 // MONTHに関しては最初の値が0開始なので、先月かどうかを比べるとき-1をしなくてOK if (Integer.toString(todateCalendar.get(Calendar.MONTH)).equals(worktimeRegistForm.getMonth())) { // 先月を取得 todateCalendar.set(Calendar.MONTH, todateCalendar.get(Calendar.MONTH) - 1); // 先月の月末日に設定する today = todateCalendar.getActualMaximum(Calendar.DATE); } // 今月の月初日を取得するためのカレンダー Calendar firstdateCalendar = Calendar.getInstance(); firstdateCalendar.set(Calendar.DATE, 1); // 勤務時間選択画面で先月を選択された場合 // MONTHに関しては最初の値が0開始なので、先月かどうかを比べるとき-1をしなくてOK if (Integer.toString(firstdateCalendar.get(Calendar.MONTH)).equals(worktimeRegistForm.getMonth())) { // 先月を取得 firstdateCalendar.set(Calendar.MONTH, firstdateCalendar.get(Calendar.MONTH) - 1); } // 選択月の月初日を生成する int firstDate = firstdateCalendar.get(Calendar.DATE); // 先月か今月の月初日の曜日を取得 int week = firstdateCalendar.get(Calendar.DAY_OF_WEEK); // 更新処理、登録処理の処理カウント int updateLoopCount = 0; // 取得した勤務時間管理がある場合、更新処理 for (Worktime worktimeByMemberId : worktimeListByMemberId) { // 更新処理 updateWorktime(worktimeByMemberId, worktimeRegistForm, updateLoopCount); updateLoopCount++; firstDate++; week++; } // 残りの日付を月末日までの日付で登録処理を繰り返す for (int insertLoopCount = updateLoopCount; insertLoopCount < today; insertLoopCount++) { // 本日の勤務日を生成する String workDate = worktimeRegistForm.getYear() + String.format("%02d", Integer.parseInt(worktimeRegistForm.getMonth())) + String.format("%02d", firstDate); int calculatedWeek = (week) % 7; insertWorktime(memberId, workDate, calculatedWeek, worktimeRegistForm, insertLoopCount); firstDate++; week++; } } |
サービスの大まかな流れは下記のようなフローになります。
①メンバーIDで対象の選択月の勤務時間管理Listを取得する
②今月か先月かを選択する
③勤務時間管理List分更新処理
④今月の場合は勤務時間管理List以降で実行日(システム日付)までのレコードを登録
先月の場合は勤務時間管理List以降で月末日までのレコードを登録
◆④についてもう少し詳しく説明
①で取得できた勤務時間管理List分は更新
それ以降の扱いは今月はシステム日付まで、先月は月末までを登録するということです。
①で取得できた勤務時間管理Listというのは、もうすでに登録されているレコードなので
そこに関しては更新を行う。
それ以降というのはレコード自体がないので登録を行う。
しかし、今月は実行日以降のレコードを登録する必要がない(未来の話になるから)ので、実行日まで
先月は過去の話なので、登録画面でレコードに何も入力されてなかったとしても
その入力がないことを勤務時間管理テーブルに登録します。
なかなか文章で説明すると複雑な内容なのでややこしいですが、
ご質問等ありましたら、「Q&A」掲示板で承ります。
メソッドの共通部品化をしよう!
今回の登録、更新メソッドは下記のようにメソッドの中でサービス内でしかメソッド呼び出しのできない、privateメソッドを呼び出しています。
上記のイメージのように3つのprivateメソッドを呼び出して、処理しています。
このようにメソッドをツリー構造に作ることによって、可読性が上がり何をしているメソッドなのかわかりやすくなります。
さらに他にもメリットがあります。
メソッドを最小限の処理で作ることで、上記のイメージのように他のメソッドからも呼び出し可能になります。
これは2度同じロジックを書く手間が省けますし、ソースコードを共通化することによって
バグが生じたときに1か所を修正するだけで済みます。
処理を最小限にして、共通部品化する。
常にロジックが共通化できるのではないかと考えながらコーディングをしてください。
共通部品化のコツ
updateOrInsertWorktimeメソッドで下記のようにmemberIdをInteger.parseInt()をして
Integer型に変えてかgetWorktimeByMemberIdの引数に渡しています。
1 2 3 4 5 6 |
// 特定したmember情報のメンバーID Integer memberId = Integer.parseInt(worktimeRegistForm.getMemberId()); // 選択月の勤務時間管理Listを取得する List<Worktime> worktimeListByMemberId = getWorktimeByMemberId(memberId, worktimeRegistForm.getYear() + String.format("%02d", Integer.parseInt(worktimeRegistForm.getMonth())) + "%"); |
memberIdはWorktimeオブジェクトでIntegerで定義されています。
もし、getWorktimeByMemberId内でStringで受け取り、それをInteger.parseInt()をするようなメソッドを作ってしまったら、他の型に対応できなくなってしまいます。
もし元のInteger型だったとしても、いちいちString型にpathingしなくてはいけなくなります。
よって元々の定義されているInteger型で受け取るほうが共通部品化として効率が良くなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * メンバー管理IDで勤務時間管理のオブジェクトを取得する * @param memberId メンバー管理ID * @param targetDate 対象の年月 * @return 勤務時間管理のオブジェクトList */ private List<Worktime> getWorktimeByMemberId(Integer memberId, String targetDate) { WorktimeExample worktimeExample = new WorktimeExample(); worktimeExample.createCriteria().andMemberIdEqualTo(memberId).andWorkDateLike(targetDate); List<Worktime> worktimeList = worktimeMapper.selectByExampleWithBLOBs(worktimeExample); return worktimeList; } |
1桁月の対応方法
メンバー管理IDで勤務時間管理のオブジェクトを取得するときに、対象月をLike検索します。
DBでは[01,02,03,04,05,06,07,08,09,10,11,12]というような情報が格納されています。
しかし画面で受け取った「worktimeRegistForm.getMonth()」は1桁月の場合は「9」というように格納されています。
この状態で9月を検索した場合、検索に引っ掛かりません。
そこで下記のようにString.formatをします。
1 |
String.format("%02d", Integer.parseInt(worktimeRegistForm.getMonth())) + "%") |
「%d」は10進整数を表します。
「%02d」は最低2桁で出力し、2桁目を0埋めしたい場合の書式設定です。
登録・更新処理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
/** * 勤務時間管理TBLを更新する * @param worktimeByMemberId 対象のメンバーの選択月のレコード * @param worktimeRegistForm 登録画面で入力された内容 * @param loopCount 処理カウント */ private void updateWorktime(Worktime worktimeByMemberId, WorktimeRegistForm worktimeRegistForm, int loopCount) { // 画面入力内容(更新リスト) List<WorktimeRegistForm> updateList = worktimeRegistForm.getWorktimeRegistFormList(); // Worktimeオブジェクトを生成して、更新内容を追加する Worktime updateWorktime = new Worktime(); // updateするときは主キーは使えない、後で消す // updateWorktime.setId(worktimeByMemberId.getId()); updateWorktime.setMemberId(worktimeByMemberId.getMemberId()); updateWorktime.setWorkDate(worktimeByMemberId.getWorkDate()); updateWorktime.setWeek(worktimeByMemberId.getWeek()); if (updateList.get(loopCount).getStartTime() != null) { updateWorktime.setStartTime(updateList.get(loopCount).getStartTime().replace(":", "")); } if (updateList.get(loopCount).getEndTime() != null) { updateWorktime.setEndTime(updateList.get(loopCount).getEndTime().replace(":", "")); } if (updateList.get(loopCount).getRestTime() != null) { updateWorktime.setRestTime(updateList.get(loopCount).getRestTime().replace(":", "")); } if (updateList.get(loopCount).getMemo() != null) { updateWorktime.setMemo(updateList.get(loopCount).getMemo()); } WorktimeExample worktimeExample = new WorktimeExample(); // このように条件を入れる(202009028 最後) worktimeExample.createCriteria().andIdEqualTo(worktimeByMemberId.getId()); worktimeMapper.updateByExampleSelective(updateWorktime, worktimeExample); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/** * 勤務時間管理TBLを登録する * @param memberId * @param workDate * @param week * @param worktimeRegistForm 登録画面で入力された内容 * @param loopCount 処理カウント */ private void insertWorktime(Integer memberId, String workDate, int week, WorktimeRegistForm worktimeRegistForm, int loopCount) { // 画面入力内容(登録リスト) List<WorktimeRegistForm> insertList = worktimeRegistForm.getWorktimeRegistFormList(); // Worktimeオブジェクトを生成して、更新内容を追加する Worktime updateWorktime = new Worktime(); // idにautoIncrement制約をつけているので、idを設定しないでinserすれば、最大に1大したシーケンス番号で登録される // updateWorktime.setId(worktimeByMemberId.getId()); updateWorktime.setMemberId(memberId); updateWorktime.setWorkDate(workDate); updateWorktime.setWeek(week); if (insertList.get(loopCount).getStartTime() != null) { updateWorktime.setStartTime(insertList.get(loopCount).getStartTime().replace(":", "")); } if (insertList.get(loopCount).getEndTime() != null) { updateWorktime.setEndTime(insertList.get(loopCount).getEndTime().replace(":", "")); } if (insertList.get(loopCount).getRestTime() != null) { updateWorktime.setRestTime(insertList.get(loopCount).getRestTime().replace(":", "")); } if (insertList.get(loopCount).getMemo() != null) { updateWorktime.setMemo(insertList.get(loopCount).getMemo()); } worktimeMapper.insert(updateWorktime); } |
登録・更新処理ともに、画面入力された一覧のレコードをListのオブジェクトを引数として渡されています。
updateList.get(loopCount).getStartTime()のようにListなので、getメソッドを使い、何レコード目(何日目)かを指定し、どのエンティティかを指定するgetメソッドを使うことによって
対象のレコードの対象のカラムデータに接近できます。
Worktimeのオブジェクトにsetterで詰めなおし、登録か更新かを行います。
最後に
次回はセッションを使いログイン情報をサーバーで保持する方法を解説いたします。
セッションを使うことによって、一度ログインした場合その情報を一定期間サーバーで保留をするので、画面にhiddenで渡す必要がなくなります。
お読みいただきありがとうございました。
コメント