Project: Gradle Project Language: Java Spring Boot: 2.5.1 (SNAPSHOTの書かれていない最新のやつ) Project Metadata: |--Group: jp.kobespiral.(自分の名前) |--Artifact: todo |--Name: todo |--Description: 猫アプリ as 初めてのSpring Boot アプリケーション. |--Package name: jp.kobespiral.hello (自動入力される) |--Packaging: war |--Java: 11 (※2021/06現在、VSCode上だと8,16は動かない!) Dependencies (Ctrlを押しながら操作すると複数同時に選べる) |--Spring Boot DevTools (開発ツール) |--Lombok (コンストラクタやsetter/getterを自動生成してくれる神ライブラリ) |--Spring Web (Webアプリを作るときは必須) |--Thymeleaf (HTML テンプレートエンジン) |--MySQL Connector (MySQLデータベースコネクタ) |--Validation (バリデーション) |--Spring Data JPA (Javaデータ永続化API)
> cd c:\TAMP\mysql-8.0.20\bin > mysqld
> cd c:\TAMP\mysql-8.0.20\bin > mysql -u root -p > Password: ****** (rootのパスワード) ... mysql> create database todoapp; mysql> create user todouser identified by 'todotodo'; mysql> grant all on todoapp.* to todouser;
# サーバポート(テスト用) server.port = 18080 # MySQLデータベース接続設定 spring.datasource.url=jdbc:mysql://localhost:3306/todoapp spring.datasource.username=todouser spring.datasource.password=todotodo # Spring-JPA: DBのテーブルを自動作成してくれる機能 # create: 新規作成, update: なければ新規作成, create-drop: 新規作成し終了時に削除 spring.jpa.hibernate.ddl-auto=update
管理者のユースケースでは,メンバーの登録(UC02),取得(UC01),削除(UC03)を行う. よって,下記の順番で実装していく
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Member { @Id String mid; //メンバーID String name; //氏名 }
@Repository public interface MemberRepository extends CrudRepository<Member, String>{ List<Member> findAll(); }
Optional<Member> findById(String mid); //null安全な1件取得 (R) Iterable<Member> findAll(); //全メンバー取得 (R) Member save(Member member); //メンバー保存・更新 (C, U) void deleteById(String mid); //メンバー削除 (D) void deleteAll(); //メンバー全権削除 (D) boolean existById(String mid); //存在チェック Long count(); //登録件数
@Data public class MemberForm { String mid; //メンバーID. String name; //名前 public Member toEntity() { Member m = new Member(mid, name); return m; } }
public class ToDoAppException extends RuntimeException{ public static final int NO_SUCH_MEMBER_EXISTS = 101; public static final int MEMBER_ALREADY_EXISTS = 102; public static final int INVALID_MEMBER_INFO = 103; public static final int INVALID_MEMBER_OPERATION = 104; public static final int NO_SUCH_TODO_EXISTS = 201; public static final int INVALID_TODO_INFO = 202; public static final int INVALID_TODO_OPERATION = 203; int code; public ToDoAppException(int code, String message) { super(message); this.code = code; } public ToDoAppException(int code, String message, Exception cause) { super(message, cause); this.code = code; } public int getCode() { return code; } }
@Service public class MemberService { @Autowired MemberRepository mRepo; /** * メンバーを作成する (C) * @param form * @return */ public Member createMember(MemberForm form) { //IDの重複チェック String mid = form.getMid(); if (mRepo.existsById(mid)) { throw new ToDoAppException(ToDoAppException.MEMBER_ALREADY_EXISTS, mid + ": Member already exists"); } Member m = form.toEntity(); return mRepo.save(m); } /** * メンバーを取得する (R) * @param mid * @return */ public Member getMember(String mid) { Member m = mRepo.findById(mid).orElseThrow( () -> new ToDoAppException(ToDoAppException.NO_SUCH_MEMBER_EXISTS, mid + ": No such member exists")); return m; } /** * 全メンバーを取得する (R) * @return */ public List<Member> getAllMembers() { return mRepo.findAll(); } /** * メンバーを削除する (D) */ public void deleteMember(String mid) { Member m = getMember(mid); mRepo.delete(m); } }
@Controller @RequestMapping("/admin") public class MemberController { @Autowired MemberService mService; /** * 管理者用・ユーザ登録ページ HTTP-GET /admin/register * @param model * @return */ @GetMapping("/register") String showUserForm(Model model) { List<Member> members = mService.getAllMembers(); model.addAttribute("members", members); MemberForm form = new MemberForm(); model.addAttribute("MemberForm", form); return "register"; } /** * 管理者用・ユーザ登録確認ページを表示 HTTP-POST /admin/check * @param form * @param model * @return */ @PostMapping("/check") String checkUserForm(@ModelAttribute(name = "MemberForm") MemberForm form, Model model) { model.addAttribute("MemberForm", form); return "check"; } /** * 管理者用・ユーザ登録処理 -> 完了ページ HTTP-POST /admin/register * @param form * @param model * @return */ @PostMapping("/register") String createUser(@ModelAttribute(name = "MemberForm") MemberForm form, Model model) { Member m = mService.createMember(form); model.addAttribute("MemberForm", m); return "registered"; } /** * 管理者用・ユーザ削除処理 HTTP-GET /admin/delete/{mid} * @param mid * @param model * @return */ @GetMapping("/delete/{mid}") String deleteUser(@PathVariable String mid, Model model) { mService.deleteMember(mid); return showUserForm(model); } }
<!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>管理者画面</title> </head> <body> <h1>管理者画面</h1> <h2>メンバー新規登録</h2> <p> メンバーIDと氏名を入力して,「確認する」を押してください.</p> <ul> <li>メンバーIDには,アルファベット小文字,数字,ハイフン,アンダーバーのみ使用できます</li> </ul> <form role="form" th:action="@{/admin/check}" th:object="${MemberForm}" method="post"> <table> <tr> <td><label>メンバーID: </label></td> <td><input type="text" required th:field="*{mid}" /></td> </tr> <tr> <td><label>氏名: </label></td> <td><input type="text" required th:field="*{name}" /></td> </tr> </table> <p><input type="submit" value="確認する" /></p> </form> <h2>登録済みメンバー</h2> <table border="1"> <tr> <th>メンバーID</th> <th>氏名</th> <th>コマンド</th> </tr> <tr th:each="m: ${members}"> <td>[[${m.mid}]]</td> <td>[[${m.name}]]</td> <td><a th:href=@{/admin/delete/{m}(m=${m.mid})}>削除</a></td> </tr> </table> </body> </html>
<!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>登録確認</title> </head> <body> <h1>登録確認</h1> <p> 以下の情報でメンバーを登録します.よろしければ登録を押してください. </p> <form role="form" th:action="@{/admin/register}" th:object="${MemberForm}" method="post"> <p> <label>メンバーID: </label> [[*{mid}]] </p> <p> <label>氏名: </label> [[*{name}]] </p> <p> <input type="button" value="戻る" onclick="history.back()" /> <input type="submit" value="登録する" /> </p> <input type="hidden" th:field="*{mid}" /> <input type="hidden" th:field="*{name}" /> </form> </body> </html>
<!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>登録完了</title> </head> <body> <h1>登録完了</h1> <p> メンバー登録を完了しました. </p> <div th:object="${MemberForm}"> <p> <label>メンバーID: </label> [[*{mid}]] </p> <p> <label>氏名: </label> [[*{name}]] </p> <p> <a th:href=@{/admin/register}>続けて登録する</a><br /> <a th:href=@{/}>初めに戻る</a> </p> </div> </body> </html>