【Java】DBの値を取得する



#DBの値を取得する
 **（ゴール）画面に入力した従業員IDでDBに検索し、検索結果を画面に表示する**

* **登場人物**
    * **リポジトリークラス**
        * DBへのCRUD操作を行い、結果を返す。MVCモデルのModel
    * **サービスクラス**
        * リポジトリークラスなどを使った色々なサービスを提供。MVCモデルのModel
    * **コントローラークラス**
        * どのサービスを使うか指定して、サービスの結果を画面に返す。MVCモデルのController
    * **ドメインクラス (モデルクラス、DTO ; Data Transfer Object)**
        * リポジトリークラスやサービスクラスなどの間で渡すクラスのこと

以下作成するコード配置

```
Project Root
└─src
    └─ main
        └─ java  
            └─ com.example
                └─ demo
                    └─trySpring
                 	   └─HelloController.java
                 	   └─HelloRepository.java
                 	   └─Employee.java
                 	   └─HelloService.java
```
```
Project Root
└─src
    └─ main
        └─ resources
            └─ data.sql
            └─schema.sql
            └─templates
                 └─hello.html                 
                 └─helloResponse.html
                 └─helloResponseDB.html
```

##dependenciesに必要なライブラリを追加
* build.gragleに追加するとライブラリがクラスパスに入る
* H2のライブラリがクラスパスに入ってたらデフォルトでH2コンソールが使えるよう設定されている
* **プロジェクト作成時に選択しなかったものをここから記述しインストールできる！(JDBC、H2など)**

```build.gradle
plugins {
    id 'org.springframework.boot' version '2.3.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

test {
    useJUnitPlatform()
}
```

##H2DBに接続する設定
* **デフォルトの設定を変更したい場合、（ポート番号など）application.propertiesを変更**
* SpringBoot起動時にschema.sqlm、data.sqlで使用するデータを登録、schema.sqlとdata.sqlを実行する
* schema.sql：テーブル作成　data.sql：使用するデータ登録
* H2データベースはメモリ上で動作しSpringBootを停止するとテーブルもデータも消える
* この設定でSpringを起動するたびにschema.sqlとdata.sqlが実行され、Springを再起動させるとデータを初期化できる

```application.properties
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driver-class-name=org.h2.Driver
spring.datasouce.username=sa
spring.datasouce.password=
spring.datasource.sql-script-encoding=UTF-8
spring.h2.console.enabled=true
spring.datasource.initialize=true
spring.datasource.schema=classpath:schema.sql
spring.datasource.data=classpath:data.sql
```

##従業員テーブル

```sql:schema.sql
--従業員テーブル
CREATE TABLE IF NOT EXISTS employee(
	employee_id INT PRIMARY KEY, 
	employee_name VARCHAR(50), 
 	age INT
);  
```
```sql:data.sql
--従業員テーブルデータ
INSERT INTO employee(employee_id,employee_name,age)
VALUES(1,'山田太郎',30);
```
##リポジトリークラス
###@Repository
リポジトリークラスにも@Repositoryクラスを付けることで、DIに登録される
(コントローラークラスだと@Controller)

### JdbcTemplate

* Springが用意しているJDBC接続用のクラス（JdbcTemplate）を使って、SELECT文を実行
* JdbcTemplateを使うには@Autowiredアノテーションが必要→インスタンスを生成
* 以下ではjdbcTemplateのqueryForMapメソッドを使って検索を実行
* 検索結果はMapに入り、Mapのgetメソッドで、テーブルのフィールド名を指定すれば、値を取得できる
* Mapから取得した値をEmployeeクラスのインスタンスにセットして返却

```java
//イメージ
private JdbcTemplate jdbcTemplate = newJdbcTemplate();
```


```HelloRepository.java
package com.example.demo.trySpring;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class HelloRepository {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public Map<String, Object> findOne(int id) {

        // SELECT文
        String query = "SELECT "
                + " employee_id,"
                + " employee_name,"
                + " age "
                + "FROM employee "
                + "WHERE employee_id=?";

        // 検索実行、mapで取得した値をemployeeクラスのインスタンスにセット
        Map<String, Object> employee = jdbcTemplate.queryForMap(query, id);

        return employee;
    }
}
```
##ドメインクラス
###@Data
* Lombok（ロンボック）の機能で@Dataアノテーションを付けると、getterやsetterなどを自動で作成してくれる
* **@Dataが付与されたクラスは、コンパイル時に AnnotationProcessor によって、<br>
全てのフィールド に対して フィールドアクセスメソッドの getter / setter を自動で生成するのです！👏**

例えば

