SpringBoot入門ガイドやってみた【Accessing Data with JPA編】

#目的

[Spring Quickstart Guide](https://spring.io/quickstart)を取り組み終えた方、SpringBootを学び始めた方、復習をしたい方に向けて、

公式が人気ガイドだからやってみて！と勧めてくれている、[Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)を実際に取り組み学んだことを共有します。

```text:開発環境
OS： macOS Mojave バージョン10.14.6
テキストエディタ： Visual Studio Code（以下VSCode）
Java: 11.0.2
```


[QuickstartGuideのおさらいはこちらから](https://qiita.com/morioheisei/items/59384f015e9e3801c4a7)
[Building a RESTful Web Service編のおさらいはこちらから](https://qiita.com/morioheisei/items/03106a823063677fe2f7)
[Consuming a RESTful Web Service編のおさらいはこちらから](https://qiita.com/morioheisei/items/c0a7432ad0f7281730d5)

#1.SpringBoot projectを始めよう！

まずは、[spring initializr](https://start.spring.io/)にアクセスします。

1.ADD DEPENDENCIESボタンをクリックして、`Spring Data JPA`と`H2 Database`を追加。
![スクリーンショット 2020-07-06 13.52.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/581b30da-97c3-4bb9-d9c8-a6c85e99c8fc.png)
![スクリーンショット 2020-07-06 13.52.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/21b46679-7fb1-479a-9ea6-69be85c6257a.png)


**Spring Data JPA（Java Persistence API）とは、Javaのオブジェクトをデータベースに保存したり取り出したりするためのAPIです(O/Rマッピングしてくれるもの)。**

**H2 Databasetohaとは、Javaに標準で用意されているデータベース。ここで追加することによりMySQLなど別途サーバーを立てる必要がないので便利ですね。**



2.Artifact, Nameは、`accessing-data-jpa`に変更。
3.Javaを11に変更。

そして`GENERATE`ボタンをクリックしてZipファイルをダウンロードします。

![スクリーンショット 2020-07-06 13.55.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/1840625a-5f40-5f9d-d957-fd4a2a2174ce.png)


ダウンロードしたZipファイルを展開したら準備完了です。

#2.コードを追加しよう！

先ほどのフォルダをVSCodeで開きます。
拡張機能のJava Extension Packのインストールの推奨します。と言われるのでインストールしておきましょう。（未インスールの方のみ）

![スクリーンショット 2020-06-30 10.08.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/80331ebc-b6e3-af7b-181a-3c8921288b00.png)


##Customer.javaを作成しよう！

src/main/java/com/example/accessingdatajpa/にCustomer.javaファイルを作成します。


![スクリーンショット 2020-07-06 14.11.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/4b7bef09-6883-0e85-0da4-2f11bd7a1e41.png)

Customer.javaファイル内にコードを追加していきます。

```java:Customer.java
package com.example.accessingdatajpa;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Customer {

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;
  private String firstName;
  private String lastName;

  protected Customer() {}

  public Customer(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  @Override
  public String toString() {
    return String.format(
        "Customer[id=%d, firstName='%s', lastName='%s']",
        id, firstName, lastName);
  }

  public Long getId() {
    return id;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }
}
```

Customer.javaファイルに追加したコードを深掘りしていきます。

###①`@Entity`

```java:Customer.java
@Entity
public class Customer {
  // 省略
}
```

`@Entity`アノテーションは、DBのテーブルと結びつくクラスとなります（エンティティクラス）。
このエンティティクラスで定義された変数は、DBのカラムと結びつきます。

###②`@Id`、`@GeneratedValue(strategy=GenerationType.AUTO)`、コンストラクタ

```java:Customer.java
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;

protected Customer() {}

public Customer(String firstName, String lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
```

`@Id`アノテーションは、付与されている変数がテーブルの主キーになります。
また、`@GeneratedValue(strategy=GenerationType.AUTO)`アノテーションで、idが連番で自動的に生成してくれるようになります。

今回のCustomerテーブルは、主キーがid、その他のカラムはfirstNameとlastNameが存在するという事です。

エンティティクラスの要件として、引数のないコンストラクタが必要ですので、引数なしのコンストラクタを定義、
引数があるコンストラクタは、DBに保存するインスタンスを生成する時の為に定義しています。


###③toStringメソッド、ゲッターメソッドの定義

```java:Customer.java
@Override
public String toString() {
  return String.format(
     "Customer[id=%d, firstName='%s', lastName='%s']",
      id, firstName, lastName);
}

public Long getId() {
  return id;
}

public String getFirstName() {
  return firstName;
}

public String getLastName() {
  return lastName;
}
```

`toStringメソッド`は、java.lang.Objectクラスで定義されている`文字列表現を返すメソッド`です。
`@Override`アノテーションは、`Objectクラスで定義されているtoStringメソッドをこのクラスでオーバーライドしています。`と明示するためのアノテーションです。

正しくオーバーライドされていなければエラーになります。
よって、`toStrign()`のようにタイプミスをしてしまっていると、コンパイル時にエラーが出て教えてくれます。

今回は、idとfirstNameとlastNameを表示するための文字列を返すメソッドにオーバーライドしています。

String.formatというメソッドを使用する際に、第一引数に決められた書式を指定しなければいけないため、
`%s`は、文字列、`%d`は10進数整数と書式を指定しています。

後は、それぞれの変数を取得するためのゲッターメソッドを定義しています。

##CustomerRepository.javaを作成しよう！

src/main/java/com/example/accessingdatajpa/にCustomerRepository.javaファイルを作成します。

![スクリーンショット 2020-07-07 12.01.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/8e8c8cb7-cddc-025b-57b3-be8c7448e0f1.png)

CustomerRepository.javaファイル内にコードを追加していきます。

```java:CustomerRepository.java
package com.example.accessingdatajpa;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

  List<Customer> findByLastName(String lastName);

  Customer findById(long id);

}
```

CustomerRepository.javaファイルに追加したコードを深掘りしていきます。

###①リポジトリインターフェースを作成する

```java:CustomerRepository.java
public interface CustomerRepository extends CrudRepository<Customer, Long> {
  // 省略
}
```

CustomerRepositoryというインターフェースを作成しています。その時にCrudRepositoryというインターフェースを継承しています。
`CrudRepositoryインターフェースは引数にエンティティの型であるCustomer、IDの型であるLongをジェネリックパラメーターとして指定しています。`

CrudRepositoryインターフェースは、Create、Read、Update、Delete（CRUD）という操作が出来るインターフェースです。
よって、CustomerRepositoryインターフェースも上記操作が出来るようになります。

###②メソッドの実装

```java:CustomerRepository.java
List<Customer> findByLastName(String lastName);

Customer findById(long id);
```

`Spring Data JPA`では、findBy〇〇と`特定の命名規則を満たすメソッド`はその内容から**クエリメソッドとして定義されます。**

今回は、`findByLastName(String lastName)`が該当します。引数で受け取ったlastNameと一致するデータを全て取得する事が可能なメソッドです。
`findById(long id)`は、特定のidと一致するデータを取得する事が可能なメソッドです。

##AccessingDataJpaApplication.javaを編集しよう！

デフォルトの状態は以下のようになっていると思います。

```java:AccessingDataJpaApplication.java
package com.example.accessingdatajpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccessingDataJpaApplication {
    
    public static void main(String[] args) {
      SpringApplication.run(AccessingDataJpaApplication.class, args);
    }

}
```

公式を参考にしつつコードを追加します。

```java:AccessingDataJpaApplication.java
package com.example.accessingdatajpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// 追加したコード
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class AccessingDataJpaApplication {

  // 追加したコード
  private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(AccessingDataJpaApplication.class);
  }

  // 追加したコード
  @Bean
  public CommandLineRunner demo(CustomerRepository repository) {
    return (args) -> {
      // save a few customers
      repository.save(new Customer("Jack", "Bauer"));
      repository.save(new Customer("Chloe", "O'Brian"));
      repository.save(new Customer("Kim", "Bauer"));
      repository.save(new Customer("David", "Palmer"));
      repository.save(new Customer("Michelle", "Dessler"));

      // fetch all customers
      log.info("Customers found with findAll():");
      log.info("-------------------------------");
      for (Customer customer : repository.findAll()) {
        log.info(customer.toString());
      }
      log.info("");

      // fetch an individual customer by ID
      Customer customer = repository.findById(1L);
      log.info("Customer found with findById(1L):");
      log.info("--------------------------------");
      log.info(customer.toString());
      log.info("");

      // fetch customers by last name
      log.info("Customer found with findByLastName('Bauer'):");
      log.info("--------------------------------------------");
      repository.findByLastName("Bauer").forEach(bauer -> {
        log.info(bauer.toString());
      });
      // for (Customer bauer : repository.findByLastName("Bauer")) {
      //  log.info(bauer.toString());
      // }
      log.info("");
    };
  }

}
```

AccessingDataJpaApplication.javaファイルに追加したコードを深掘りしていきます。

###①ログ出力のためのlog

```java:AccessingDataJpaApplication.java
// 追加したコード
private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);
```

ターミナルでログを表示するためにLogger、LoggerFactoryを用いています。
`LoggerFactory.getLogger()`の引数にクラスを指定してログを取得することが出来ます。

###②データの登録、取得

```java:AccessingDataJpaApplication.java
// 追加したコード
@Bean
public CommandLineRunner demo(CustomerRepository repository) {
  return (args) -> {
    // save a few customers
    repository.save(new Customer("Jack", "Bauer"));
    repository.save(new Customer("Chloe", "O'Brian"));
    repository.save(new Customer("Kim", "Bauer"));
    repository.save(new Customer("David", "Palmer"));
    repository.save(new Customer("Michelle", "Dessler"));

    // fetch all customers
    log.info("Customers found with findAll():");
    log.info("-------------------------------");
    for (Customer customer : repository.findAll()) {
      log.info(customer.toString());
    }
    log.info("");

    // fetch an individual customer by ID
    Customer customer = repository.findById(1L);
    log.info("Customer found with findById(1L):");
    log.info("--------------------------------");
    log.info(customer.toString());
    log.info("");

    // fetch customers by last name
    log.info("Customer found with findByLastName('Bauer'):");
    log.info("--------------------------------------------");
    repository.findByLastName("Bauer").forEach(bauer -> {
      log.info(bauer.toString());
    });
    // for (Customer bauer : repository.findByLastName("Bauer")) {
    //  log.info(bauer.toString());
    // }
    log.info("");
  };
}
```

データの登録、取得を行なっている
`CommandLineRunner demo(CustomerRepository repository)`というメソッドは、SpringBootがアプリ実行後に呼び出してくれます。


その中でまずは、データの登録を行なっています。
Customerをインスタンス化する際にコンストラクタの引数に文字列（firstName、lastName）を渡しています。
そしてrepositoryのsaveメソッドでその生成されたJavaオブジェクトをCustomerテーブルに登録しています。


```java:
repository.save(new Customer("Jack", "Bauer"));
```


次に、``findAll()``で全てのデータを取得しています。
拡張for文でループを回して、取得した複数のデータを引数customerで受け取り１つずつ出力しています。
出力する際にオーバーライドした``toString()``を使用しています。

```java:
for (Customer customer : repository.findAll()) {
  log.info(customer.toString());
}
```

次に、``findById(1L)``でIDが１のデータを取得しています。
これは特定の１つのデータしか取得できないので、ループを回さずに出力しています。


```java:
// fetch an individual customer by ID
Customer customer = repository.findById(1L);
log.info(customer.toString());
```


最後に、``findByLastName("Bauer")``で、lastNameの値が``Bauer``のデータを取得しています。
このデータは複数ある可能性があるので、List型で受け取るよう定義されていましたね。

取得した複数のデータをforEach文でループを回して、引数bauerで受け取り１つずつ出力しています。
コメントアウトされている拡張for文は、こちらの書き方でも良いですよという事だと思います。

```java:
// fetch customers by last name
repository.findByLastName("Bauer").forEach(bauer -> {
    log.info(bauer.toString());
});
// for (Customer bauer : repository.findByLastName("Bauer")) {
  //  log.info(bauer.toString());
// }
```

#3.実行してみよう！

アプリケーション実行の準備が出来たので確認しましょう。

ターミナルで以下のコマンドを入力してEnterしてください。

```terminal:ターミナル
$ ./mvnw spring-boot:run
```

しばらくすると、ターミナルに以下の文字が出てきます。

```terminal:ターミナル
Customers found with findAll():
-------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']

Customer found with findById(1L):
--------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']

Customer found with findByLastName('Bauer'):
--------------------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']
```

#参考サイト

**[JPA関連アノテーションの基本として-その1-](https://blog1.mammb.com/entry/20090830/1251606823)**
**[エンティティークラスの要件](https://docs.oracle.com/cd/E19798-01/821-1841/bnbqb/index.html)**
**[CrudRepository](https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html)**
**[【Spring Data JPA】自動実装されるメソッドの命名ルール](https://qiita.com/shindo_ryo/items/af7d12be264c2cc4b252)**