SpringBoot入門その2Spring Data JPA

(注、この記事は書き途中です。後日更新します。)
SpringBoot入門その1を書いてから、1年以上経ってしまった(大汗)ということで、今回は1年ぶりのSpringBoot入門その2です。
今回は、Spring Data JPAによるデータベースへのアクセスとSpring Securityによる認証・認可について簡単に紹介したいと思います。
# Spring Data JPAによるデータベースへのアクセス
1. そもそもJPAとは何か
一言でいうと、Javaでの標準のO/Rマッパーです。O/Rマッパーは、ObjectとRelationを対応づける、すなわちオブジェクト指向のクラスとデータベースのテーブルを関連させるものと考えておけばいいでしょう。
例として、「顧客」というクラスがあるとします。メンバー変数としてはID(Integer、必須)、名前(String、必須)、性別(Byte、1:男 2:女)、年齢(Integer)を持っているとします。
この時、対応するcustomorテーブルはこんな感じになるでしょう。
| id | name | gender | age |
| —- | —- | —- | —- |
| 1 | 鈴木 | 1 | 28|
| 2 | 佐藤 | 2 | 30|
| 3 | 田中 | 1 | |
| 4 | 山田 | | 26|
Javaの顧客クラスからDBのcustomorテーブルに簡単にアクセスできるようにする仕組みが、JPAです。JPAには、このマッピングの他にもDBへのCRUD(Create,Read,Update,Delete)処理をカプセル化したAPIおよびJavaオブジェクトを間作するためのクエリ言語(JPQL)を持っています。
2. pom.xmlとapplication.propertiesの設定
まずは、pom.xmlに、下記の内容を追加して、「spring-boot-starter-data-jpa」を読み込ませます。

org.springframework.boot
spring-boot-starter-data-jpa

次に、application.propertiesに下記の設定をします。なお、ここではMySQLを使う場合を想定しています。


spring.datasource.url=jdbc:mysql://localhost:3306/database
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.database=MYSQL
logging.level.jdbc=OFF
spring.jpa.hibernate.ddl-auto=validate

3. エンティティとリポジトリの作成
次に、エンティティクラスとリポジトリインタフェースを定義します。
エンティティクラスは、いわゆるMVCの”M”にあたるもので、以下のように定義します。


@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue
private Integer id;
private String firstName;
private String lastName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = true, name = "username")
private User user;
}

リポジトリインタフェースはJavaからデータベースにアクセスするためのインタフェースで、次のようにします。


import com.example.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface CustomerRepository extends JpaRepository<Customer, Integer> {
@Query(“SELECT x FROM Customer x ORDER BY x.firstName, x.lastName”)
List findAllOrderByName();¥
}

最低限必要なのはエンティティクラスと”org.springframework.data.jpa.repository.JpaRepository”のインポートです。これに”org.springframework.data.jpa.repository.Query”を追加でインポートすることでSQL文を作ってそれを呼び出すメソッドの定義もできるようになります。
それ以外だと、Serviceクラスを作ってそこからエンティティへの操作を定義して、それをコントローラーから呼び出すという使い方をすることが多いです。
Serviceクラスの作り方、使い方については「SpringBoot入門その3」あたりで紹介しようと思います(いつになるかわからないけど)。

Javaのコレクション入門

(注.この記事に限らず、Javaの記事では基本的にJava8以降を前提にします)
0. 前置き
Javaに限らず、プログラミングではデータをまとめておくのに配列だけではどうにもうまくいかないことが多々あります。そこでデータ構造が重要になってくるのですが、そのあたりの表現方法は言語によって様々です。
C++ならvectorとかlistとか標準ライブラリのコンテナが用意されていて、(あまり経験ないけど)C#だとLINQというものを使うようです。PHPだと配列でかなりのことが出来たりします(その分使い方には慎重さを要する)。Pythonだとリスト型やタプル、辞書などを用途に応じて使い分ける感じですね。

1. コレクションフレームワークとは
さて、Java。Javaでは、コレクションフレームワークというものを使います。コレクションフレームワークではリスト(値が順番に並んだもの)、セット(値が順番になっているとは限らないが、同じ値のものが1つだけのもの)、マップ(キーごとに値が対応したもの)といったデータを表現するのに必要なデータ構造が一通り揃っています(他にも両端からしか値を出し入れできないDequeというのもあるんですが、用途が限られるので今回は割愛)。

