#目的
Spring Quickstart Guideを取り組み終えた方、SpringBootを学び始めた方、復習をしたい方に向けて、
公式が人気ガイドだからやってみて!と勧めてくれている、Accessing Data with JPAを実際に取り組み学んだことを共有します。
OS: macOS Mojave バージョン10.14.6
テキストエディタ: Visual Studio Code(以下VSCode)
Java: 11.0.2
QuickstartGuideのおさらいはこちらから
Building a RESTful Web Service編のおさらいはこちらから
Consuming a RESTful Web Service編のおさらいはこちらから
#1.SpringBoot projectを始めよう!
まずは、spring initializrにアクセスします。
1.ADD DEPENDENCIESボタンをクリックして、Spring Data JPA
とH2 Database
を追加。
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ファイルをダウンロードします。
ダウンロードしたZipファイルを展開したら準備完了です。
#2.コードを追加しよう!
先ほどのフォルダをVSCodeで開きます。
拡張機能のJava Extension Packのインストールの推奨します。と言われるのでインストールしておきましょう。(未インスールの方のみ)
##Customer.javaを作成しよう!
src/main/java/com/example/accessingdatajpa/にCustomer.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
@Entity
public class Customer {
// 省略
}
@Entity
アノテーションは、DBのテーブルと結びつくクラスとなります(エンティティクラス)。
このエンティティクラスで定義された変数は、DBのカラムと結びつきます。
###②@Id
、@GeneratedValue(strategy=GenerationType.AUTO)
、コンストラクタ
@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メソッド、ゲッターメソッドの定義
@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ファイルを作成します。
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ファイルに追加したコードを深掘りしていきます。
###①リポジトリインターフェースを作成する
public interface CustomerRepository extends CrudRepository<Customer, Long> {
// 省略
}
CustomerRepositoryというインターフェースを作成しています。その時にCrudRepositoryというインターフェースを継承しています。
CrudRepositoryインターフェースは引数にエンティティの型であるCustomer、IDの型であるLongをジェネリックパラメーターとして指定しています。
CrudRepositoryインターフェースは、Create、Read、Update、Delete(CRUD)という操作が出来るインターフェースです。
よって、CustomerRepositoryインターフェースも上記操作が出来るようになります。
###②メソッドの実装
List<Customer> findByLastName(String lastName);
Customer findById(long id);
Spring Data JPA
では、findBy〇〇と特定の命名規則を満たすメソッド
はその内容からクエリメソッドとして定義されます。
今回は、findByLastName(String lastName)
が該当します。引数で受け取ったlastNameと一致するデータを全て取得する事が可能なメソッドです。
findById(long id)
は、特定のidと一致するデータを取得する事が可能なメソッドです。
##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);
}
}
公式を参考にしつつコードを追加します。
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
// 追加したコード
private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);
ターミナルでログを表示するためにLogger、LoggerFactoryを用いています。
LoggerFactory.getLogger()
の引数にクラスを指定してログを取得することが出来ます。
###②データの登録、取得
// 追加したコード
@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テーブルに登録しています。
repository.save(new Customer("Jack", "Bauer"));
次に、findAll()
で全てのデータを取得しています。
拡張for文でループを回して、取得した複数のデータを引数customerで受け取り1つずつ出力しています。
出力する際にオーバーライドしたtoString()
を使用しています。
for (Customer customer : repository.findAll()) {
log.info(customer.toString());
}
次に、findById(1L)
でIDが1のデータを取得しています。
これは特定の1つのデータしか取得できないので、ループを回さずに出力しています。
// fetch an individual customer by ID
Customer customer = repository.findById(1L);
log.info(customer.toString());
最後に、findByLastName("Bauer")
で、lastNameの値がBauer
のデータを取得しています。
このデータは複数ある可能性があるので、List型で受け取るよう定義されていましたね。
取得した複数のデータをforEach文でループを回して、引数bauerで受け取り1つずつ出力しています。
コメントアウトされている拡張for文は、こちらの書き方でも良いですよという事だと思います。
// 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してください。
$ ./mvnw spring-boot:run
しばらくすると、ターミナルに以下の文字が出てきます。
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-
エンティティークラスの要件
CrudRepository
【Spring Data JPA】自動実装されるメソッドの命名ルール