Professional Documents
Culture Documents
Docswell 598Q7L
Docswell 598Q7L
2024/02/02
株式会社カサレアル 石井 真
自己紹介
所属
株式会社 カサレアル
名前
石井 真
プロフィール
➢ 主に、Java 系の研修コースの開発や、トレーニングの実施などをしています。
他にもフロントエンド系(TypeScript、React/Next.js、Vue.js/Nuxt.jsなど)や、クラウドネイティブ系(基礎、道場)など
多岐にわたる技術系研修を実施しています。
Copyright 2024 CASAREAL,Inc. All rights reserved. 3
本勉強会で解説する内容
• 本勉強会では、以下の研修コースから中心的な部分をピックアップして解説いたします。
⚫ GraphQL : RESTとの違い
⚫ GraphQL入門
⚫ Spring for GraphQLを利用した開発方法
⚫ @GraphQlRepositoryによりSpring Data JPAリポジトリを
直接リソースとして公開する
◼ ライセンス : 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
エンドポイントが
大量に用意されると… フロントエンド側の
要望 どれ使えばいいん 要望が絶えない
対応 だっけ… 本来やらなきゃいけ
ないこと沢山あるの
フロントエンド フロントエンド に… サーバーサイド
開発者 開発者 開発者
エンドポイントのパスは
/graphql 固定で1つのみ。 サーバー側はそれぞれのオブジェクトに対す
る全件取得、主キー取得を実装すればOK。
エンドポイントは増えない
Spring for GraphQL がqueryを解釈して、
HTTPメソッドは、検索・更新 取得したオブジェクトから必要な項目や関連
フロントエンド するオブジェクトを含むJSONを組み立て、
開発者 含め、全て POST を使う
レスポンスを返してくれる サーバーサイド開発者
• type で定義できる型(タイプ)は、以下の3つです。
型 (タイプ) 説明
オブジェクト EmployeeやDepartmentなど オブジェクトを型として定義
Query 定義したフィールドで検索オペレーション(操作)ができる
Mutation 定義したフィールドで更新オペレーション(操作)ができる
よみ:ミューテーション
• 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を要素とする配列型
• フィールドの型がオブジェクト型などフィールドを持つ型の場合、
{取得したいフィールドのリスト}が必要
• フィールドの型がスカラー型の場合、{取得したいフィールドのリスト}は不要
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
}
}
}
employee.query
query { … 先頭の query は省略可能
employee(id : 101) { … Queryで定義されたフィールド
id name joinedDate email birthDay
department { … 入れ子のフィールドを指定
id, name
}
}
}
• メソッドの引数にスカラー型ではなく、オブジェクト型を使いたい場合は、
通常のtypeで定義するオブジェクト型ではなく、inputオブジェクト型にする必要があります。
• inputオブジェクト型以外に、enum型やinterface型も定義することができますが、
こちらもtypeではなく、enumやinterfaceで定義します。
※ Javaでもenumやrecordなど特殊なクラスは、classではなく、
enumやrecordを使うのと同じようなイメージ。
• フィールドの型がスカラー型の場合、{戻り値のフィールドのリスト} は不要
mutationオペレーション(操作) ⇒ SQLでのINSERT/UPDATE/DELETE文など
更新系のSQL文に該当します
deleteEmployee.mutation
mutation {
deleteEmployee(id: 110) … 引数
}
• deleteEmployee はスカラー型(String)なので、
{戻り値のフィールドのリスト} は不要です。
• 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/
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”: “営業部” } }
…
]}
リポジトリ インターフェース
スキーマ定義
query操作の取得したいフィールドで
type Query { type Employee {
departmentが指定された場合は②が employees: [Employee] id: ID
呼び出される employee(id: ID): Employee …
指定されない場合は②は呼び出されない } department: Department
}
@Controller
public class DepartmentController {
// 省略
@SchemaMapping
public Department department(Employee employee) {
logger.info("departmentメソッドが呼び出された");
logger.info(employee.toString());
return departmentService.findById(employee.getDepartmentId());
}
スキーマ定義
query操作の取得したいフィールドで
type Query { type Employee {
departmentが指定された場合、 employees: [Employee] id: ID
②は、①の戻り値リストの要素数分、 employee(id: ID): Employee …
繰り返し呼び出される } department: Department
指定されない場合、②は呼び出されない }
• いずれにせよ、沢山のSELECTが発行されてしまうので、N+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回
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
あります。 }
@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;
} }
本勉強会では、ページネーションの 以下のように読み替えてください。
解説は割愛しています
public class EmployeeController { public class EmployeeServiceImpl implements EmployeeService {
// 省略 // 省略
@QueryMapping @Override
public List<Employee> employees(){ public List<Employee> findAll() {
return employeeService.findAll(); return employeeRepository.findAll();
基本的に考え方は同じ。
ネスト元のオブジェクト型(Department)から、ネスト先のオブジェクト型(Office)を一括取得する
@BatchMappingを付けたメソッドを作ればよい。
スキーマ定義
type Employee { type Department { type Office {
id: ID id: ID id: ID
… name: String name: String
department: Department office: Office }
} }
input EmployeeInput {
name: String!
joinedDate: String!
departmentId: Int!
email: String!
birthDay: String!
}
Bean Validationのアノテーションで
入力検証ルールを設定できる
@GraphQlExceptionHandler
public List<GraphQLError> handleInvalidationError(ConstraintViolationException exception) {
List<GraphQLError> errors = new ArrayList<>();
省略
return errors;
}
Query型 フィールド 説明
/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件)
]
}
}
findEmployeeBy_1.query 実行結果
query { {
findEmployeeBy(employee: { "data": {
departmentId: 10 "findEmployeeBy" : [
}) { {
id "id": "101",
name "name": "山田太郎"
departmentId "departmentId": 10
} },
} 省略(departmentIdが10 2件)
]
}
}
findEmployeeBy_2.query 実行結果
query { {
findEmployeeBy(employee: { "data": {
departmentId: 10 "findEmployeeBy": [
name:"山田太郎" {
}) { "id": "101",
id "name": "山田太郎",
name "departmentId": 10
departmentId }
} ]
} }
}
• ここでは、便利なライブラリ、ツールの紹介をしながら、以下の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での大きな変更点とその対応
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.
# 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}/
• Javaバージョン 8以降、さまざまな有用な新機能が追加されています。これらの新機能
を利用していくことができます。
• 起動し、最後に以下のメッセージがコンソールに出力されたら、起動成功です。
…
Scanning edu-spring-boot-migrator
finished scan. Please open: http://localhost:8080/spring-boot-upgrade
• ブラウザから、コンソールに出力されている以下のURLでアクセスします。
• http://localhost:8080/spring-boot-upgrade
• それ以外の選択メニューの概要は、以下の通りです。
メニュー 概要
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 る
メニュー 概要
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が依存ライブラ
リから削除されるこ
とを予告する
• ただし、マイクロサービス化を進める場合、他にも考える必要があることがあります。
• 以下のコースでは、マイクロサービスアーキテクチャーにおけるトランザクションについて学ぶことができます。
• マイクロサービスアーキテクチャにおけるトランザクション入門
• https://www.casareal.co.jp/ls/service/openseminar/cloudnativedojo/c140a-online
• 現代のエンタープライズ・システムでは、Docker、Kubernetesなどの技術を取り込み、よりスケーラブルで堅牢なシステムの構築、
運用が可能となるクラウドネイティブなシステムの活用が進められており、この クラウドネイティブに関する技術要素が急速に拡充し
ています。
• また、クラウドネイティブ環境においては、コンテナなどの技術を使用して、いわゆるマイクロサービスアーキテクチャを持ったシステムが
構築されることも多くなってきました。
• モノリスアプリケーションと異なり、様々な考慮・設計事項が必要となってくるマイクロサービスアーキテクチャですが、中でも一番重要
となる事項の1つがトランザクション制御ではないでしょうか。
• 本コースでは、マイクロサービスアーキテクチャにおけるトランザクション制御について、分散システムならではの問題点を提示し、その
問題点への解決方法を随時ハンズオンを織り交ぜながら、説明していきます。
• ※本コースでは、クラウドネイティブ環境としてAWSを使用します。
• ※本コースでは、マイクロサービスアーキテクチャにおけるトランザクションの理解補助のために、マイクロサービスランタイム環境として
Daprを使用します。
• アプリケーションエンジニアのためのKubernetes基礎
https://www.casareal.co.jp/ls/service/openseminar/cloudnative/c033-online
• 本コースはアプリケーションをKubernetes上で動かすために必要な基礎知識やポイントを体
系的にわかりやすくご紹介します。
• これからKubernetesを使い始めようとしている方々にお勧めのコースです。
お問い合わせは下記へお気軽にご連絡ください。
〒108-0075 東京都港区港南1-2-70
品川シーズンテラス
お問い合わせ先:営業部
TEL:03-4405-7865
Copyright 2024 CASAREAL,Inc. All rights reserved. 94