2. リスト
まずはリスト。リストは0から始まるインデックスごとにデータが入ったものです。Listインターフェースを元に実装されていて、ArrayList(可変長配列)LinkedList(連結リスト)の2つのクラスがよく使われます。ArrayListがC++でいうvectorみたいなものです。なお、JavaにもVectorクラスはあるんですが、こちらはJava8以降では非推奨になっています。
それぞれ、次のように定義します。

List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();

どちらのクラスで定義するかは、コンストラクタを呼び出す時に決まります。ちなみに、クラスとかっこの間の<>は、ダイヤモンド演算子といって、要素の型を省略する時の記法です。
よく使う操作は以下のような感じです。ArrayListに対しての記述になっていますが、LinkedListであっても行う処理は同じです。

List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
arrayList.add("Apple");  // リストの最後に要素"Apple"を追加
arrayList.get(5);  // リストの5 + 1番目の要素を取得する
arrayList.set(3, "Orange");  // リストの3 + 1番目の要素の値を"Orange"に設定する
arrayList.remove(4); // リストの4 + 1番目の要素を削除し、後続のインデックスを1つ減らす
arrayList.size();  // リストの要素数を返す
arrayList.clear();  // リストを空にする
// arrayListの要素一覧を出力する
for (String data : arrayList) {
	System.out.println(data);
}

ただ、上で「行う処理は同じ」と書きましたが、ArrayListとLinkedListでは実装方法が違うので、それぞれ得意不得意があったりします。
getとかsetとかはそれぞれの要素がメモリ上でインデックスされているArrayListの方が速く、addとかremoveは要素間がお互いのリンク情報(C言語のポインタのようなイメージ。Javaにはポインタはないけど)で繋がっているLinkedListの方が高速だったりします。
なので、データベースとかからある程度大きなデータを持ってきて、その中の要素へのアクセスが主目的の場合はArrayListが、リストへのデータの追加や削除が頻繁に発生する場合はLinkedListが向いています。

3. セット
セットはリストと同様にデータをまとめたものですが、リストとは違ってインデックスは存在せず、また、同じ値のものは2つ以上持てないというものです。大学で数学を勉強した人は集合・位相の集合をイメージしてもらえればと思います。
こちらはSetインターフェースを元に実装されていて、HashSet、TreeSet、LinkedHashSetの3つがよく使われます。それぞれ、次のように定義します。

Set<String> hashSet = new HashSet<>();
Set<String> treeList = new TreeList<>();
Set<String> linkedHashSet = new LinkedHashSet<>();

基本的な操作は以下のような感じです。HashSetに対して書いていますが、他の2つでも同じです。getとsetがないところに気をつけてください。

Set<String> hashSet = new HashSet<>();
Set<String> treeSet = new TreeSet<>();
Set<String> linkedHashSet = new LinkedHashSet<>();
hashSet.add("Banana");  // 集合の要素に"Banana"が含まれていなければ追加する
hashSet.remove("Lemon"); // 集合に"Lemon"が含まれていたら削除する
hashSet.size();  // 集合の要素数を返す
// hashSetの要素一覧を出力する
for (String data : hashSet) {
	System.out.println(data);
}

HashSet、TreeSet、LinkedHashSetの違いですが、HashSetは処理が高速な代わりに順番が全く保証されず、TreeSetは要素がソートされた状態で保持され、LinkedHashSetは要素を追加した順番が保持されます。

treeSet.add("Banana");
treeSet.add("Orange");
treeSet.add("Apple");
for (String data : treeSet) {
	System.out.println(data);
}

だと、”Apple” -> “Banana” -> “Orange”の順に出力され、

linkedHashSet.add("Banana"); 
linkedHashSet.add("Orange");
linkedHashSet.add("Apple"); 
for (String data : linkedHashSet) {
	System.out.println(data);
}

だと、”Banana” -> “Orange” -> “Apple”の順番に出力されます。

4. マップ
マップは値だけでなく、値を指定するキーと一緒の組み合わせでデータを保持するものです。マップではキーは一意(同じものが複数存在しない)ですが、値は同じものが複数あってもいいです。
マップはMapインターフェースを元に実装されていて、HashMap、TreMap、LinkedHashMapの3つがよく使われます。それぞれ次のように定義します。

Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();

