# mybatisについて(2) ## mybatisについて これは前回説明したので[そちら](https://hackmd.io/@ka-777/Hkdqwxvid)を参照してください。今回は続きのサンプルアプリの製作です。 今回は前回の表示に加えてCRUD操作(Create-Read-Update-Delete)をボタンを押すだけでできるようにします。 ## 2つ目のアプリ ### フォルダ構造 ![](https://i.imgur.com/6nWti2l.png) mybatisシリーズは前回のに追加、編集をして作成しているので前回のを作成していない場合はそちらからお願いします。 ファイル構造にあるけど今回プログラムを掲載していない場合は前回と同じプログラムのまま使用しています。 ### プロジェクト作成&依存関係 基本的には前回と全部同じなので、前回のspring-mybatisをコピーしてプロジェクト名をcrudとかを付け加えて使用したほうがいいです。 ### モデルの編集 Teacher.java ``` package dev.itboot.mb.model; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import lombok.Getter; import lombok.Setter; @Setter @Getter public class Teacher { private Long id; // Validationを追加 @NotBlank @Size(max = 60) private String userName; // Validationを追加 @NotBlank @Email @Size(max = 254) private String email; } ``` > 今までに説明しましたが軽く説明を > NotBlankで空白を禁止して必ず何かしらの値が入るようにしています。また@Sizeで文字数の制限もしています。 > @Emailはこれをつけるだけで適性なemail形式かどうかを判断してチェックしてくれます。 ### マッパーの編集 TeacherMapper.java ``` package dev.itboot.mb.mapper; import java.util.List; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import dev.itboot.mb.model.Teacher; @Mapper public interface TeacherMapper { @Select("SELECT * FROM teacher") List<Teacher> selectAll(); // 以下を追加 @Select({ "SELECT * FROM teacher", "WHERE id = #{id}" }) Teacher selectByPrimaryKey(Long id); @Insert({ "INSERT INTO teacher(user_name, email)", "VALUES(#{userName}, #{email})" }) int insert(Teacher record); @Update({ "UPDATE teacher", "SET user_name = #{userName}, email = #{email}", "WHERE id = #{id}" }) int updateByPrimaryKey(Teacher record); @Delete({ "DELETE FROM teacher", "WHERE id = #{id}" }) int deleteByPrimaryKey(Long id); } ``` > アノテーションで記述した@SELECT,@INSERT,@UPDATE,@DELETEはすべて対応するSQL文を記述することで使うことができます。 > またSQLに変数を渡す場合は#{変数名}の形で記述します(SELECT文のidを渡すとこ等) > クラスの中身を渡す場合は#{フィールド名}です(INSERT文のuserNameとemailを渡すとこ等) ### サービスの作成 dev.itboot.mbで右クリック→新規→クラスで作成します パッケージ名に.serviceを追加して見やすくファイルをわけます 今回のファイル名はTeacherService.javaです TeacherService.java ``` package dev.itboot.mb.service; import java.util.List; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import dev.itboot.mb.model.Teacher; import dev.itboot.mb.mapper.TeacherMapper; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Transactional @Service public class TeacherService { private final TeacherMapper mapper; public List<Teacher> selectAll() { return mapper.selectAll(); } public Teacher selectByPrimaryKey(Long id) { return mapper.selectByPrimaryKey(id); } public void save(Teacher teacher) { if (teacher.getId() == null) { mapper.insert(teacher); } else { mapper.updateByPrimaryKey(teacher); } } public void deleteByPrimaryKey(Long id) { mapper.deleteByPrimaryKey(id); } } ``` 基本的にサービス層には複雑なDBの処理を入れることがあるのでトランザクション処理(処理の一貫性を保つ方法)で実行するため@Transactionalというトランザクション処理をしてくれるアノテーションを記述しています。また@Serviceはサービス層というのを示すだけのアノテーションです ### コントローラの編集 TeacherController.java ``` package dev.itboot.mb.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import dev.itboot.mb.model.Teacher; import dev.itboot.mb.service.TeacherService; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Controller public class TeacherController { // mapperをserviceに変更 private final TeacherService service; // mapperをserviceに変更 @GetMapping("/") public String getAllTeachers(Model model) { model.addAttribute("page", service.selectAll()); return "list"; } // 以下を追加 @GetMapping("/add") public String addTeacher(@ModelAttribute Teacher teacher) { return "form"; } @PostMapping("/process") public String process(@Validated @ModelAttribute Teacher teacher, BindingResult result) { if (result.hasErrors()) { return "form"; } service.save(teacher); return "redirect:/"; } @GetMapping("/edit/{id}") public String editTeacher(@PathVariable Long id, Model model) { model.addAttribute("teacher", service.selectByPrimaryKey(id)); return "form"; } @GetMapping("/delete/{id}") public String deleteTeacher(@PathVariable Long id) { service.deleteByPrimaryKey(id); return "redirect:/"; } } ``` > @PostMappingでPOST処理をしてくれることを示しています > @Validatedで入力値チェックをしてその結果をBindingResultに入れてくれます。なのでresult.haserror()でエラーがあるかの確認ができます > @ModelAttributeでモデル属性にデータを関連付けてくれるので変換等する手間が省けます > @PathVariableでパスの値を変数に格納してくれます > だいたい新しいのはこのくらいなのでほかのアノテーションでわからなければぐぐるとでます ### html(Thymeleaf)の編集 list.html ``` <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <th:block th:insert="fragments/base :: header"></th:block> </head> <body> <div class="container-fluid"> <h4>MyBatis APP</h4> <!-- th:ifを追加 --> <table th:if="${page.size > 0}" class="table"> <thead> <tr> <th>#</th> <th>名前</th> <th>E-Mail</th> <!-- 列を追加 --> <th></th> </tr> </thead> <tbody> <tr th:each="teacher : ${page}" th:object="${teacher}"> <td th:text="*{id}"></td> <td th:text="*{userName}"></td> <td th:text="*{email}"></td> <!-- 列を追加 --> <td class="py-1"> <a th:href="@{/edit/{id}(id=*{id})}" class="btn btn-outline-primary"> <i class="fa fa-pencil"></i> 編集 </a> <a th:href="@{/delete/{id}(id=*{id})}" class="btn btn-outline-danger"> <i class="fa fa-trash"></i> 削除 </a> </td> </tr> </tbody> </table> <!-- ボタンを追加 --> <div class="row"> <div class="col-auto mr-auto"> <a th:href="@{/add}" class="btn btn-outline-primary"> <i class="fa fa-plus"></i> 新規追加 </a> </div> </div> </div> <th:block th:insert="fragments/base :: scripts"></th:block> </body> </html> ``` > th:から始まるのはThymeleaf特有の方法です > th:ifはそのままでif文(分岐処理)の条件式を記述することができます > th:hrefでリンクURL式で表します(実際表示されるのはidとかわかりやすいのがURLの最後に/1や/2等で表示される) 次に新しいHTMLファイルを作ります いつも通りtemplatesを右クリック→新規→その他で拡張子に.htmlを付けて作成します(今回はform.html) form.html ``` <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <th:block th:insert="fragments/base :: header"></th:block> </head> <body> <div class="container-fluid"> <h4>MyBatis APP</h4> <form th:action="@{/process}" th:object="${teacher}" method="post" novalidate> <input type="hidden" th:field="*{id}"> <div class="form-group"> <label for="userName">名前:</label> <input type="text" class="form-control" th:errorclass="is-invalid" th:field="*{userName}"> <div class="invalid-feedback" th:errors="*{userName}"></div> </div> <div class="form-group"> <label for="email">E-Mail:</label> <input type="email" class="form-control" th:errorclass="is-invalid" th:field="*{email}"> <div class="invalid-feedback" th:errors="*{email}"></div> </div> <button class="btn btn-outline-primary px-5"> <i class="fa fa-file"></i> 保存 </button> </form> </div> <th:block th:insert="fragments/base :: scripts"></th:block> </body> </html> ``` >今回もたくさんth:がありますがここら辺は覚えるしかないので頑張ってください >ググればまとめてる人もいると思います >だいたいはHTMLをやってる人なら聞いたことのある単語ばかりですのでHTMLの時とほぼ意味は変わりませんth:actionとかもフォームのPOST先を指定してるだけですし・・・ ### 実行 ここまできてようやくCRUD操作の実装まで完了です いつも通り実行して[ここ](http://localhost:8080/)にアクセスします。 すると以下のような画像がでてきてまず新規追加のボタンを押します ![](https://i.imgur.com/eCOhJQK.png) ↓↓↓↓↓↓ すると以下のような画面に変わります ここでメールアドレスに@を入れないで追加しようとするとエラーで教えてくれます ![](https://i.imgur.com/zNo5DTJ.png) これで無事に追加すると自動的に最初の画面に戻り、そこの2番目に新しくデータが追加されてると思います。 編集を押すと2番目の画像と同じ画面に行きますが削除ボタンだと何も注意画面がでないで1発で消えますので注意してください ### まとめ mybatisむずかしくてながい またmybatis関係のサンプルアプリは随時下に追加していきます 前回の内容↓↓↓↓↓↓ https://hackmd.io/@ka-777/Hkdqwxvid 次の内容↓↓↓↓↓↓ https://hackmd.io/@ka-777/B1Rokojj_