はじめに
DreamHanksの松下です。
前回は勤務時間選択画面のJSPについて解説しました。
今回は勤務時間登録画面を作るために必要なデータをサーバーで生成します。
そのロジック的なところを解説していきます。
勤務時間登録画面の仕様説明
①前回の選択画面で選択した月の一覧を表示する
②今月だった場合、本日(10/26)の出社時間/退社時間は前回の選択画面から参照する
③DBから取得した値(登録済みの値)分画面に描画する
④DBから取得できなかった場合(未登録の場合)空欄で描画する
ロジックの解説
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 |
/** * 勤務時間登録画面を作成するメソッド * @param worktimeChoiceForm 勤務時間選択画面で入力されてた値 * @param result バリデーションチェック結果 * @param model model * @return 勤務時間登録画面.jsp */ @RequestMapping(value = "/regist") public String regist(@ModelAttribute @Valid WorktimeChoiceForm worktimeChoiceForm, BindingResult result, Model model) { // ヴァリデーションチェック不正がある場合 // result.hasErrors()の意味はエラーがある場合にtrueをreturnするメソッド if (result.hasErrors()) { return "worktime_choice"; } // システム日付を取得するためのカレンダー Calendar todateCalendar = Calendar.getInstance(); // 勤務時間選択画面で先月を選択された場合 if ("0".equals(worktimeChoiceForm.getMonth())) { // 先月を取得 todateCalendar.set(Calendar.MONTH, todateCalendar.get(Calendar.MONTH) - 1); } // DBの年月フォーマットを生成する。 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM"); // 勤務時間管理Listを取得する List<Worktime> worktimeListByMemberId = memberService.getWorktimeByMemberId(Integer.parseInt( worktimeChoiceForm.getMemberId()), sdf.format(todateCalendar.getTime()) + "%"); // 勤務時間登録画面の画面描画するためのオブジェクトを生成する List<Worktime> wtList = memberService.makeWorktimeRegstObj(todateCalendar, worktimeListByMemberId, worktimeChoiceForm); // 出社時間・退社時間を生成する List<String> worktimeList = memberService.makeWorktimeList(); // 勤務時間登録画面でjspにformを使えるようにするために(インポートのような感覚で)attributeする。 model.addAttribute("worktimeRegistForm", new WorktimeRegistForm()); // 勤務時間選択画面に年をマッピングして渡す model.addAttribute("year", todateCalendar.get(Calendar.YEAR)); // 勤務時間選択画面に月をマッピングして渡す model.addAttribute("month", todateCalendar.get(Calendar.MONTH) + 1); // 勤務時間選択画面にwtListをマッピングして渡す model.addAttribute("wtList", wtList); // 勤務時間選択画面に出社/退社時間をマッピングして渡す model.addAttribute("worktimeList", worktimeList); // 勤務時間選択画面にworktimeChoiceFormをマッピングして渡す model.addAttribute("worktimeChoiceForm", worktimeChoiceForm); // 勤務時間選択画面にメンバーIDをマッピングして渡す model.addAttribute("memberId", worktimeChoiceForm.getMemberId()); return "worktime_regist"; } |
今回登場するコントローラです。
このように長々とソースコードが書かれていると、処理を追うのに混乱してしまいますが
慣れてくると下記のように見えてきます。
徐々に具体的に各セクションの処理を理解していきましょう。抽象 ⇒ 具体
(抽象から具体、またその具体を抽象から具体というように考えましょう)
基準値の設定:カレンダーメソッドを使う
javaが提供している
1 |
Calendar システム日付のカレンダーオブジェクト = Calendar.getInstance(); |
Calendar.getInstance();をするだけで、今月(10月だとしたら10月の)カレンダーオブジェクトが生成されます。
カレンダーオブジェクトということは10/1~10/31までの日付や曜日、システム日付が入った「まとまりの箱」(オブジェクト)を生成するということです。
先月か今月かの判定処理
前回の選択画面で今月を「1」、先月を「0」とvalue値を決めました。
0の場合先月なので、カレンダーを先月に設定します。
1 2 3 4 5 |
// 勤務時間選択画面で先月を選択された場合 if ("0".equals(worktimeChoiceForm.getMonth())) { // 先月を取得 todateCalendar.set(Calendar.MONTH, todateCalendar.get(Calendar.MONTH) - 1); } |
メイン処理:選択月のユーザの勤務管理情報を取得する
コントローラ側では、計算やDB操作は行いません。
ビジネスロジックはサービス側で行うという基本を思い出してください。
◆コントローラ側
1 2 3 4 5 6 |
// DBの年月フォーマットを生成する。 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM"); // 勤務時間管理Listを取得する List<Worktime> worktimeListByMemberId = memberService.getWorktimeByMemberId(Integer.parseInt( worktimeChoiceForm.getMemberId()), sdf.format(todateCalendar.getTime()) + "%"); |
メンバーIDと年月を渡せば、その年月のユーザの勤務情報が取れるメソッドです。
文字列の”%”をなぜ文字列連結しているの?
年月例えば「202009」に%を足している理由はLIKE検索をするためです。
work_dateが2020年の9月のレコードを全て取得したいので、LIKE検索を使います。
DB操作を行う場合はツリー構造を思い描きながら、データ構造を意識して適切なSQL文を考えましょう。
今回の場合memberIdである程度絞れますが、同じmemberIdだとしても
その中に2020年のレコードもあれば2021のレコードもある、9月もあれば、10月もある
このようにmemberIdに対して、年と月でデータがツリー構造になっています。
◆サービス側
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * メンバー管理IDで勤務時間管理のオブジェクトを取得する * @param memberId メンバー管理ID * @param targetDate 対象の年月 * @return 勤務時間管理のオブジェクトList */ public 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; } |
なぜコントローラ側(呼び元のメソッドの引数)でInteger.parseInt(worktimeChoiceForm.getMemberId())をしてStringをInteger型に変えたかというと、ビジネスロジックの汎用性を高めるためです。
このメソッド内で受け取ったStringをIntegerにPathingしていたら、他の型で着た場合に対応ができない汎用性が少ないメソッドになります。
このメソッドはいつもDBと同じ型のInteger型のmemberIdを引数で渡せば、勝手に勤務時間が取得できる。という風にしたいのです。これが汎用性が高い共通部品化されているとも言います。
mybatisの機能を使って、andWorkDateLikeを使ってさっきの文字列「yyyyMM%」を渡せば
簡単に疑似的にLIKE検索のSQL文ができます。
メソッドの汎用性を高めるために常に意識すること
同じロジックは2度書かない
⇒特にデータ生成・計算ロジックは最小単位にして、共通部品化する
他のどこからメソッド呼び出しをされた場合でも、使いやすいメソッドになるように引数に何を入れるかを考える。
何をするメソッドなのか?これが最小単位なのか?を常に考える。
メイン処理:登録画面の画面描画するためのオブジェクトを生成する
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 73 74 75 76 77 78 79 80 81 82 83 84 |
/** * 勤務時間登録画面の画面描画するためのオブジェクトを生成するメソッド * @param todateCalendar カレンダーオブジェクト(先月or今月) * @param worktimeListByMemberId 入力ユーザの勤務時間管理情報List * @param inputedStartTime 選択画面で入力された開始時間 * @param inputedEndTime 選択画面で入力された終了時間 * @return 勤務時間登録画面の画面描画するためのオブジェクト */ public List<Worktime> makeWorktimeRegstObj(Calendar todateCalendar, List<Worktime> worktimeListByMemberId, String inputedStartTime, String inputedEndTime) { // 日数を取得 int days = todateCalendar.getActualMaximum(Calendar.DATE); // 本日を取得 int today = todateCalendar.get(Calendar.DATE); // 月初日を取得するためのカレンダー Calendar firstdateCalendar = Calendar.getInstance(); firstdateCalendar.set(Calendar.YEAR, todateCalendar.get(Calendar.YEAR)); firstdateCalendar.set(Calendar.MONTH, todateCalendar.get(Calendar.MONTH)); firstdateCalendar.set(Calendar.DATE, 1); // 曜日を取得 int week = firstdateCalendar.get(Calendar.DAY_OF_WEEK); // 勤務時間管理TBLのオブジェクトを作成 List<Worktime> wtList = new ArrayList<Worktime>(); // 選択月の日数分処理を繰り返す for (int i = 0; i < days; i++) { // 月初日からDBから取得したListのSizeまで処理をする if (i < worktimeListByMemberId.size()) { Integer intWorkDate = Integer.parseInt(worktimeListByMemberId.get(i).getWorkDate().substring(6)); worktimeListByMemberId.get(i).setWorkDate(Integer.toString(intWorkDate)); // 曜日インターバルを作成 worktimeListByMemberId.get(i).setWeek((worktimeListByMemberId.get(i).getWeek()) % 7); // 始業時間 String startTime = worktimeListByMemberId.get(i).getStartTime(); if (!"".equals(startTime)) { // 始業時間をコロン区切りにする String startTimeWithColon = startTime.substring(0, 2) + ":" + startTime.substring(2, startTime.length()); // 始業時間(コロン区切り)をセットする worktimeListByMemberId.get(i).setStartTime(startTimeWithColon); } // デフォルトの終業時間 String endTime = worktimeListByMemberId.get(i).getEndTime(); if (!"".equals(endTime)) { // デフォルトの終業時間をコロン区切りにする String endTimeWithColon = endTime.substring(0, 2) + ":" + endTime.substring(2, endTime.length()); // デフォルトの終業時間(コロン区切り)をセットする worktimeListByMemberId.get(i).setEndTime(endTimeWithColon); } wtList.add(worktimeListByMemberId.get(i)); } // 本日以降 else { // 勤務時間管理TBLのオブジェクトに一覧情報を詰める Worktime wt = new Worktime(); // 勤務日付を作成 wt.setWorkDate(Integer.toString(i + 1)); // 曜日インターバルを作成 wt.setWeek((week + i) % 7); // DBから取得したListのSizeから今日まで(週末は除く) if (i < today && (wt.getWeek() != 0 && wt.getWeek() != 1)) { wt.setStartTime(inputedStartTime); wt.setEndTime(inputedEndTime); } wtList.add(wt); } } return wtList; } |
このロジックは長くてなんだか難しいそうに思えますが、抽象から具体で考えます。
最後に
今回は「勤務時間登録画面を描画するためのオブジェクト生成」をサーバー内で行いました。
生成したオブジェクトをアトリビュートし、jspでどのように使うのかを解説していきます。
コメント