You are on page 1of 94

JSUG勉強会2024その1

Spring for GraphQL入門、


そしてSpring Boot 3へのマイグレーション

2024/02/02
株式会社カサレアル 石井 真
自己紹介
所属
株式会社 カサレアル

名前
石井 真

プロフィール
➢ 主に、Java 系の研修コースの開発や、トレーニングの実施などをしています。

Copyright 2024 CASAREAL,Inc. All rights reserved. 2


カサレアルで実施している研修コース (一部抜粋)
• Java系 コース https://www.casareal.co.jp/ls/service/openseminar/java
コース名 トピック
Javaプログラミング入門 コース改定 2024年4月開催コースより
-オブジェクト指向プログラミングの基本をマスター- Java 21 対応
Javaプログラミング基礎
-開発現場を見据えた次のステップ-
Javaプログラミング作法
JUnitによるテスト入門 ⇒ Database Rider 対応
Spring Boot 3 ではじめるWebアプリケーション開発入門
Spring Boot 3 ではじめるRESTful Web Service開発入門 ⇒ Spring Boot 3.2 対応
Spring Securityではじめる認証・認可入門
Spring for GraphQL ではじめるGraphQLアプリケーション開発入門 2024年3月 新規コース
Java 21、Spring Boot 3.2 対応
<速習>Spring Boot 3 へのマイグレーション 2023年10月 新規コース

他にもフロントエンド系(TypeScript、React/Next.js、Vue.js/Nuxt.jsなど)や、クラウドネイティブ系(基礎、道場)など
多岐にわたる技術系研修を実施しています。
Copyright 2024 CASAREAL,Inc. All rights reserved. 3
本勉強会で解説する内容
• 本勉強会では、以下の研修コースから中心的な部分をピックアップして解説いたします。

• Spring for GraphQL ではじめるGraphQLアプリケーション開発入門


https://www.casareal.co.jp /ls/service/openseminar/java/j190
GraphQLアプリケーションの基礎をSpring Productsの1つである「Spring for GraphQL」を使って
学習するコースです。実際にプログラムを作成し動作を確認しながら学習することができます。

• <速習>Spring Boot 3 へのマイグレーション


https://www.casareal.co.jp /ls/service/openseminar/java/j180
Spring Bootで開発したアプリケーションのライブラリーのバージョンアップ方法を学習するコースです。
実際にプログラムを変更し動作を確認しながら学習することができます。

Copyright 2024 CASAREAL,Inc. All rights reserved. 4


アジェンダ

⚫ GraphQL : RESTとの違い
⚫ GraphQL入門
⚫ Spring for GraphQLを利用した開発方法
⚫ @GraphQlRepositoryによりSpring Data JPAリポジトリを
直接リソースとして公開する

⚫ Spring Boot 3へのマイグレーション

Copyright 2024 CASAREAL,Inc. All rights reserved. 5


サンプルコード、実行環境など (前半 : GraphQLパート)
◼ ソース https://gitlab.com/makoishii/spring-for-graphql
• Mavenプロジェクト形式です。

◼ ライセンス : LGPL
• 個人学習目的のサンプルコードです。自己責任のもと、ご利用ください。
◼ 実行環境
• JDK 21
• DB : MySQL 8
• こちらのREADME.md にDB環境などの準備方法を記載しています
https://gitlab.com/makoishii/spring-for-graphql/-/blob/main/README.md
Copyright 2024 CASAREAL,Inc. All rights reserved. 6
サンプルコード、実行環境など (最後 : Boot 3マイグレーションパート)
◼ ソース https://gitlab.com/makoishii/spring-boot-migration
• Mavenプロジェクト形式です。
◼ ライセンス : LGPL
• 個人学習目的のサンプルコードです。自己責任のもと、ご利用ください。
◼ 実行環境
• JDK 17 (Spring Boot 2系も動作させるため)
• DB : MySQL 8
• こちらのREADME.md にDB環境などの準備方法を記載しています
https://gitlab.com/makoishii/spring-boot-migration/-/blob/main/README.md

Copyright 2024 CASAREAL,Inc. All rights reserved. 7


1. GraphQL : RESTとの違い
1.1 RESTful Web Service
GET /api/employees/101
オーバーフェッチ問題
ここでは、idとnameだけ
がほしいのに… {“id”: 101, “name”: “カサ太郎”, “joinedDate”: “…”, “departmentId”: 10}

アンダーフェッチ問題 GET /api/departments/10


1つのリクエストで部署名
も含めて取得できると
良いのに…
{“id”:10, “name”: “営業部” }

エンドポイントが
大量に用意されると… フロントエンド側の
要望 どれ使えばいいん 要望が絶えない
対応 だっけ… 本来やらなきゃいけ
ないこと沢山あるの
フロントエンド フロントエンド に… サーバーサイド
開発者 開発者 開発者

Copyright 2024 CASAREAL,Inc. All rights reserved. 9


1.2 GraphQL
POST /graphql idとnameのみをリクエスト
{ “query”: “{ employee(id: 101) { id name }}”}
フロントエンド側で
欲しいデータを
query で指定すれば
過不足なく取得できる { “id”: 101, “name”: “カサ太郎” }
関連するデータも取得できる
オーバーフェッチ問題 POST /graphql
アンダーフェッチ問題 { “query”: “{ employee(id: 101) { id name department { id name }}}”}
の発生は無し!

{ “id”: 101, “name”: “カサ太郎”, “department”: { “id”: 10, “name”: “営業部” } }

エンドポイントのパスは
/graphql 固定で1つのみ。 サーバー側はそれぞれのオブジェクトに対す
る全件取得、主キー取得を実装すればOK。
エンドポイントは増えない
Spring for GraphQL がqueryを解釈して、
HTTPメソッドは、検索・更新 取得したオブジェクトから必要な項目や関連
フロントエンド するオブジェクトを含むJSONを組み立て、
開発者 含め、全て POST を使う
レスポンスを返してくれる サーバーサイド開発者

Copyright 2024 CASAREAL,Inc. All rights reserved. 10


2. GraphQL入門
2.0 GraphQL
• GraphQL https://graphql.org/
• GraphQLは、API用のクエリ言語で、既存のデータを使用してこれらのクエリを実行するためのランタイム。
• 必要なものをクエリで指定し、過不足なく取得できる。
• 1回のリクエストでより多くのリソースを取得できる。
• 単一のエンドポイントからデータの全機能にアクセスすることができる。
• 型を使用して、アプリが可能なことのみを要求し、明確で役立つエラーを提供できる。
• 仕様 : https://graphql.org/learn/
• 対応している実装言語 :
https://graphql.org/code/

Copyright 2024 CASAREAL,Inc. All rights reserved. 12


2.1 GraphQL はじめに
• サーバーサイド開発者はスキーマ定義を作成し、その定義に基づいて動作するようにサーバー実装を行う。
• フロントエンド開発者は、サーバーサイド開発者により作成されたスキーマ定義を確認すれば、
クエリを書くことができる。
type Query {
employees: [Employee] 1. スキーマ定義を作ったから、
employee(id: ID): Employee これを見て、クエリを
} 書いてね。
2. 了解です!
サイバーサイ
type Employee { ド開発者
フロントエンド id: ID
開発者 name: String
department: Department
}

• リレーショナルデータベースで、 type Department {


テーブル定義を確認すれば id: ID まず、スキーマ定義から
SELECT文が書けるのと同じイメージ。 name: String 説明します
}
スキーマ定義 一部サンプル
Copyright 2024 CASAREAL,Inc. All rights reserved. 13
2.2 スキーマ定義 ルートレベル
• 書式
type 型 {
フィールド :型 … 引数無しフィールド
フィールド(引数 : 型 ):型 … 引数付きフィールド
...
}

• type で定義できる型(タイプ)は、以下の3つです。
型 (タイプ) 説明
オブジェクト EmployeeやDepartmentなど オブジェクトを型として定義
Query 定義したフィールドで検索オペレーション(操作)ができる
Mutation 定義したフィールドで更新オペレーション(操作)ができる
よみ:ミューテーション

