46.8K Views
February 04, 24
スライド概要
JSUG勉強会 2024その1 2024-02-02(金)で使ったスライド資料です。
GraphQL入門、Spring for GraphQLを使ったGraphQLアプリケーション開発方法
そして、最後に、Spring Boot 3へのマイグレーションについて説明します。
研修のコース開発やトレーニングの実施をしています。
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 フロントエンド側で 欲しいデータを query で指定すれば 過不足なく取得できる オーバーフェッチ問題 アンダーフェッチ問題 の発生は無し! POST /graphql { “query”: “{ employee(id: 101) { id name }}”} idとnameのみをリクエスト { “id”: 101, “name”: “カサ太郎” } 関連するデータも取得できる POST /graphql { “query”: “{ employee(id: 101) { id name department { id name }}}”} { “id”: 101, “name”: “カサ太郎”, “department”: { “id”: 10, “name”: “営業部” } } エンドポイントのパスは /graphql 固定で1つのみ。 エンドポイントは増えない フロントエンド 開発者 HTTPメソッドは、検索・更新 含め、全て POST を使う サーバー側はそれぞれのオブジェクトに対す る全件取得、主キー取得を実装すればOK。 Spring for GraphQL がqueryを解釈して、 取得したオブジェクトから必要な項目や関連 するオブジェクトを含むJSONを組み立て、 サーバーサイド開発者 レスポンスを返してくれる 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 はじめに • サーバーサイド開発者はスキーマ定義を作成し、その定義に基づいて動作するようにサーバー実装を行う。 • フロントエンド開発者は、サーバーサイド開発者により作成されたスキーマ定義を確認すれば、 クエリを書くことができる。 2. 了解です! フロントエンド 開発者 • リレーショナルデータベースで、 テーブル定義を確認すれば SELECT文が書けるのと同じイメージ。 type Query { employees: [Employee] employee(id: ID): Employee } type Employee { id: ID name: String department: Department } type Department { id: ID name: String } 1. スキーマ定義を作ったから、 これを見て、クエリを 書いてね。 サイバーサイ ド開発者 まず、スキーマ定義から 説明します スキーマ定義 一部サンプル 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 Department { id :ID name :String } type Employee { id: ID name: String joinedDate: 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 { employees { … Queryで定義されたフィールド id name joinedDate email birthDay department { id name } } } … 先頭の query は省略可能 … 入れ子のフィールドを指定 Copyright 2024 CASAREAL,Inc. All rights reserved. 21
2.7.2 query操作 実行結果1 : employees フィールド
• employees: [Employee] フィールドを指定したquery操作 実行結果
⇒ Employeeを要素とする配列が返る
{
"data": {
"employees": [
{
"id": "101",
"name": "山田太郎",
"joinedDate": "2010-04-01",
"email": "[email protected]",
"birthDay": "1986-05-11",
"department": {
"id": "10",
"name": "営業部"
}
},
… 省略 …
{
"id": "109",
"name": "長谷川宏",
"joinedDate": "2012-04-01",
"email": "[email protected]",
"birthDay": "1989-11-29",
"department": {
"id": "20",
"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": "[email protected]",
"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 } input EmployeeInput { name: String! joinedDate: String! departmentId: Int! email: String! birthDay: String! } 型! … non null(NULL許容ではない) null以外の値を設定する必要があることを 示しています。 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: "[email protected]"
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": "[email protected]",
"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: "[email protected]"
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": "[email protected]",
"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": "[email protected]",
"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固有
public class Employee {
アノテーション
@Id
@GeneratedValue(省略) ← JPA固有
アノテーション
private Integer id;
private String name;
private LocalDate joinedDate;
private Integer departmentId;
private String email;
private LocalDate birthDay;
// getter/setter/equals/hashCodeなど
@Entity
← JPA固有
public class Department {
アノテーション
@Id
@GeneratedValue(省略)
← JPA固有
private Integer id;
アノテーション
private String name;
// getter/setter/equals/hashCodeなど
永続化層はSpring Data JPAで実装しています。
MyBatis SpringやJDBCなどでも構いません。
その場合は、置き換えてください。
リポジトリ インターフェース
public interface EmployeeRepository
extends JpaRepository<Employee, Integer> {
}
public interface DepartmentRepository
extends JpaRepository<Department, Integer> {
}
Copyright 2024 CASAREAL,Inc. All rights reserved.
39
3.1.3 エンティティクラス、永続化層インターフェース エンティティ @Entity public class Employee { @Id @GeneratedValue(省略) private Integer id; private String name; private LocalDate joinedDate; private Integer departmentId; private String email; private LocalDate birthDay; // getter/setter/equals/hashCodeなど • このサンプルの実装は、JPAの関連エンティティは使わず、 それぞれ独立したエンティティで実装しています。 • GraphQLスキーマで定義した オブジェクト型の関連は、 GraphQLの機能で実現します。 • EmployeeクラスのdepartmentId(関連するエンティティ のId)は、GraphQLで関連を実現するうえで必要です。 • スキーマ Employeeオブジェクト型の定義では、 departmentIdはなくても問題ありません。 Copyright 2024 CASAREAL,Inc. All rights reserved. 40
3.1.4 ビジネスロジック層
サービス実装クラス
@Service
@Transactional(readOnly = true)
public class EmployeeServiceImpl
implements EmployeeService {
// 省略
@Override
public List<Employee> findAll() {
return employeeRepository.findAll();
}
@Override
public Employee findById(Integer id) {
return employeeRepository.findById(id)
.orElseThrow(
() -> new RuntimeException());
}
@Service
@Transactional(readOnly = true)
public class DepartmentServiceImpl
implements DepartmentService {
// 省略
@Override
public Department findById(Integer id) {
return departmentRepository.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) { id name … department { … } ①、②の戻り値から レスポンスを組み立てる { “id”: 101, … “department”: { “id”: 10, …} } Spring for GraphQL ① Employee Controller Department Controller @QueryMapping employee(Integer id) ①: 関連するDepartmentを含まない Employee単体を返せばよい Employee ② @SchemaMapping department(Employee employee) ②: Department単体を返す Department query操作の取得したいフィールドで departmentが指定された場合は②が 呼び出される 指定されない場合は②は呼び出されない スキーマ定義 type Query { employees: [Employee] employee(id: ID): Employee } type Employee { id: ID … department: Department } Copyright 2024 CASAREAL,Inc. All rights reserved. 42
3.1.6 @QueryMapping スキーマ定義とコントローラーメソッドの対応 スキーマ定義 Query型 type Query { employees: [Employee] employee(id: ID): Employee } コントローラーメソッド 基本形:Queryのフィールド名とメソッド名を合わせる @QueryMapping 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 … department: Department } 基本形:オブジェクトのフィールド名とメソッド名を合わせる @SchemaMapping Department department(Employee employee) { メソッド名を変えたい場合 @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 {
// 省略
@QueryMapping
public Employee employee(@Argument Integer id) {
return employeeService.findById(id);
}
Query定義の引数付きフィールド
employee(id: ID): Employee
に対応したメソッドの引数には、@Argumentを付加する
@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 {
id name …
department { … }
{[
{ “id”: 101, …
“department”: {
“id”: 10, …}
}
省略
]}
Spring for
GraphQL
①
Employee
Controller
Department
Controller
@QueryMapping
employees()
List<Employee>
②
@SchemaMapping
department(Employee employee)
Department
query操作の取得したいフィールドで
departmentが指定された場合、
②は、①の戻り値リストの要素数分、
繰り返し呼び出される
指定されない場合、②は呼び出されない
スキーマ定義
type Query {
employees: [Employee]
employee(id: ID): Employee
}
type Employee {
id: ID
…
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 id PK B a_id FK C a_id FK A id PK D a_id FK • リレーションシップごとに、結合で対応 • テーブル間の関連が多ければ、その分の JOINを伴うSELECT文が必要 B a_id FK C a_id FK D a_id FK • 参照される側のリポジトリに List<エンティティ> findByIds(Set ids) があれば、良い • 3テーブル以上の結合があればさらにバリエーションが 増える ⇒ リポジトリに大量のメソッドが必要になる 結合とは(1対1の関連を除き)、非正規化した冗長なデータを作ること ⇒ 末端のサーバー間(DB~AP)で冗長なデータの受け渡しが行われる Copyright 2024 CASAREAL,Inc. All rights reserved. 51
3.1.15 <参考> N+1問題 の解決: employees:[Employee]
Spring for
GraphQL
employees
{[
{ “id”: 101, …
“department”: {
“id”: 10, …}
}
省略
]}
①
Employee
Controller
@QueryMapping
employees()
Department
Controller
Department
Service
DataLoader
@BatchMappingを使う
List<Employee>
②
@BatchMapping
department(List<Employee> employees)
List<Department>
Map<Employee, Department>
②が呼び出されるのは1回のみ
こちら<参考>のサンプルコードは
graphql-data-loaderプロジェクトに
あります。
findByIds(Set<Integer> ids)
スキーマ定義
type Query {
employees: [Employee]
employee(id: ID): Employee
}
type Employee {
id: ID
…
department: Department
}
Copyright 2024 CASAREAL,Inc. All rights reserved.
52
3.1.16 <参考> N+1問題 の解決:プレゼンテーション層
コントローラクラス (DepartmentContlloer)
@BatchMapping
public Map<Employee, Department> department(
List<Employee> employees) {
// 社員のListから検索対象の部署IDのSetを生成する。
Set<Integer> targetDepartmentIds = new HashSet<>();
for (Employee employee : employees) {
targetDepartmentIds.add(employee.getDepartmentId());
}
// 部署を検索する。
List<Department> departments =
departmentService.findByIds(targetDepartmentIds);
// 「key:Department.id、value:Department」のMap生成
Map<Integer, Department> deptMap = new HashMap<>();
for (Department department : departments) {
deptMap.put(department.getId(), department);
}
// 「key:Employee、value:Department」のMap生成
Map<Employee, Department> map = new HashMap<>();
for (Employee employee : employees) {
// employeeテーブルのdepartment_id列はNULLを許容して
// いるので注意。
if(employee.getDepartmentId() != null){
map.put(employee,
deptMap.get(employee.getDepartmentId()));
}
// department_id列はNULLの場合、mapにそのEmployeeは
// 入らないが問題ない。呼び出し元ではList<Employee>を
// 回して,結果を作成する
}
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 {
// 省略
@QueryMapping
public Window<Employee> employees(
ScrollSubrange subrange) {
// 省略
return employeeService.findAll(position, count);
本勉強会では、ページネーションの
解説は割愛しています
public class EmployeeController {
// 省略
@QueryMapping
public List<Employee> employees(){
return employeeService.findAll();
public class EmployeeServiceImpl implements EmployeeService {
// 省略
@Override
public Window<Employee> findAll(
ScrollPosition position, OptionalInt count) {
// 省略
return employeeRepository.findAllBy(position, limit);
以下のように読み替えてください。
public class EmployeeServiceImpl implements EmployeeService {
// 省略
@Override
public List<Employee> 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 {
id: ID
…
department: Department
}
スキーマ定義
type Department {
id: ID
name: String
office: Office
}
type Office {
id: ID
name: String
}
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 GraphQL updateEmployee(…) ① { “id”: 101, … “department”: { “id”: 10, …} } ② Employee Controller Department Controller @MutationMapping updateEmployee(…) Employee @SchemaMapping department(Employee employee) Department type Mutation { updateEmployee(id: ID! employeeInput: EmployeeInput!): Employee … } type Employee { … departmentId: Int department: Department スキーマ定義 input EmployeeInput { name: String! joinedDate: String! departmentId: Int! email: String! birthDay: String! } Copyright 2024 CASAREAL,Inc. All rights reserved. 59
3.2.4 inputオブジェクト型に対応したクラス inputクラス public class EmployeeInput { @NotBlank @Length(max = 10) private String name; @NotNull @PastOrPresent private LocalDate joinedDate; @NotNull private Integer departmentId; @NotBlank @Email @Length(max = 256) private String email; @NotNull @PastOrPresent private LocalDate birthDay; // getter/setter/equals/hashCodeなど Bean Validationのアノテーションで 入力検証ルールを設定できる Copyright 2024 CASAREAL,Inc. All rights reserved. 60
3.2.5 プレゼンテーション層: mutation操作
updateEmployee(id: ID! employeeInput: EmployeeInput!): Employee
コントローラクラス
@Controller
@Validatedを付けると、このメソッドの呼び出し直前で
public class EmployeeController {
// 省略
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 URL https://www.oracle.com/jp https://postgrest.org/en/st /database/technologies/ap able/ pdev/rest.html https://www.graphile.org/ postgraphile/ 公開I/F REST API GraphQL 機能 DB上のテーブルを、REST APIやGraphQLでアクセスできる トランザクション エンドポイントに対し、DBス エンドポイントに対し、独自 カスタムmutation クリプトをPL/SQLで記述 SQLスクリプトで記述 PL/pgSQLで記述した関数 を呼び出す 依存性 Oracle DBに依存 PostgREST PostGraphile REST API 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
public class Employee {
@Id
@GeneratedValue(省略)
private Integer id;
private String name;
private LocalDate joinedDate;
private String email;
private LocalDate birthDay;
// getter/setter/equals/hashCodeなど
リポジトリ インターフェース
@GraphQlRepository
public interface EmployeeRepository
extends JpaRepository<Employee, Integer> {
}
JpaRepositoryはQueryByExampleExecutorを継承して
いるので、以下のQueryが実行可能です。
・主キー検索など単一行の取得
・全件検索やエンティティのプロパティの値による
複数行の取得
・ページネーションによる複数行の取得
永続化層は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) {
id
name
}
}
{
"data": {
"employee": {
"id": "101",
"name": "山田太郎"
}
}
}
Copyright 2024 CASAREAL,Inc. All rights reserved.
69
4.6.2 実行確認 (query操作の実行) employees.query 実行結果 query { employees { id name } } { "data": { "employees": [ { "id": "101", "name": "山田太郎" }, 省略(8件) ] } } Copyright 2024 CASAREAL,Inc. All rights reserved. 70
4.6.2 実行確認 (query操作の実行) findEmployeeBy_1.query 実行結果 query { findEmployeeBy(employee: { departmentId: 10 }) { id name departmentId } } { "data": { "findEmployeeBy" : [ { "id": "101", "name": "山田太郎" "departmentId": 10 }, 省略(departmentIdが10 2件) ] } } Copyright 2024 CASAREAL,Inc. All rights reserved. 71
4.6.2 実行確認 (query操作の実行) findEmployeeBy_2.query 実行結果 query { findEmployeeBy(employee: { departmentId: 10 name:"山田太郎" }) { id name departmentId } } { "data": { "findEmployeeBy": [ { "id": "101", "name": "山田太郎", "departmentId": 10 } ] } } Copyright 2024 CASAREAL,Inc. All rights reserved. 72
5. Spring Boot 3へのマイグレーション
5.1 Spring Boot オープンソースとしてのサポートポリシー • Spring Bootは、原則として半年ごとに、マイナーバージョンがリリースされます。 • マイナーバージョン奇数 春5月リリース • マイナーバージョン偶数 秋11月リリース 例 2.7: 2022-5月、 3.1: 2023-5月 例 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-bootmigrator/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=ALLUNNAMED 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 dependency 依存ライブラリがSpring Boot 3系で削除や非推奨になった場合に利 用する 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 Removed スネーク形式のYAML解析を行うYamlJsonParserが削除されることを予 告する 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 Process アプリケーションを起動するMavenコマンド(spring-boot:run、springboot: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