基本的な操作は以下のようになります。putメソッドを使ってキーと値の組合せをマップに追加します。

Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
hashMap.put("Tomato", 300);  // マップのキー"Tomato"の値を300に設定する
hashMap.get("Tomato");  // マップのキーが"Tomato"のものの値を返す(なければnull)
hashMap.remove("Lemon"); // マップにキーが"Lemon"のものが含まれていたら削除する
hashMap.size();  // マップの要素数を返す
// マップの要素ごとにキー:値という形で出力する
for (Map.Entry<String, Integer> data : hashSet.entrySet()) {
	System.out.println(data.getKey() + ":" + data.getValue());
}

こちらも、セットと同様HashMapは高速だけど順番が保証されない、TreeMapはキーを順番としてデータを保持、LinkedHashMapはデータを追加した順番に保持されます。

5. 終わりに
Javaで通常よく使うコレクションについて、駆け足で紹介してみました。他にもいくつかあるようですが、今回紹介したものを用途に応じて使いこなせれば、Javaでのデータの扱いで困ることはそんなにはないと思います。
自分は以前、名前とあるオブジェクトの組み合わせのデータをHashMapで定義していたのですが、途中で名前の昇順に出力する必要があることに気がついて、あわててTreeMapに書き直したことがありました。皆さんもコレクションを使うときは何を目的にしているかを考えて、最適なものを選択するようにしてください。

Spring Boot入門その1

この記事で書いたように、昨年までPHPのWebフレームワークLaravelを使っていましたが、今年の4月に入った会社ではJavaのSpring Bootを用いて開発を行っています。
WEB+DB PRESS106号でSpring Bootの記事でタスク管理サービスとQiitaのクロール&配信サービスを作るというのがあったので、とりあえずタスク管理サービスをの方をやってみました。
今回WEB+DB PRESSの記事をまねて作ったソースコードはこちらにあります。今後色々と改良していく予定です。
ちなみに、完成品(?)はこんな感じです。

1.Spring Bootとは
2003年にSpring FrameworkというJavaのWebフレームワークが作られて、それを使いやすくしたものとして2014年に作られたのがSpring Bootです。
DI(依存性の注入)の考え方を用いているのが特徴です。

2. Spring Bootで開発を行うために必要なもの

OpenJDK(フレームワーク以前にJavaで開発をやるんなら当然必要でしょう。バージョンは今時の機能を使うんなら最低でも8以上は必要かと)
Spring Tool Suite(STSと略す。EclipseベースのIDE。本だと3.9.5でやってたみたいだが、2019年4月時点だと4以降がデフォルトなので、自分は4.1.2でやった)
Lombok(ロンボックと読む。アノテーションをつけるだけでgetterとsetterを自動生成してくれる便利なライブラリです。ダウンロードしたlombok.jarをダブルクリックするだけでインストールされます。)
・データベース管理システム(Webシステムなんだから必要に決まってる。自分が普段使っているやつを使えばいいと思います。本ではH2 Databaseを使っていたが、自分は使い慣れているMySQLでやりました。)
Pleiades(プレアデスと読む。Eclipse日本語化プラグイン。英語が得意な人には必要ではないんだろうけど、自分は入れました。導入の仕方はこちらを参照(リンク先はWindowsでの説明だけど、Macでもfeaturesとpluginsフォルダにダウンロードしたものの中身を入れるという、同じやり方で大丈夫です)。)
Maven(メイヴンと読む。Javaのビルドツール。STSを公式ページからダウンロードしてインストールした場合は大丈夫(なはず)だけど、brewコマンドとかでインストールした場合はmvn -versionで入っているか確認して、なかったら公式ページから持ってくるかbrew install mavenとかでインストールしてください)

3. Spring Bootの構成の大雑把な説明
ここでは、Laravelとの比較っぽい感じでSpring Bootの構成をかいつまんで説明して行きます。なお、Spring BootではLaravelとかと違って、フォルダ構成は決まっていないので、自分で構成を考える必要があります。
Javaのソースはsrc/main/java以下に、Java以外(html, css, jsなど)はsrc/main/resources以下に置かれます。JUnitでテストを行うためのソースはsrc/test以下に置かれます。

