見出し画像

【Javaお勉強日記】springbootを使ってサンプルアプリ-3・springJPAを使ってCRUD出来るアプリを作ったりthymeleaf使ったり

サラッと流したかったんですけど、書いておかないと忘れそうで

thymeleafを使って共通部品を外出しする

依存関係に追加

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

 
個別ページがわ↓

<!DOCTYPE html>
<html lang='ja' xmlns='http://www.thymeleaf.org'>
<head>
<meta charset='UTF-8'>
<th:block th:insert="base :: header"></th:block>
</head>
<body id="page-top">
   <div id = "wrapper">
       <th:block th:insert="sidebar :: sidebar "></th:block>
   </div>
</body>       

共通部品側↓

<!-- base.html -->
<!DOCTYPE html>
<html lang='ja' xmlns='http://www.thymeleaf.org'>
<head>
   <meta charset='UTF-8'>
   <th:block th:fragment="header">
       <meta name="viewport" content="width=device-width, initial-scale=1">
       <title>社員情報アプリ</title>
       <link rel='stylesheet' th:href='@{/webjars/bootstrap/4.3.1/css/bootstrap.min.css}'>
   </th:block>
</head>
<!-- 略 -->

1:共通部品にしたい箇所を

<th:block th:fragment="FRAGMENT-NO-NAMAE"></th:block>

で囲む

2:共通部品を入れたい箇所に

<th:block th:insert="PART-PAGE-NO-NAMAE :: FRAGMENT-NO-NAMAE"></th:block>

を入れておく。パーツページ側の名前は、フォルダに入ってる場合フォルダ名/ページ名(.htmlは不要)

これで別ファイルで定義した共通パーツを各ページへ入れられる。
(PHPでincludeするみたいなかんじ)

JPA+H2 Databaseでデータの出し入れを試してみる

H2 Databaseを使えるようにする

build.gradleの依存関係に追加

runtimeOnly 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

※Lombokとかvalidationとかの依存関係は入っているものとする

application.propertiesに以下を記述

spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.datasource.username=admin
spring.datasource.password=password

これで起動時にjdbc:h2:mem:testdbという設定のDBが作られる。

spring.h2.console.enabled=trueで、コンソール画面にアクセス出来る。

コンソール画面はhttp://localhost:8080/h2-consoleでアクセスできる。

データベースに突っ込むデータのモデルを作る

// Hoge.java
// import略

@Getter
@Setter
@Entity
public class Hoge {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
   
   @NotBlank
   private String name;
   
   // 色んなnameのデータが1つのtypeに紐付くよ、の意
   @ManyToOne
   private Type type; // Typeクラスのインスタンスが入る
}


// Type.java
// import略

@Getter
@Setter
@Entity
public class Type {
   @Id
   @GeneratedValue
   private Long id;
   
   @NotBlank
   @Size(max = 40)
   private String name;
   
   // 一つのtypeに複数のhogeが紐付くよ
   @OneToMany(mappedBy = "type")
   // Hogeクラスのtypeフィールドに入ってるインスタンスのidで照合する
   private List<Hoge> hoges;
}

INSERT文を書く

resourcesフォルダ内に、data.sqlファイルを作成して、

INSERT INTO type(id, name)
VALUES (1,'あれ'),(2,'これ'),(3,'それ'),(4,'どれ');

と書いておく。これでbootrun時に自動的にDBが生成されて、値が入る。

DB周り以外の部分のコードを書く

リポジトリを作る

import com.example.craddemo.model.Hoge;
import org.springframework.data.jpa.repository.JpaRepository;
public interface HogeRepository extends JpaRepository<Hoge, Long> {
}

JpaRepositoryを継承(implementsじゃない)して作ったHogeRepositoryインターフェイスを作っておく。継承するので、中身の処理はもう全部作られてるからoverrideは不要。自身はインターフェイスとして作る事に注意。

同様にTypeRepositoryも用意しておく。

コントローラを作る

// HogeController.java
//import 略
@RequiredArgsConstructor
@Controller
public class HogeController {
   private final HogeRepository hogeRepository;
   
   @GetMapping("/list")
   public String showList(Model model){
       model.addAttribute("hoges", hogeRepository.findAll());
       return "index";
   }
   
   @GetMapping("/add")
   public String addHoge(@ModelAttribute Hoge hoge){
       return "form";
   }
   