Copyright 2024 CASAREAL,Inc. All rights reserved. 14


2.3 スキーマ - オブジェクト型の定義
• オブジェクト型の定義サンプル1 : 部署を表わすオブジェクト
type Department {
id :ID
name :String
}
• スカラー型 : フィールドを持たず、単体の値を保持する型
• GraphQLでは以下のスカラー型が定義済みで、利用できます。
スカラー型 説明 Javaでのマッピング
Int 32ビット整数 int
Float 倍精度浮動小数 double
String UTF-8 文字列 String
Boolean 真偽値 true/false boolean
ID オブジェクトを一意に特定する識別子 主キーの型
Copyright 2024 CASAREAL,Inc. All rights reserved. 15
2.3 スキーマ : オブジェクト型の定義 その2
• オブジェクト型の定義サンプル その2 : 社員を表わすオブジェクト
type Employee {
id: ID
name: String
joinedDate: String
departmentId: Int
email: String
birthDay: String
department: Department … 定義したオブジェクト型
} (Department)を利用

Copyright 2024 CASAREAL,Inc. All rights reserved. 16


2.3.1 RESTのリソースとGraphQLのオブジェクト
• REST の リソースは、『フロントエンドで欲しいもの』
• /api/employees … Employeeの全ての項目
• /api/employee_idnames … Employeeのidとnameのみ
• /api/employees_departments … Departmentの項目も含むEmployeeの全項目

• GraphQLのオブジェクトは、『ドメインモデルのエンティティそのもの』
• 『フロントエンドで欲しいもの』は、queryで指定する
type Employee {
id: ID
type Department { name: String
id :ID joinedDate: String
name :String email: String
} birthDay: String
department: Department
}
Copyright 2024 CASAREAL,Inc. All rights reserved. 17
2.4 スキーマ : Query型の定義
• Query型で定義されたフィールドは、フロントエンド側からqueryオペレーション(操作)で
検索系処理を行うことができます。
• 実行すると、そのフィールドで定義された型のデータがレスポンスされます。
• サンプル
type Query {
employees: [Employee] … Employee 全件を取得
employee(id: ID): Employee … idに対応するEmployee 1件を取得
}

• [型] … その型の配列型
• [Employee] … Employeeを要素とする配列型

Copyright 2024 CASAREAL,Inc. All rights reserved. 18


2.5 GraphQLサーバーの実行デモ (query操作の実行)
• 完成済みの GraphQLサーバーを起動して、queryを実行できる状態にしましょう。
• プロジェクト : graphql-intro-complete
• com.example. GraphqlIntroCompleteApplicationクラスを実行
• スキーマ定義の確認 :
• http://localhost:8080/graphql/schema
• ソース : src/main/resorces/graphql/schema.graphqls
• オペレーションの実行 : GraphiQL (グラフィカル)
• http://localhost:8080/graphiql?path=/graphql

• フロントエンド サンプル(Javascript Fetch API利用)


• http://localhost:8080/
• htmlソース : src/main/resorces/static/index.html
• JSソース : src/main/resorces/static/js/index.js
Copyright 2024 CASAREAL,Inc. All rights reserved. 19
2.6 queryオペレーション(操作)の書式
• queryオペレーション(操作)の記述 書式
query { … 先頭の query は省略可能
フィールド名 { … Queryで定義されたフィールド
取得したいフィールドのリスト
入れ子になっているフィールド名 { … フィールドがオブジェクト型
取得したいフィールドのリスト の時
}
}
}