src/main/java (Javaソースファイル)
・Application(メイン関数です。アノテーションに@SpringBootApplicationをつけます。プロジェクトを実行するとこのメイン関数が呼ばれます。Laravelのindex.phpに近いか。)
・Entity(データの定義です。@Entityアノテーションをつけます。lombok.Dataをインポートして@Dataをつければゲッターとセッターが自動的に生成されます。さらにJPA(Javaでデータベースを扱う標準技術)を使って@Table(name = “テーブル名”)をつければ、自動的にテーブルを作ってくれます。LaravelのModel(app直下のやつ)+マイグレーションといったところか。)
・Repository(Entityで定義したデータのCRUD(create, read, update, delete)の基本操作を行うためのもので、JpaRepositoryを継承したインタフェースとして定義されます。Laravelにはない機能ですね。)
・Controller(MVCのCを担うControllerで、Laravelのものと似ていますが、データに対する処理などは次に説明するServiceを呼び出す形で実装します。@Controllerアノテーションをクラスにつけます。また、Getメソッドならメソッドに@GetMapping(value=”(URL)”)を、Postメソッドならメソッドに@PostMapping(value=”(URL)”)をアノテーションにつけることでリクエストをマッピングすることができます。なので、LaravelだとController+routesに近いイメージかも)
・Service(データに対して行う処理を実装して、Controllerに渡すものです。これもLaravelにはないですが、あえて言えばModelとControllerの間をつなぐものというイメージかもしれないです。@Serviceアノテーションをつけます。実際の処理はRepositoryのCRUD操作を呼び出して実装します。)
・Form(htmlフォームに入力した値をコントローラに渡すためのクラス。バリデーションとかもここで行うことが多いようです。LaravelでいうRequest?)

src/main/resources(フロントエンドなど)
・Thymeleaf(タイムリーフと読む。植物のハーブから名付けられたようです。いわゆるテンプレートエンジン。LaravelだとBladeがデフォルトだけど、Spring Bootではこれ以外にもいくつかのテンプレートエンジンをサポートしているようです。)
・application.properties(アプリケーションの設定値を持たせるもので、Laravelだとconfig配下のファイルが近いと思います。使用するデータベースの設定なんかもここで行います。)

以上、かなり駆け足で書いたのでざっくりとした説明になってしまいましたが、その2を書くときはもう少し詳しく書ければと思います。

ここ5年ほどのJavaを簡単に振り返ってみる

下の記事で転職先が決まって久しぶりにJavaを使うことになると書いたが、ここでここ5年ほどのJavaの動向を振り返ってみようと思う。

WikipediaのページJDK公式ページによると、大体こんな感じ。
2014年3月 Java8(ラムダ式、型アノテーション、Date and Time API 、JavaFX(Swingより高機能なGUIツール)など)
2017年9月 Java9(言語レベルでのモジュール化のサポートなど)
2018年3月 Java10(ローカル変数型推論など)
2018年9月 Java11(ネストベースのアクセスコントロールなど、OracleJDKの有償化!!
2019年3月 Java12(JVM定数のAPIなど)

こうして見ると、5年前のJava8で色々な機能が追加されたのが目につく。中でも、今更ながらラムダ式の導入はかなり大きかったのではないかと。
ラムダ式の例としては、こんな感じ。

package Lambdatest;
import java.util.function.*;

public class LambdaTest {
	public static void main(String[] args) {
		double x1 = 3.0;
		double y1 = 4.0;
		BiFunction<Double, Double, Double> z = (x, y) -> Math.sqrt(x * x + y * y);
		System.out.println(z.apply(x1, y1));
	}
}

これと同じことをラムダ式を使わずにやろうとすると、無名クラスを使ったり、結構ややこしいことになるんではと思う。
そういえば、C++も言語仕様が大きく追加されたC++11からラムダ式が使えるようになったんだったな。

他にも色々な機能が増えて、ここ数年で本当に進歩したんだなあ。とはいえ、いいことばかりではなくJava11からOracleJDKが有償化されてしまった(個人で使う分には大丈夫みたいですが)のは多くの人にとっては痛そうですね。。。OpenJDKの方は無償で使うことができるのですが、サポート期間が短かったりするので、悩みどころですね。

追記
Qiitaのコメント欄で情報をいただいたので、少しだけ追記します。
Amazon CorrettoというOpenJDK互換の無償のディストリビューションがあるようです。インストール方法などはこちらを参考にして見てください。