   @PostMapping("/process")
   public String process(@Validated @ModelAttribute Hoge hoge, BindingResult result) {
       if(result.hasErrors()){
           return "form";
       }
       repository.save(hoge);
       return "redirect:/list";
   }
   
   @GetMapping("/edit/{id}")
   public String editHoge(@PathVariable Long id, Model model){
       model.addAttribute("hoge", hogeRepository.findById(id));
       return "form";
   }
   
   @GetMapping("/delete/{id}")
   public String deleteHoge(@PathVariable Long id){
       hogeRepository.deleteById(id);
       return "redirect:/list";
   }
   
}

データ追加・編集用のフォームを表示するときには

(@ModelAttribute Hoge hoge)

を引数にとって、フォーム側(HTML)で正しくidを設定すれば、フォームで送った内容が自動的にhogeの中に入る。

編集用フォームを表示する時は

@GetMapping("/edit/{id}")

アノテーションを付けて、フォーム側の遷移先として{id}を付けた/edit/idを設定する。で、idを取得したら

model.addAttribute("hoges", hogeRepository.findById(id));

してやれば、hogeRepositoryを経由してidをキーにして取得してきたデータを、hogesフィールドの中身としてmodelに追加して画面へ渡せる。


で、編集を実行する時は

(@Validated @ModelAttribute Hoge hoge, BindingResult result)

を引数にとって、

hogeRepository.save(hoge)

するだけで自動的にレコードがなければ新規作成、あれば上書きされる。削除も同様にhogeRepository.deleteById(id);すれば消える。DB内でどうこうする処理は書かなくて良い。簡単!

HTMLを用意する

<!-- index.html -->
<!DOCTYPE html>
<html lang='ja' xmlns='http://www.thymeleaf.org'>
<head>
<meta charset='UTF-8'>
<th:block th:insert="base :: header"></th:block>
</head>
<body id="page-top">
   <div id = "wrapper">

 <th:block th:insert="sidebar :: sidebar "></th:block>

 <div>
     <!-- hogesフィールドの要素数が0ならここを表示 --> 
     <div th:if="${hoges.size() == 0}">
         該当データがありません
     </div>
  
    <!-- hogesフィールドの要素数が1以上ならここを表示 -->    
    <table class="table table-borderd" th:if="${employees.size() > 0}">
        <thead>
            <tr>
                <th>#</th>
                <th>NAME</th>
                <th>TYPE</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="hoge : ${hoges}" th:object = "${hoge}">
                <td th:text = "*{id}"></td>
                <td th:text = "*{name}"></td>
                <td th:text = "*{type.name}"></td>
                <!-- ↑typeにはtype型オブジェクトが入っているので.nameで名称を取得する -->
                <td>
                   <a th:href="@{/edit/{id}(id=*{id})}" class="btn">
                       <span class="text">編集</span>
                   </a>
                   <a th:href="@{/delete/{id}(id=*{id})}" class="btn">
                       <span class="text">削除</span>
                   </a>
                </td>
            </tr>
        </tbody>
    </table>

   </div>
   <th:block th:insert="base :: scripts"></th:block>
</body>
</html>
<!-- form.html -->
<!DOCTYPE html>
<html lang='ja' xmlns='http://www.thymeleaf.org'>
<head>
   <meta charset='UTF-8'>
   <th:block th:insert="base :: header"></th:block>
</head>
<body>
    <div>
        <form th:action="@{/process}" th:object="${hoge}" method="POST">
           <input type="hidden" th:field="*{id}">
           <div class="form-group">
               <label for="name">NAME</label>
               <input type="text" th:errorclass="is-invalid" th:field="*{name}">
               <div th:errors="*{name}"></div>
           </div>
           <div>
               <label for="type">TYPE</label>
               <select class="form-control" th:field="*{type}">
               <!-- typeテーブルのデータを持ってきて選択肢として表示する -->
                   <th:block th:each="type : ${@typeRepository.findAll()}">
                       <option th:value="${type.id}" th:text="${type.name}"></option>
                   </th:block>
               </select>
           </div>
           <button class="btn"><span class="text">保存</span></button>
        </form>
    </div>
</body>
</html>

これで一通り、Create、Read、Update、Deleteが出来るようになる。

こうやって見ると長々しいけど、実際やってみるとバリデーションとかマッピングとかややこしいことは全部アノテーション任せで、ちょちょいのちょいで作れる感じ。

この記事が気に入ったらサポートをしてみませんか?