• フィールドの型がオブジェクト型などフィールドを持つ型の場合、
{取得したいフィールドのリスト}が必要
• フィールドの型がスカラー型の場合、{取得したいフィールドのリスト}は不要
queryオペレーション(操作) ⇒ SQLでのSELECT文に該当します
Copyright 2024 CASAREAL,Inc. All rights reserved. 20
2.7.1 query操作 1: 社員全件取得
• Query型で定義された employees: [Employee] フィールドを指定した
query操作
プロジェクト直下 /GraphQL-Script にqueryファイルがあります。
employees.query
query { … 先頭の query は省略可能
employees { … Queryで定義されたフィールド
id name joinedDate email birthDay
department { … 入れ子のフィールドを指定
id name
}
}
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 21


2.7.2 query操作 実行結果1 : employees フィールド
• employees: [Employee] フィールドを指定したquery操作 実行結果
⇒ Employeeを要素とする配列が返る
{
"data": {
"employees": [ {
{ "id": "109",
"id": "101", "name": "長谷川宏",
"name": "山田太郎", "joinedDate": "2012-04-01",
"joinedDate": "2010-04-01", "email": "hasegawa@example.com",
"email": "yamada@example.com", "birthDay": "1989-11-29",
"birthDay": "1986-05-11", "department": {
"department": { "id": "20",
"id": "10", "name": "開発部"
"name": "営業部" }
} }
}, ]
… 省略 … }
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 22


2.8.1 query操作2 : 主キーによる社員1件取得
• Query型で定義された employee(id: ID): Employee フィールドを指定した
query操作

employee.query
query { … 先頭の query は省略可能
employee(id : 101) { … Queryで定義されたフィールド
id name joinedDate email birthDay
department { … 入れ子のフィールドを指定
id, name
}
}
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 23


2.8.2 query操作実行結果2 : employee(id: ID)フィールド
• employee(id: ID): Employee フィールドを指定したquery操作 実行結果
⇒ Employeeオブジェクトが返る
{
"data": {
"employee": {
"id": "101",
"name": "山田太郎",
"joinedDate": "2010-04-01",
"email": "yamada@example.com",
"birthDay": "1986-05-11",
"department": {
"id": "10",
"name": "営業部"
}
}
}
}
Copyright 2024 CASAREAL,Inc. All rights reserved. 24
2.9 スキーマ(更新系) : Mutation型、inputオブジェクト型の定義
• スキーマ(更新系) : Mutation型、inputオブジェクト型の定義書式 :
type Mutation {
フィールド (引数 :inputオブジェクト型 ) :型 … inputオブジェクト型は
} メソッドの引数にできる

input inputオブジェクト { … inputオブジェクト型は


フィールド : 型 typeではなくinputで定義する
}

• メソッドの引数にスカラー型ではなく、オブジェクト型を使いたい場合は、
通常のtypeで定義するオブジェクト型ではなく、inputオブジェクト型にする必要があります。
• inputオブジェクト型以外に、enum型やinterface型も定義することができますが、
こちらもtypeではなく、enumやinterfaceで定義します。
※ Javaでもenumやrecordなど特殊なクラスは、classではなく、
enumやrecordを使うのと同じようなイメージ。

Copyright 2024 CASAREAL,Inc. All rights reserved. 25


2.9 スキーマ(更新系) : Mutation型, inputオブジェクト型の定義
• スキーマ(更新系) : Mutaiton型, inputオブジェクト型の定義 サンプル :
type Mutation {
updateEmployee(id: ID! employeeInput: EmployeeInput!): Employee
insertEmployee(employeeInput: EmployeeInput!): Employee
deleteEmployee(id: ID!): String
}
型! … non null(NULL許容ではない)
input EmployeeInput {
null以外の値を設定する必要があることを
name: String! 示しています。
joinedDate: String!
departmentId: Int!
email: String!
birthDay: String!
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 26


2.10 mutationオペレーション(操作)の書式
• mutationオペレーション(操作)の記述 書式
mutation {
フィールド名(引数) {
戻り値のフィールドのリスト
}
}

• フィールドの型がスカラー型の場合、{戻り値のフィールドのリスト} は不要

mutationオペレーション(操作) ⇒ SQLでのINSERT/UPDATE/DELETE文など
更新系のSQL文に該当します

Copyright 2024 CASAREAL,Inc. All rights reserved. 27


2.11.1 GraphQLサーバーの実行デモ (mutation操作の実行1)
• insertEmployee(employeeInput: EmployeeInput!): Employee
• 社員の登録をmutation操作で実行
insertEmployee.mutation
mutation {
insertEmployee(employeeInput: { … 引数
name: "和田三郎"
joinedDate: "2022-01-01"
departmentId: 10
email: "wada@example.com"
birthDay: "1989-12-22"
}) { … 戻り値の受け取りたい
id name joinedDate email birthDay フィールドリスト
department { id name }
}
}
Copyright 2024 CASAREAL,Inc. All rights reserved. 28
2.11.2 mutation操作 結果1 : insertEmployee (…)フィールド
• insertEmployee(…): Employee フィールドを指定したmutation操作
実行結果 ⇒ Employeeオブジェクトが返る
{
"data": {
"insertEmployee": {
"id": "110",
"name": "和田三郎",
"joinedDate": "2022-01-01",
"email": "wada@example.com",
"birthDay": "1989-12-22",
"department": {
"id": "10",
"name": "営業部"
}
}
}
}
Copyright 2024 CASAREAL,Inc. All rights reserved. 29
2.11.3 GraphQLサーバーの実行デモ (mutation操作の実行2)
• updateEmployee(id: ID! employeeInput: EmployeeInput!): Employee
• 社員の更新をmutation操作で実行
updateEmployee.mutation
mutation {
updateEmployee(id: 110 employeeInput: { … 引数
name: "斎藤四郎"
joinedDate: "2012-01-01"
departmentId: 20
email: "saito@example.com"
birthDay: "1981-04-02"
}) { … 戻り値の受け取りたい
id name joinedDate email birthDay フィールドリスト
department { id name }
}
}
Copyright 2024 CASAREAL,Inc. All rights reserved. 30
2.11.4 mutation操作 結果2 : updateEmployee (…)フィールド
• updateEmployee(…): Employee フィールドを指定したmutation操作
実行結果 ⇒ Employeeオブジェクトが返る
{
"data": {
"updateEmployee": {
"id": "110",
"name": "斎藤四郎",
"joinedDate": "2012-01-01",
"email": "saito@example.com",
"birthDay": "1981-04-02",
"department": {
"id": "20",
"name": "開発部"
}
}
}
}
Copyright 2024 CASAREAL,Inc. All rights reserved. 31
2.11.5 GraphQLサーバーの実行デモ (mutation操作の実行3)
• deleteEmployee(id: ID!): String
• 社員の削除をmutation操作で実行

deleteEmployee.mutation
mutation {
deleteEmployee(id: 110) … 引数
}
• deleteEmployee はスカラー型(String)なので、
{戻り値のフィールドのリスト} は不要です。

Copyright 2024 CASAREAL,Inc. All rights reserved. 32


2.11.4 mutation操作 結果2 : updateEmployee (…)フィールド
• updateEmployee(…): Employee フィールドを指定したmutation操作
実行結果 ⇒ Employeeオブジェクトが返る
{
"data": {
"updateEmployee": {
"id": "110",
"name": "斎藤四郎",
"joinedDate": "2012-01-01",
"email": "saito@example.com",
"birthDay": "1981-04-02",
"department": {
"id": "20",
"name": "開発部"
}
}
}
}
Copyright 2024 CASAREAL,Inc. All rights reserved. 33
3. Spring for GraphQLを利用した開発方法
3.0.1 Spring for GraphQL
• Spring for GraphQL https://spring.pleiades.io/projects/spring-graphql
• Spring FrameworkでWebアプリケーションを作成するときと同じようなプログラミングで
GraphQLアプリケーションを作成できます。

• JavaアプリケーションでGraphQLアプリケーションを実行するにはGraphQL Javaというライブラリを利用し
ます。
• Spring for GraphQLはSpring FrameworkでGraphQLアプリケーションの作成をサポートする
Springプロジェクトの一つです。フレームワーク内部ではGraphQL Javaが使われます。
• Spring for GraphQLはGraphQL JavaとSpring Frameworkの開発エンジニアによる共同作業で
開発が進められています。

• 変遷
• もともとはGraphQL Javaの開発エンジニアがGraphQL Java Springというプロジェクトを作成していましたが、
その後継としてSpring for GraphQLが誕生しました。
• リファレンスドキュメント https://spring.pleiades.io/spring-graphql/reference/
• API ドキュメント https://spring.pleiades.io/spring-graphql/docs/current/api/

Copyright 2024 CASAREAL,Inc. All rights reserved. 35


3.0.2 本章で利用するプロジェクト
前章で動作させたプロジェクトのコードを解説します。
• プロジェクト : graphql-intro-complete
• com.example. GraphqlIntroCompleteApplicationクラスを実行
• スキーマ定義の確認 :
• http://localhost:8080/graphql/schema
• オペレーションの実行 : GraphiQL (グラフィカル)
• http://localhost:8080/graphiql?path=/graphql

Copyright 2024 CASAREAL,Inc. All rights reserved. 36


3. Spring for GraphQLを利用した開発方法
3.1 参照系機能の実装
3.1.1 検索機能の実装
まずは、検索機能(主キー検索、全件取得)の実装から解説します。

POST /graphql
{ “query”: “{ employee(id: 101) { id name department { id name }}}”}

主キー検索
{ “id”: 101, “name”: “カサ太郎”, “department”: { “id”: 10, “name”: “営業部” } }

POST /graphql
{ “query”: “{ employees { id name department { id name }}}”}
全件取得
{[
{ “id”: 101, “name”: “カサ太郎”, “department”: { “id”: 10, “name”: “営業部” } }

]}

Copyright 2024 CASAREAL,Inc. All rights reserved. 38


3.1.2 エンティティクラス、永続化層インターフェース
エンティティ
@Entity ← JPA固有 @Entity ← JPA固有
public class Employee { アノテーション public class Department { アノテーション
@Id @Id
@GeneratedValue(省略) ← JPA固有 @GeneratedValue(省略) ← JPA固有
private Integer id; アノテーション private Integer id; アノテーション
private String name; private String name;
private LocalDate joinedDate; // getter/setter/equals/hashCodeなど
private Integer departmentId;
private String email;
永続化層はSpring Data JPAで実装しています。
private LocalDate birthDay;
// getter/setter/equals/hashCodeなど MyBatis SpringやJDBCなどでも構いません。
その場合は、置き換えてください。

リポジトリ インターフェース

public interface EmployeeRepository public interface DepartmentRepository


extends JpaRepository<Employee, Integer> { extends JpaRepository<Department, Integer> {
} }

Copyright 2024 CASAREAL,Inc. All rights reserved. 39


3.1.3 エンティティクラス、永続化層インターフェース
エンティティ
@Entity • このサンプルの実装は、JPAの関連エンティティは使わず、
public class Employee { それぞれ独立したエンティティで実装しています。
@Id
@GeneratedValue(省略) • GraphQLスキーマで定義した オブジェクト型の関連は、
private Integer id; GraphQLの機能で実現します。
private String name;
private LocalDate joinedDate;
private Integer departmentId; • EmployeeクラスのdepartmentId(関連するエンティティ
private String email; のId)は、GraphQLで関連を実現するうえで必要です。
private LocalDate birthDay; • スキーマ Employeeオブジェクト型の定義では、
// getter/setter/equals/hashCodeなど departmentIdはなくても問題ありません。

Copyright 2024 CASAREAL,Inc. All rights reserved. 40


3.1.4 ビジネスロジック層
サービス実装クラス
@Service @Service
@Transactional(readOnly = true) @Transactional(readOnly = true)
public class EmployeeServiceImpl public class DepartmentServiceImpl
implements EmployeeService {
implements DepartmentService {
// 省略
// 省略
@Override
public List<Employee> findAll() { @Override
return employeeRepository.findAll(); public Department findById(Integer id) {
} return departmentRepository.findById(id)
.orElseThrow(
@Override () -> new RuntimeException());
public Employee findById(Integer id) { }
return employeeRepository.findById(id)
.orElseThrow( 青字の処理
() -> new RuntimeException()); RepositoryのfindById 戻り値 Optional<エンティティ>の場合
}
戻り値 エンティティ型の場合は、不要

Copyright 2024 CASAREAL,Inc. All rights reserved. 41


3.1.5 サーバー内動作 : query操作 employee(ID=xx):Employee
employee(ID : 101) { Spring for Employee Department
id name … GraphQL Controller Controller
department { … }
@QueryMapping
employee(Integer id) ①:
①、②の戻り値から ① 関連するDepartmentを含まない
レスポンスを組み立てる
Employee Employee単体を返せばよい
{ “id”: 101, … @SchemaMapping
“department”: { department(Employee employee)
“id”: 10, …} ② ②:
} Department Department単体を返す

スキーマ定義
query操作の取得したいフィールドで
type Query { type Employee {
departmentが指定された場合は②が employees: [Employee] id: ID
呼び出される employee(id: ID): Employee …
指定されない場合は②は呼び出されない } department: Department
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 42


3.1.6 @QueryMapping スキーマ定義とコントローラーメソッドの対応
スキーマ定義 コントローラーメソッド
Query型
type Query { 基本形:Queryのフィールド名とメソッド名を合わせる
employees: [Employee] @QueryMapping
employee(id: ID): Employee Employee employee(@Argument Integer id) {
}
メソッド名を変えたい場合
@QueryMapping("employee")
Employee findById(@Argument Integer id) {

Copyright 2024 CASAREAL,Inc. All rights reserved. 43


3.1.7 @SchemaMapping スキーマ定義とコントローラーメソッドの対応
スキーマ定義 コントローラーメソッド
オブジェクト型
type Employee { 基本形:オブジェクトのフィールド名とメソッド名を合わせる
id: ID @SchemaMapping
… Department department(Employee employee) {
department: Department
} メソッド名を変えたい場合
@SchemaMapping(field="department")
Department findById(Employee employee) {

Copyright 2024 CASAREAL,Inc. All rights reserved. 44


3.1.8 プレゼンテーション層: query操作 employee(ID=xx):Employee
コントローラクラス
@Controller
public class EmployeeController {
// 省略 Query定義の引数付きフィールド
@QueryMapping
employee(id: ID): Employee
public Employee employee(@Argument Integer id) {
に対応したメソッドの引数には、@Argumentを付加する
return employeeService.findById(id);
}

@Controller
public class DepartmentController {
// 省略
@SchemaMapping
public Department department(Employee employee) {
logger.info("departmentメソッドが呼び出された");
logger.info(employee.toString());
return departmentService.findById(employee.getDepartmentId());
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 45


3.1.9 サーバー内動作 : query操作 employees:[Employee]
employees { Spring for Employee Department
id name … GraphQL Controller Controller
department { … }
@QueryMapping
{[ employees()
{ “id”: 101, … ①
“department”: { List<Employee>
“id”: 10, …} @SchemaMapping
} department(Employee employee)
省略 ②
]} Department

スキーマ定義
query操作の取得したいフィールドで
type Query { type Employee {
departmentが指定された場合、 employees: [Employee] id: ID
②は、①の戻り値リストの要素数分、 employee(id: ID): Employee …
繰り返し呼び出される } department: Department
指定されない場合、②は呼び出されない }

Copyright 2024 CASAREAL,Inc. All rights reserved. 46


3.1.10 プレゼンテーション層: query操作 employees:[Employee]
コントローラクラス
@Controller
public class EmployeeController {
// 省略
@QueryMapping
public List<Employee> employees() {
return employeeService.findAll();
}

カサレアル研修:「Spring for GraphQLではじめるGraphQLアプリケーション開発入門」


https://www.casareal.co.jp/ls/service/openseminar/java/j190-online
では、フロントエンド側からのページネーション、ソート指定に対応した実装の解説も
含まれていますが、本勉強会では、割愛します。
ご了承くださいませ。

Copyright 2024 CASAREAL,Inc. All rights reserved. 47


3.1.11 <参考> N+1問題
• query操作 employees:[Employee] の様に、関連するオブジェクトがあり
複数件が返される場合、GraphQLでも、N+1問題が発生する
• employee 9件、department3件の場合、以下のコントローラーメソッドが呼び出される
• List<Employees> employees():1回
• Department department(Employee employee) : 9回
• 発行されるSELECT文は以下の通り。
• MyBatis、JDBCなど。JPA以外 ⇒ 計10回
• SELECT … FROM employee : 1回、
• SELECT … FROM department WHERE id=? : 9回
• Spring Data JPA ⇒ 計4回
• SELECT … FROM employee : 1回
• SELECT … FROM department WHERE id=? : 3回
• EntityManagerの管理下にあるエンティティについてはSELECTは発行されないため
※ Web環境:spring.jpa.open-in-viewプロパティのデフォルト設定により、EntityManagerの生存期間はリクエストスレッド単位

• いずれにせよ、沢山のSELECTが発行されてしまうので、N+1対応が必要。

Copyright 2024 CASAREAL,Inc. All rights reserved. 48


3.1.12 <参考> N+1問題 GraphQLでの解決方法
• GraphQL には DataLoader という仕組みが備わっています。
• DataLoaderを使うと、N+1問題を解決することができます。
• DataLoaderの目的:
• リレーショナルデータベース、キーバリューストア、外部APIなど様々なデータストアに対して
関連するデータを取得する処理のプログラミングを統一できる
• プログラミングを統一したうえで、バッチ処理やキャッシュを介して効率的に取得できる

Copyright 2024 CASAREAL,Inc. All rights reserved. 49


3.1.13 <参考> N+1問題 の解決: 永続化層 / GraphQL 比較
• 永続化層での N+1問題は、JOINを使ったSELECT文で解決する
1. SELECT … FROM employee e
JOIN department d ON e.department_id = d.id;
• 発行されるSELECT文 : N+1回 ⇒ 1回

• DataLoaderを利用する場合、
永続化層(Repository)に以下の検索を行うメソッドを用意しておくと実現できます。
1. EmployeeRepository :
List<Employee> findAll()
• SELECT … FROM employee;
2. DepartmentRepository :
List<Department> findByIds(Set<Departmentの主キー型> ids)
• SELECT … FROM department WHERE id IN (id1, id2, …);
※ Repositoryのメソッド名は一例です。
• 発行されるSELECT文 : N+1回 ⇒ 2回

Copyright 2024 CASAREAL,Inc. All rights reserved. 50


3.1.14 <参考> N+1問題 の解決: 永続化層 / GraphQL 比較
永続化層の解決 GraphQL DataLoaderでの解決
A A
id PK id PK

B C D B C D
a_id FK a_id FK a_id FK a_id FK a_id FK a_id FK

• リレーションシップごとに、結合で対応 • 参照される側のリポジトリに
List<エンティティ> findByIds(Set ids)
• テーブル間の関連が多ければ、その分の
JOINを伴うSELECT文が必要 があれば、良い

• 3テーブル以上の結合があればさらにバリエーションが
増える
⇒ リポジトリに大量のメソッドが必要になる
結合とは(1対1の関連を除き)、非正規化した冗長なデータを作ること
⇒ 末端のサーバー間(DB~AP)で冗長なデータの受け渡しが行われる
Copyright 2024 CASAREAL,Inc. All rights reserved. 51
3.1.15 <参考> N+1問題 の解決: employees:[Employee]
Spring for Employee Department Department
GraphQL Controller Controller Service
employees
@QueryMapping
employees() DataLoader
{[ ① @BatchMappingを使う
{ “id”: 101, … List<Employee>
“department”: {
“id”: 10, …} @BatchMapping
} department(List<Employee> employees)
省略 ② findByIds(Set<Integer> ids)
]} List<Department>
Map<Employee, Department>

スキーマ定義
②が呼び出されるのは1回のみ type Query { type Employee {
employees: [Employee] id: ID
こちら<参考>のサンプルコードは employee(id: ID): Employee …
graphql-data-loaderプロジェクトに } department: Department
あります。 }

Copyright 2024 CASAREAL,Inc. All rights reserved. 52


3.1.16 <参考> N+1問題 の解決:プレゼンテーション層
コントローラクラス (DepartmentContlloer)

@BatchMapping
public Map<Employee, Department> department( // 「key:Employee、value:Department」のMap生成
List<Employee> employees) { Map<Employee, Department> map = new HashMap<>();
// 社員のListから検索対象の部署IDのSetを生成する。 for (Employee employee : employees) {
Set<Integer> targetDepartmentIds = new HashSet<>(); // employeeテーブルのdepartment_id列はNULLを許容して
for (Employee employee : employees) { // いるので注意。
targetDepartmentIds.add(employee.getDepartmentId()); if(employee.getDepartmentId() != null){
} map.put(employee,
// 部署を検索する。 deptMap.get(employee.getDepartmentId()));
List<Department> departments = }
departmentService.findByIds(targetDepartmentIds); // department_id列はNULLの場合、mapにそのEmployeeは
// 「key:Department.id、value:Department」のMap生成 // 入らないが問題ない。呼び出し元ではList<Employee>を
Map<Integer, Department> deptMap = new HashMap<>(); // 回して,結果を作成する
for (Department department : departments) { }
deptMap.put(department.getId(), department); return map;
} }

Copyright 2024 CASAREAL,Inc. All rights reserved. 53


3.11.17 <参考> N+1問題 の解決: graphql-data-loaderプロジェクト
• graphql-data-loaderプロジェクトは、Spring for GraphQL及び、Spring Dataの機能
を使って、ページネーションを実装しています。
public class EmployeeController { public class EmployeeServiceImpl implements EmployeeService {
// 省略 // 省略
@QueryMapping @Override
public Window<Employee> employees( public Window<Employee> findAll(
ScrollSubrange subrange) { ScrollPosition position, OptionalInt count) {
// 省略 // 省略
return employeeService.findAll(position, count); return employeeRepository.findAllBy(position, limit);

本勉強会では、ページネーションの 以下のように読み替えてください。
解説は割愛しています
public class EmployeeController { public class EmployeeServiceImpl implements EmployeeService {
// 省略 // 省略
@QueryMapping @Override
public List<Employee> employees(){ public List<Employee> findAll() {
return employeeService.findAll(); return employeeRepository.findAll();

Copyright 2024 CASAREAL,Inc. All rights reserved. 54


3.1.18 <参考の参考> ネストしたN+1問題の解決
コントローラクラス
public class OfficeController {
// 省略
@BatchMapping
public Map<Department, Ofiice> office(List<Department> departments) {

基本的に考え方は同じ。
ネスト元のオブジェクト型(Department)から、ネスト先のオブジェクト型(Office)を一括取得する
@BatchMappingを付けたメソッドを作ればよい。

スキーマ定義
type Employee { type Department { type Office {
id: ID id: ID id: ID
… name: String name: String
department: Department office: Office }
} }

Copyright 2024 CASAREAL,Inc. All rights reserved. 55


3. Spring for GraphQLを利用した開発方法
3.2 更新系機能の実装
3.2.1 更新系機能の実装
更新系機能の実装の解説します。
• スキーマ(更新系) : Mutation型、inputオブジェクト型の定義 :
type Mutation {
updateEmployee(id: ID! employeeInput: EmployeeInput!): Employee
insertEmployee(employeeInput: EmployeeInput!): Employee
deleteEmployee(id: ID!): String
}

input EmployeeInput {
name: String!
joinedDate: String!
departmentId: Int!
email: String!
birthDay: String!
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 57


3.2.2 ビジネスロジック層 更新系メソッド
サービスインターフェース
public interface EmployeeService {
// 検索系メソッド省略

Employee update(Employee employee);

Employee insert(Employee employee);

void delete(Integer id);

Copyright 2024 CASAREAL,Inc. All rights reserved. 58


3.2.3 サーバー内動作 : mutation操作
updateEmployee(id: ID! employeeInput: EmployeeInput!): Employee
Spring for Employee Department
GraphQL Controller Controller
updateEmployee(…)
@MutationMapping
updateEmployee(…)

{ “id”: 101, … Employee
“department”: { @SchemaMapping
“id”: 10, …} department(Employee employee)
}

Department
スキーマ定義
type Mutation { input EmployeeInput {
updateEmployee(id: ID! employeeInput: EmployeeInput!): Employee name: String!
… joinedDate: String!
} departmentId: Int!
type Employee { email: String!
… birthDay: String!
departmentId: Int }
department: Department

Copyright 2024 CASAREAL,Inc. All rights reserved. 59


3.2.4 inputオブジェクト型に対応したクラス
inputクラス

public class EmployeeInput { @NotBlank


@Email
@NotBlank @Length(max = 256)
@Length(max = 10) private String email;
private String name;
@NotNull
@NotNull @PastOrPresent
@PastOrPresent private LocalDate birthDay;
private LocalDate joinedDate;
// getter/setter/equals/hashCodeなど
@NotNull
private Integer departmentId;

Bean Validationのアノテーションで
入力検証ルールを設定できる

Copyright 2024 CASAREAL,Inc. All rights reserved. 60


3.2.5 プレゼンテーション層: mutation操作
updateEmployee(id: ID! employeeInput: EmployeeInput!): Employee
コントローラクラス
@Controller
public class EmployeeController { @Validatedを付けると、このメソッドの呼び出し直前で
// 省略 Bean Validationの入力検証を行われる(RESTと同じ)
@MutationMapping
public Employee updateEmployee(@Argument Integer id, @Argument @Validated EmployeeInput employeeInput) {
// クライアントから送信されてきたパラメータを元に新しいエンティティを生成する
Employee employee = new Employee();
// Spring FrameworkのBeanUtilsを使うことで
// Javaインスタンスがフィールドに保持している値を同名のフィールドにコピーすることができる。
// クライアントから送信されてきたパラメータ(employeeInput)をエンティティ(employee)にコピー
BeanUtils.copyProperties(employeeInput, employee);
// 更新対象の主キーはidパラメータとして送信されてくるので別途設定する
employee.setId(id);
// 更新処理を呼び出す
employeeService.update(employee);
// クライアントには更新後のデータをレスポンスする
return employee;
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 61


3.2.6 入力検証 制約違反の対応
• Bean Validationの検証で制約違反があると、以下の例外が発生します。
• jakarta.validation.ConstraintViolationException
• RESTの場合と同様、@Validatedが付いたコントローラメソッドを呼び出す前に入力検証をおこない、
制約違反があると、この例外がフレームワーク内でスローされます。
• GraphQLでは、システムエラーをクライアント側に返すための専用クラス(GraphQLError)があります。
• @GraphQlExceptionHandlerを付けたメソッドでGraphQLErrorを返すと、クライアント側にシステ
ムエラーがレスポンスされます。
com.example.exception.GlobalExceptionHandlerクラス
@ControllerAdvice
public class GlobalExceptionHandler {

@GraphQlExceptionHandler
public List<GraphQLError> handleInvalidationError(ConstraintViolationException exception) {
List<GraphQLError> errors = new ArrayList<>();
省略
return errors;
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 62


4. @GraphQlRepositoryにより
Spring Data JPAリポジトリを直接リソースと
して公開する
4.0 本章で利用するプロジェクト
以下のプロジェクトのコードを解説します。
• プロジェクト : graphql-repository-sample
• com.example.GraphqlRepositoryApplicationクラスを実行

Copyright 2024 CASAREAL,Inc. All rights reserved. 64


4.1 @GraphQlRepository
• Spring Data RESTライブラリを依存関係に追加すると、Spring DataのRepositoryのみ
で(コントローラー、ビジネスロジック層なしで)、REST APIを公開できます。
※ リソースのPOST/PUT/PATCH/DELETEにも対応しています。
• 参照系や単一RepositoryのCRUD ⇒ おすすめです。例 商品検索、残高照会 APIなど
• 複数のRepositoryを利用したトランザクション処理 ⇒ コントローラ,ビジネスロジック層を用意
• 例 購入(注文+在庫引き当て+…)、定期預金作成 APIなど
• DBによりストアドプロシージャーを使えば、Repositoryのみでも可能ですがおすすめしません。
データベース製品ごとにプロシージャーは異なるため。(PL/SQL,PL/pgSQL,…)

• 同じように、Spring Data JPAのRepositoryインターフェースに、@GraphQlRepository


を付加すると(コントローラー、ビジネスロジック層なしで)、GraphQLのリソースを直接公開する
ことができます。
• Spring Data RESTの場合、Repositoryインターフェースの全メソッドを実行可能ですが、
@GraphQlRepositoryによる公開の場合、機能が制限され、単一値クエリ、複数値クエリ、
ページ分割されたクエリが実行できます。ミューテーションは実行できません。

Copyright 2024 CASAREAL,Inc. All rights reserved. 65


4.2 <参考> 同様のローコードでWebサービス を構築する技術
プロダクト Oracle REST Data Services PostgREST PostGraphile
URL https://www.oracle.com/jp https://postgrest.org/en/st https://www.graphile.org/
/database/technologies/ap able/ postgraphile/
pdev/rest.html
公開I/F REST API REST API GraphQL
機能 DB上のテーブルを、REST APIやGraphQLでアクセスできる
トランザクション エンドポイントに対し、DBス エンドポイントに対し、独自 カスタムmutation
クリプトをPL/SQLで記述 SQLスクリプトで記述 PL/pgSQLで記述した関数
を呼び出す
依存性 Oracle DBに依存 PostgreSQLに依存 PostgreSQLに依存
• これらも、複数テーブルのトランザクション処理は、それぞれ独自で依存度が高くなるため、
おすすめしません。
• 単一テーブルのCRUD、検索系での利用がおすすめです。
• サーバーレスでは、AWS AppSync (https://aws.amazon.com/jp/appsync/)や
AWS Amplify (https://aws.amazon.com/jp/amplify/)も
GraphQLサーバーをローコードで構築できます。(AWSに依存)

Copyright 2024 CASAREAL,Inc. All rights reserved. 66


4.3 エンティティクラス、永続化層インターフェース
・ 用意する必要があるJavaソースは、以下のみです。
エンティティ リポジトリ インターフェース
@Entity @GraphQlRepository
public class Employee { public interface EmployeeRepository
@Id extends JpaRepository<Employee, Integer> {
@GeneratedValue(省略) }
private Integer id;
private String name; JpaRepositoryはQueryByExampleExecutorを継承して
private LocalDate joinedDate; いるので、以下のQueryが実行可能です。
private String email; ・主キー検索など単一行の取得
private LocalDate birthDay; ・全件検索やエンティティのプロパティの値による
複数行の取得
// getter/setter/equals/hashCodeなど
・ページネーションによる複数行の取得

永続化層はSpring Data JPAを使う必要があります。


MyBatis SpringやJDBCなどでは、@GraphQlRepositoryによるリポジトリの自動公開はできません。
Copyright 2024 CASAREAL,Inc. All rights reserved. 67
4.4 スキーマ Queryの定義とRepositoryの自動マッピング
• スキーマで定義したQuery操作は、以下の通りです。

Query型 フィールド 説明

employee(id: ID): Employee 引数で渡した値で主キー検索を行い単数行の結果が


レスポンスされる。
employees: [Employee] 全件検索を行い複数行の結果がレスポンスされる。
findEmployeeBy( 引数で渡した値で条件検索を行い複数行の結果がレ
employee: EmployeeInput): スポンスされる。
[Employee]
• @GraphQlRepositoryによってRepositoryインターフェースとGraphQLのQueryを結び付けます。
• 下記の二つが一致するQueryが自動的に結び付けられます。
• GraphQLのQueryの戻り値の型名
• Repositoryインターフェースが対象とするドメインすなわちエンティティクラスの型名
• つまり、Queryの3つのフィールドともに、EmployeeRepositoryとマッピングされます。
Copyright 2024 CASAREAL,Inc. All rights reserved. 68
4.6.1 実行確認 (query操作の実行)
• 完成済みの GraphQLサーバーを起動して、queryを実行してみましょう。
• プロジェクト : graphql-repository-sample
• com.example.GraphqlRepositoryApplicationクラスを実行

/GraphQL-Scriptにqueryファイルがあります
employee.query 実行結果
query { {
employee(id: 101) { "data": {
id "employee": {
name "id": "101",
} "name": "山田太郎"
} }
}
}
Copyright 2024 CASAREAL,Inc. All rights reserved. 69
4.6.2 実行確認 (query操作の実行)

employees.query 実行結果
query { {
employees { "data": {
id "employees": [
name {
} "id": "101",
} "name": "山田太郎"
},
省略(8件)
]
}
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 70


4.6.2 実行確認 (query操作の実行)

findEmployeeBy_1.query 実行結果
query { {
findEmployeeBy(employee: { "data": {
departmentId: 10 "findEmployeeBy" : [
}) { {
id "id": "101",
name "name": "山田太郎"
departmentId "departmentId": 10
} },
} 省略(departmentIdが10 2件)
]
}
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 71


4.6.2 実行確認 (query操作の実行)

findEmployeeBy_2.query 実行結果
query { {
findEmployeeBy(employee: { "data": {
departmentId: 10 "findEmployeeBy": [
name:"山田太郎" {
}) { "id": "101",
id "name": "山田太郎",
name "departmentId": 10
departmentId }
} ]
} }
}

Copyright 2024 CASAREAL,Inc. All rights reserved. 72


5. Spring Boot 3へのマイグレーション
5.1 Spring Boot オープンソースとしてのサポートポリシー
• Spring Bootは、原則として半年ごとに、マイナーバージョンがリリースされます。
• マイナーバージョン奇数 春5月リリース 例 2.7: 2022-5月、 3.1: 2023-5月
• マイナーバージョン偶数 秋11月リリース 例 3.0: 2022-11月、3.2: 2023-11月
• Spring Bootのオープンソースとしてのサポート期間は原則として、1年間です。
• Spring Bootを導入する場合は継続的インテグレーションを積極的に導入し、頻繁なバージョン
アップに対応できる体制を整えておくことをお勧めします。
• Spring Boot / Spring Framework 各バージョンのサポート期間は、以下の公式ページから
確認できます。
• Spring Boot リファレンス - サポート期間
• https://spring.io/projects/spring-boot#support
• Spring Framework リファレンス - サポート期間
• https://spring.io/projects/spring-framework#support

• Spring Boot 2系の最終バージョンである 2.7のOSSサポート期間は、2023-11-24


まです。

Copyright 2024 CASAREAL,Inc. All rights reserved. 74


5.2 バージョンアップの基本: マイナーバージョン単位でバージョンアップ
• 新しいマイナーバージョンがリリースされると、そのリリースノートに一つ前のマイナーバージョン
からのアップグレードとして、変更点が記載されています。
• 基本的に、マイナーバージョン単位にあげていくことをお勧めします。

• ここでは、便利なライブラリ、ツールの紹介をしながら、以下の3つのポイントについて
お話しします。
• 対象:Spring Webアプリケーション
• サンプルアプリケーションで使用しているライブラリ
• spring-boot-starter-web
• spring-boot-starter-thymeleaf
• spring-boot-starter-data-jpa
• マイナーバージョンアップ対応で良く発生する作業とその対応
• Spring Boot 2系 2.7まででの大きな変更点とその対応
• Spring Boot 2.7から3.0での大きな変更点とその対応

Copyright 2024 CASAREAL,Inc. All rights reserved. 75


5.3 マイナーバージョンアップ対応で良く発生する作業とその対応
• プロパティの変更
• Spring Bootアプリケーションの設定ファイルであるapplication.propertiesは頻繁に変更
が発生します。
• 既存のプロパティが非推奨となり、その後ある程度の猶予をおいて削除となりますので、早めの
対応が求められます。
• spring-boot-properties-migrator ライブラリ
• Mavenの依存関係に、spring-boot-properties-migratorを入れておくと、変更になった
プロパティがコンソールに出力され、とても便利です。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>

Copyright 2024 CASAREAL,Inc. All rights reserved. 76


5.3.1 spring-boot-properties-migrator
• spring-boot-properties-migrator を加えて、起動させるとコンソールに以下のような
メッセージが出力されます。
コンソール出力 (Spring Boot のバージョンを2.3 から 2.4にUPして実行)
2023-09-11 15:42:13.278 WARN 4712 --- [ main] o.s.b.c.p.m.PropertiesMigrationListener :
The use of configuration keys that have been renamed was found in the environment:

Property source 'Config resource 'class path resource [application.properties]' via location
'optional:classpath:/'':
Key: spring.resources.cache.cachecontrol.no-store
Line: 15
Replacement: spring.web.resources.cache.cachecontrol.no-store
Key: spring.resources.static-locations
Line: 19
Replacement: spring.web.resources.static-locations

Each configuration key has been temporarily mapped to its replacement for your convenience. To silence
this warning, please update your configuration to use the new keys.

Copyright 2024 CASAREAL,Inc. All rights reserved. 77


5.3.2 aprication.properteys プロパティ変更
• 出力メッセージに従いプロパティを変更して、バージョンアップ対応します。
application.propertiyes 一部抜粋
# Disable browser's cache
# spring.web.resources.cache.cachecontrol.no-store に変更
#spring.resources.cache.cachecontrol.no-store=true
spring.web.resources.cache.cachecontrol.no-store=true

# Static Contents
static.contents.storage.location=/home/casareal
# spring.web.resources.static-locations に変更
#spring.resources.static-locations=classpath:/META-INF/resources/,\
spring.web.resources.static-locations=classpath:/META-INF/resources/,\
classpath:/resources/,\
classpath:/static/,\
classpath:/public/,\
file:${static.contents.storage.location}/

Copyright 2024 CASAREAL,Inc. All rights reserved. 78


5.4 Spring Boot 2系 2.7まででの大きな変更点とその対応
• Spring Boot 2.7まででの大きな変更点は、こちらです。
• @RequestMappingのマッピングルールの変更 (Spring Boot 2.6での仕様変更)
@GetMapping("/path/**/index.html")
public String method_3() {
System.out.println("method_3が呼び出された");
return "index";
}
• ** は0個以上の任意のディレクトリ にマッチします。
• Spring Boot 2.5までは、上記のようにパスの途中で利用できました。
• Spring Boot 2.6から、**はパスの最後のみでしか使えなくなりました。

• Spring Boot 2.6以降で、パスの先頭や中間で ** を利用したい場合は


application.propertiesに以下を追加します。
application.properties
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
Copyright 2024 CASAREAL,Inc. All rights reserved. 79
5.5 Spring Boot 2.7から3.0での大きな変更点
• Spring Boot 2と Spring Boot 3 での大きな変更点として、以下の対象となるバージョ
ンが変わっています。
Spring Boot 2 Spring Boot 3
Spring Framework 5 Spring Framework 6
Java EE 8 Jakarta EE 10 (9+)
Java 8 Java 17

Copyright 2024 CASAREAL,Inc. All rights reserved. 80


5.5.0 Spring Boot Migrator
• Spring Boot Migrator というツールを使うと、Spring Boot 2.7 から 3 へのマイグレー
ションを自動化することができます。
• ここから先で説明する内容は、このツールで行うことができますが、
その前に、どんな変更点があり、手動で変更する場合、どのように行うかを説明します。

• なお、 Spring Boot Migratorの利用するには以下の条件を満たしている必要がありま


す。
• JDK 17がインストール済みでjavaコマンドが利用できる
• 移行元のアプリケーションのSpring Bootのバージョンが2.7である
• 移行元のアプリケーションがMaven形式である
• 移行元のアプリケーションがGitによるバージョン管理対象になっている
⇒ gitコマンドが使える環境、ローカルリポジトリを利用。リモートリポジトリは不要です。

Copyright 2024 CASAREAL,Inc. All rights reserved. 81


5.5.1 Java EE 8 から Jakarta EE 10(9+) に変更
• Spring Boot 3では、デフォルトでJakarta EE 10が使用されます。
(設定により、Jakarta EE 9を使うことも可能です。)
• Java EEとJakarta EEでは、パッケージ名が異なります。
Java EE 8 Jakarta EE 10
javax. jakarta.
• 基本的に、Spring Bootを利用してアプリケーションを作成する場合、Jakarta EE/Java EE
の機能は各Springのライブラリの中で利用されており、直接、Jakarta EE/Java EEで提供さ
れるクラスやインターフェースを使ってロジックを記述をする必要はありません。(注1)
• ただし、アノテーションはJakarta EE/Java EEで提供されるものを使って記述します。
アノテーションに関しては、パッケージ名の変更が必要です。
• 頻繁に利用する主要なJakarta EEのアノテーションは以下の通りです。
• Jakarta Bean Validation
• Jakarta Persistence (JPA)

注1: Spring Frameworkの機能を利用せずに、


直接Java EEの機能を利用したプログラムを記述している場合は、修正が必要になります。
Copyright 2024 CASAREAL,Inc. All rights reserved. 82
5.5.2 Javaのベースラインが Java 17に変更
• Jakarta EE 10で利用できるJavaのバージョンの最低ラインは、Java 17です。
これに伴い、Spring Boot 3で利用できるJavaは、Java 17以上になります。

• Javaバージョン 8以降、さまざまな有用な新機能が追加されています。これらの新機能
を利用していくことができます。

Copyright 2024 CASAREAL,Inc. All rights reserved. 83


5.5.3.1 その他のSpring Boot 3での主な変更点
• Webアプリケーションの変更点
• @RequestMappingに設定したパスの解釈
@GetMapping("/insertMain")
• Spring Boot 2では、 /insertMain/ でもアクセスできました。
• Spring Boot 3では、パスの末尾に/があると、404 NotFoundが返されます。
• ログフォーマットの変更
Spring Boot フォーマット 例
2系 yyyy-MM-dd HH:mm:ss.SSS 2023-09-28 11:19:40.545
3系 yyyy-MM-dd’T’HH:mm:ss.SSSXXX 2023-09-28T11:19:40.545+09:00
• 下記のプロパティで、ログの日時部分のフォーマットを指定することができます。
application.properties
logging.pattern.dateformat=yyyy-MM-dd HH:mm:ss.SSS

Copyright 2024 CASAREAL,Inc. All rights reserved. 84


5.5.3.2 その他のSpring Boot 3での主な変更点(続き)
• JPAでバインドされたパラメータのログ出力
• Spring Boot 2系ではJPAの実装にHibernate 5系が使われます。
• 一方、Spring Boot 3系ではHibernate 6系が使われます。
• アプリケーション実行時に発行されたSQLにバインドされたパラメータをログに出力する設定が
変更されています。
application.properties
# SQLパラメータ出力 - for hibernate 6.x(spring boot 3.x/spring data jpa 3.x)
logging.level.org.hibernate.orm.jdbc.bind=trace

# SQLパラメータ出力 - for hibernate 5.x(spring boot 2.x/spring data jpa 2.x)


# logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace

Copyright 2024 CASAREAL,Inc. All rights reserved. 85


5.5.4 Spring Boot Migratorによるマイグレーションの自動化
• Spring関連のプロジェクトの中にはSpring Projects Experimentalという実験的なプロジェク
トが存在します。
• その中の1つにSpring Boot Migrator(略称:SBM)というプロジェクトがあり、Mavenを利用し
たSpring Boot2.7のアプリケーションをSpring Boot 3.0に自動的にアップグレードできます。
利用するプロジェクト edu-spring-boot-migrator
• 以下から、spring-boot-upgrade.jarをダウンロードできます。
ダウンロードしたものが、プロジェクトのトップ spring-boot-migrationにあります。
• https://github.com/spring-projects-experimental/spring-boot-
migrator/releases
• その配下の edu-spring-boot-migrator フォルダ上で、以下のコマンドで、ローカルリポジトリに
登録します。
cd edu-spring-boot-migrator
git init
git add .
git commit -m "適当なコミットメッセージ"

Copyright 2024 CASAREAL,Inc. All rights reserved. 86


5.5.4.1 Spring Boot Migratorによるマイグレーションの自動化(続き)
• 一つ上の階層(spring-boot-migrationフォルダ)に戻り、 Spring Boot Migratorを起動します。
cd ..
java -jar --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-
UNNAMED spring-boot-upgrade.jar edu-spring-boot-migrator 1行

• 起動し、最後に以下のメッセージがコンソールに出力されたら、起動成功です。

Scanning edu-spring-boot-migrator
finished scan. Please open: http://localhost:8080/spring-boot-upgrade

• ブラウザから、コンソールに出力されている以下のURLでアクセスします。
• http://localhost:8080/spring-boot-upgrade

Copyright 2024 CASAREAL,Inc. All rights reserved. 87


5.5.4.2 Spring Boot Migratorによるマイグレーションの自動化(続き)
• ブラウザからアクセスできたら、「2. Relevant Changes」のサブメニューが出てくるところまで、下にスクロール
します。

Copyright 2024 CASAREAL,Inc. All rights reserved. 88


5.5.4.3 Relevant Changes サブメニュー
• 2.2. Upgrade Spring Boot Version to 3.0
• Maven pom.xml 上のSpring Boot (⇒ 3.0.0)や Java (⇒ 1.7)のバージョンを更新します。
• 2.3. Upgrade to Jakarta EE 10
• 解説済み ソース上のパッケージ名 javax.* ⇒ jakarta.* に書き換えます。
• 2.4. Logging Date Format
• 解説済み ログ出力のDate Format を Spring Boot 2系と同じ出力にしたい場合、選択します。

• それ以外の選択メニューの概要は、以下の通りです。
メニュー 概要
2.1. Prepare for Spring 3.0 依存ライブラリがSpring Boot 3系で削除や非推奨になった場合に利
dependency 用する
2.5. Hibernate 6.1 JPAの実装であるHibernateのバージョンアップにより非互換性が発生
する可能性があることを示唆する
2.6. Updated Phases for Graceful アプリケーション終了時のフェーズの実装が変更されることを予告す
Shutdown る

Copyright 2024 CASAREAL,Inc. All rights reserved. 89


5.5.4.3 Relevant Changes サブメニュー
• それ以外の選択メニューの概要(続き)

メニュー 概要
2.7. YamlJsonParser Has Been スネーク形式のYAML解析を行うYamlJsonParserが削除されることを予
Removed 告する
2.8. 'server.max-http-header-size' リクエストヘッダーのサイズの上限値を設定するプロパティが変更さ
れることを予告する
2.9. Auto-configuration Files AutoConfigurationクラスを事前登録する設定ファイルが変更されるこ
とを予告する
2.10. Git Commit ID Maven Plugin Git Commit ID Maven Pluginのバージョンが変更されることを予告する
2.11. Running Your Application in the アプリケーションを起動するMavenコマンド(spring-boot:run、spring-
Maven Process boot:start)が削除
されることを予告する
2.12. ANTLR 2 ANTLR 2が依存関係管理から削除されることを予告する節
2.13. Other Removals Apache ActiveMQ、Atomikos、EhCache 2、Hazelcast 3が依存ライブラ
リから削除されるこ
とを予告する

Copyright 2024 CASAREAL,Inc. All rights reserved. 90


5.6 <参考> Spring Boot 3.2 トピック
• Spring Boot 3.2では、 Java 21 の仮想スレッドが利用できるようになります。
• application.properties に以下を設定
spring.threads.virtual.enabled=true
• これにより、逐次処理であれば、WebFluxを使ってリアクティブプログラミングを書かなくても
処理パフォーマンスを高めることができます。
※ 複数のWeb APIを並行して、呼び出したいような場合は、WebFluxが必要です。
⇒ モノシリックなアプリケーションをマイクロサービス化していく際の障壁が1つ解消

• ただし、マイクロサービス化を進める場合、他にも考える必要があることがあります。

Copyright 2024 CASAREAL,Inc. All rights reserved. 91


5.6 1 マイクロサービスアーキテクチャーによるトランザクション
• モノシリックなアプリケーションの場合、統合トランザクションにより、データの整合性を保つことができますが
マイクロサービスでは、それぞれ個々のマイクロサービスではローカルトランザクションを使い、
システム全体では、TCCパターン(Try-Confirm/Cancel)や、Sagaパターン(Commit/打消しの補
償トランザクション)を採用して、実現します。

• 以下のコースでは、マイクロサービスアーキテクチャーにおけるトランザクションについて学ぶことができます。

• マイクロサービスアーキテクチャにおけるトランザクション入門
• https://www.casareal.co.jp/ls/service/openseminar/cloudnativedojo/c140a-online
• 現代のエンタープライズ・システムでは、Docker、Kubernetesなどの技術を取り込み、よりスケーラブルで堅牢なシステムの構築、
運用が可能となるクラウドネイティブなシステムの活用が進められており、この クラウドネイティブに関する技術要素が急速に拡充し
ています。
• また、クラウドネイティブ環境においては、コンテナなどの技術を使用して、いわゆるマイクロサービスアーキテクチャを持ったシステムが
構築されることも多くなってきました。
• モノリスアプリケーションと異なり、様々な考慮・設計事項が必要となってくるマイクロサービスアーキテクチャですが、中でも一番重要
となる事項の1つがトランザクション制御ではないでしょうか。
• 本コースでは、マイクロサービスアーキテクチャにおけるトランザクション制御について、分散システムならではの問題点を提示し、その
問題点への解決方法を随時ハンズオンを織り交ぜながら、説明していきます。
• ※本コースでは、クラウドネイティブ環境としてAWSを使用します。
• ※本コースでは、マイクロサービスアーキテクチャにおけるトランザクションの理解補助のために、マイクロサービスランタイム環境として
Daprを使用します。

Copyright 2024 CASAREAL,Inc. All rights reserved. 92


5.6.2 多重化したマイクロサービスの管理
• SAGAパターンなどの分散トランザクションを行う場合、対象のマイクロサービスがダウンしていると、打消しの補
償トランザクションを実行できなくなりため、1つサービスを多重化して複数稼働させていきます。
• 1つのマイクロサービスとして動作する複数のホストがある場合、簡単な方法としてはDNSにサービス名に対し、
複数のホスト名を登録することで、実現できますが、DNSでは、実際にそのホストで稼働しているマイクロサー
ビスの死活(動作しているか/ダウンしているか)状況に合わせた結果は返ってきません。
• Kubernetesなどを利用すると、サービスの死活状態も管理し、適切な呼び出し先に要求を発行することが
できます。
• 以下のコースでは、 Kubernetesについて学ぶことができます。

• アプリケーションエンジニアのためのKubernetes基礎
https://www.casareal.co.jp/ls/service/openseminar/cloudnative/c033-online
• 本コースはアプリケーションをKubernetes上で動かすために必要な基礎知識やポイントを体
系的にわかりやすくご紹介します。
• これからKubernetesを使い始めようとしている方々にお勧めのコースです。

Copyright 2024 CASAREAL,Inc. All rights reserved. 93


創る、
学べる、
カサレアル。

お問い合わせは下記へお気軽にご連絡ください。
〒108-0075 東京都港区港南1-2-70
品川シーズンテラス
お問い合わせ先:営業部
TEL:03-4405-7865
Copyright 2024 CASAREAL,Inc. All rights reserved. 94

You might also like