```java
private int age;
```
というフィールドには以下の2メソッドを生成

```java
// getter
public int getAge(){ 
   return this.age;
}
// setter
public void setAge(int value){ 
    this.age = value;
}
```

### **(注)ソースコード上にはgetter / setterメソッドは存在しないため Intellij は エラー表示になる**
* **Intellij でLombok プラグインをインストールすればOK!**
* Preference - Plugins, Marketplaceから "Lombok"
![エラー文.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282755/f761aad4-2815-4ad9-f2e6-b8b05c700ea0.png)


```java:Employee.java
package com.example.demo.trySpring;

import lombok.Data;

@Data
public class Employee {

    private int employeeId; //従業員ID
    private String employeeName; //従業員名
    private int age; //年齢
}

```

##サービスクラス
###@Service
*  サービスクラスには@Serviceアノテーションを付ける
* HelloRepositoryクラスを使うために、@Autowiredアノテーションを付ける

```
//イメージ
private HelloRepository helloRepository = new HelloRepository();
```


```java:HelloService.java
package com.example.demo.trySpring;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class HelloService {

    @Autowired
    private HelloRepository helloRepository;

    public Employee findOne(int id) {

        // １件検索実行
        Map<String, Object> map = helloRepository.findOne(id);

        // Mapから値を取得
        int employeeId = (Integer)map.get("employee_id");
        String employeeName = (String)map.get("employee_name");
        int age = (Integer)map.get("age");

        // Employeeクラスに値をセット
        Employee employee = new Employee();
        employee.setEmployeeId(employeeId);
        employee.setEmployeeName(employeeName);
        employee.setAge(age);

        return employee;
    }
}

```
##コントローラークラス
###画面から受け取った文字列をポストする
* `@RequestParam`で`“text2”`という名前のパラメータを受け取る
* HelloServiceクラスのfindOneメソッドに渡し検索結果としてEmployeeのインスタンスを得る
* Employeeのクラスの値を`model.attribute`で登録し、画面で受け取る


``` HelloController.java
package com.example.demo.trySpring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class HelloController {

    @Autowired
    private HelloService helloService;

    /**
     * GET用の処理.
     */
    @GetMapping("/hello")
    public String getHello() {
        // hello.htmlに画面遷移
        return "hello";
    }

    /**
     * POST用の処理.
     */
    @PostMapping("/hello")
    public String postRequest(@RequestParam("text1") String str, Model model) {

        // 画面から受け取った文字列をModelに登録
        model.addAttribute("sample", str);

        // helloResponse.htmlに画面遷移
        return "helloResponse";
    }

    /**
     * POST用の処理（DB）.
     */
    @PostMapping("/hello/db")
    public String postDbRequest(@RequestParam("text2") String str, Model model) {

        // Stringからint型に変換
        int id = Integer.parseInt(str);

        // １件検索
        Employee employee = helloService.findOne(id);

        // 検索結果をModelに登録
        model.addAttribute("id", employee.getEmployeeId());
        model.addAttribute("name", employee.getEmployeeName());
        model.addAttribute("age", employee.getAge());

        // helloResponseDB.htmlに画面遷移
        return "helloResponseDB";
    }
}
```

```html:hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF8"></meta>
    <title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
<form method="post" action="/hello">
    好きな文字を入力：
    <input type="text" name="text1" th:value="${text1_value}"/>
    <input type="submit" value="クリック"/>
</form>
<br/>
<form method="post" action="/hello/db">
    従業員IDを入力：
    <input type="text" name="text2" th:value="${text2_value}"/>
    <input type="submit" value="クリック"/>
</form>
<body>
</html>
```

```helloResponse.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>
    <title>ResponseSample</title>
</head>
<body>
<h1>HelloResponse</h1>
<!-- 受け取った文字を表示する -->
<p th:text="${sample}"></p>
<body>
</html>
```


```helloResponseDB.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>
    <title>ResponseSample</title>
</head>
<body>
<h1>HelloResponseDB</h1>
<table>
    <tr>
        <td>ID：</td><td th:text="${id}"></td>
    </tr>
    <tr>
        <td>名前：</td><td th:text="${name}"></td>
    </tr>
    <tr>
        <td>年齢：</td><td th:text="${age}"></td>
    </tr>
</table>
<body>
</html>
```


##URL：http://localhost:8080/hello で画面を確認！
* data.sqlに登録している`employee_id`を入力すると、取得した値をhttp://localhost:8080/hello/db 上に反映

![form1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282755/5715bc6d-0128-2e11-da3c-34f2b5987c56.png)

![form2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282755/ff8961e3-d3b8-cb96-8fda-fef7e1a77fa1.png)