Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
「2024年版 Kotlin サーバーサイドプログラミング実践開発」の補講 〜O/Rマッパー編〜
Search
Takehata Naoto
July 18, 2024
Programming
2
550
「2024年版 Kotlin サーバーサイドプログラミング実践開発」の補講 〜O/Rマッパー編〜
2024年7月18日(土) 「Server-side Kotlin Night 2024/07」の発表資料です。
Takehata Naoto
July 18, 2024
Tweet
Share
More Decks by Takehata Naoto
See All by Takehata Naoto
2024年版 Kotlin サーバーサイドプログラミング実践開発
n_takehata
6
4.6k
Server-Side目線で見る、Kotlin Festの楽しみ方
n_takehata
0
380
KotlinとCloud Vision APIで領収書の電子帳簿保存法対応をする
n_takehata
1
680
KotlinConf 2023 現地参加レポート
n_takehata
1
320
サーバーサイドKotlinクイズ
n_takehata
0
180
サーバーサイドでのKotlin Coroutines
n_takehata
0
1.1k
KotlessとDynamoDBで自分のツイートを収集するサーバーレスアプリケーションを作る
n_takehata
0
380
書籍『Kotlin サーバーサイドプログラミング実践開発』のこだわりとおすすめポイント
n_takehata
0
520
Server-Side Kotlinで必要なJavaの知識
n_takehata
1
490
Other Decks in Programming
See All in Programming
「とりあえず動く」コードはよい、「読みやすい」コードはもっとよい / Code that 'just works' is good, but code that is 'readable' is even better.
mkmk884
6
1.4k
テストコード書いてみませんか?
onopon
2
330
ecspresso, ecschedule, lambroll を PipeCDプラグインとして動かしてみた (プロトタイプ) / Running ecspresso, ecschedule, and lambroll as PipeCD Plugins (prototype)
tkikuc
2
1.8k
VisionProで部屋の明るさを反映させるシェーダーを作った話
segur
0
100
20年もののレガシープロダクトに 0からPHPStanを入れるまで / phpcon2024
hirobe1999
0
1k
Beyond ORM
77web
11
1.6k
週次リリースを実現するための グローバルアプリ開発
tera_ny
1
1.1k
どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか
okashoi
3
870
良いユニットテストを書こう
mototakatsu
11
3.6k
Fibonacci Function Gallery - Part 2
philipschwarz
PRO
0
210
令和7年版 あなたが使ってよいフロントエンド機能とは
mugi_uno
10
5k
Flatt Security XSS Challenge 解答・解説
flatt_security
0
720
Featured
See All Featured
Product Roadmaps are Hard
iamctodd
PRO
50
11k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
6
500
How GitHub (no longer) Works
holman
312
140k
RailsConf 2023
tenderlove
29
970
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
The Power of CSS Pseudo Elements
geoffreycrofte
74
5.4k
Building Better People: How to give real-time feedback that sticks.
wjessup
366
19k
Designing for Performance
lara
604
68k
Faster Mobile Websites
deanohume
305
30k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
A Tale of Four Properties
chriscoyier
157
23k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
Transcript
「2024年版 Kotlin サーバーサイドプログラミング 実践開発」の補講 〜O/Rマッパー編〜 2024年7月18日 Server-side Kotlin Night 2024/07
竹端 尚人
自己紹介
竹端 尚人 主にバックエンドエンジニア Twitter: @n_takehata • 2006.04〜 公務員 • 2007.12〜
SES • 2011.04〜 モバイルゲーム開発(サーバーサイド Kotlinを始める) • 2020.12〜 フリーランス(バックエンド開発、 テックリード、技術顧問など) 概要 現在は主に、クラウド型電子カルテを開発している株式 会社ヘンリーでエンジニアとして従事 Kotlin愛好会というコミュニティの運営もやっています
Kotlin愛好会も明日やります!
• CEDEC 2018、2019登壇 • Software Design 2019年2月号〜4月号で短期連載 「サーバーサイド開発の品質を向上させる Java→Kotlin移行のススメ」執筆 •
2021年4月 書籍「Kotlin サーバーサイドプログラ ミング実践開発」出版 • 2023年4月 Techpitにて「Kotlin入門ガイドー言語 思想から特徴・歴史・使いどころまで、まるっと予 備知識がわかる教科書」執筆 • Kotlin Fest 2024登壇 登壇、執筆
2024年版 Kotlin サーバーサイドプログラミング実践開発
Kotlin Fest 2024で話した内容から、 盛り上がったO/Rマッパーについて深堀って話します
Kotlin Fest 2024の発表資料はこちら https://speakerdeck.com/n_takehata/kotlin-s erver-side-programming-practice-2024
アジェンダ 1. Kotlin Fest 2024の振り返り 2. 各種O/Rマッパーのおさらい 3. 色々なSQLを実装してみる 4.
まとめ
アジェンダ 1. Kotlin Fest 2024の振り返り 2. 各種O/Rマッパーのおさらい 3. 色々なSQLを実装してみる 4.
まとめ
1. Kotlin Fest 2024の振り返り
「2024年版 Kotlin サーバーサイドプログラミング 実践開発」 の内容
• 「Kotlin サーバーサイドプログラミング実践開発」の 内容紹介 • 現在のサーバーサイドKotlinで使えるフレームワークの 紹介(Web、DI、O/Rマッパー、テスト) • 今Kotlinでアプリケーションを作る場合のフレームワー ク選定の紹介
「2024年版 Kotlin サーバーサイドプログラミング 実践開発」 の内容
• Ktor(Webアプリケーションフレームワーク) • Koin(DIフレームワーク) • JOOQ(O/Rマッパー) • Kotest(テストフレームワーク)
RepositoryImpl O/Rマッパー (JOOQ) ドメイン オブジェクト Repository Test (Kotest) Service Controller
(Ktor) DI(Koin)
Ask the Speaker、懇親会で O/Rマッパーの話で盛り上がった
• セッションでも「O/Rマッパーが一番迷った」と話して いた • 「O/Rマッパーだけは何使おうか迷うんですよね」とい う悩みが多かった • SQLに近い形で書ける方がいいという意見に「やっぱ りそうですよね」というリアクション O/Rマッパーの話で盛り上がった
結局エンジニアはSQLを書きたい(人が多い)
なぜエンジニアはSQLを書きたいのか?
• 結局「こういうSQLを書きたいからコードは・・・」 という順序で考えている • テーブル設計を意識してデータを扱うのに、SQLだけ 抽象化しても混乱する • テーブルもSQLも意識せず「こういうデータが欲し い」を考えるだけにできないと抽象化の意味がない なぜエンジニアはSQLを書きたいのか?
結局エンジニアはSQLを書きたい(人が多い) 書きたいわけではないけど、いい具合に抽象化された ソリューションがない?
今回は各種O/Rマッパーで色々なSQLの 実装方法を紹介します
参考: Kotlin Fest 2024登壇の振り返り(ブログ) https://blog.takehata-engineer.com/entry/loo k-back-at-kotlin-fest-2024
アジェンダ 1. Kotlin Fest 2024の振り返り 2. 各種O/Rマッパーのおさらい 3. 色々なSQLを実装してみる 4.
まとめ
2. 各種O/Rマッパーのおさらい
• Exposed • JOOQ • Ktorm Kotlin Fest 2024で紹介したO/Rマッパー
Exposed
• JetBrains社が開発しているKotlin製のO/Rマッパー • SQLライクに実装できるDSL、軽量なDAOという2つの アクセス方法が用意されている • 長い間0.x系が続いていたが、2024年中に1.0になるこ とが予定されている Exposedとは?
JOOQ
• SQLに近いDSLでのクエリ作成が可能 • テーブルスキーマからのコード生成が可能で、もとも とJavaのO/RマッパーだがKotlinのコード生成にも対応 している • R2DBCによるノンブロッキングI/Oにも対応 JOOQとは?
Ktorm
• 純粋なJDBCに基づいたKotlin用の軽量なO/Rマッパー • Kotlin製のサードパーティフレームワーク • 生のSQLに近い形で書ける柔軟なDSLが用意されている Ktormとは?
Kotlin Fest 2024では紹介しなかった その他のO/Rマッパー
• MyBatis • Doma2 • Komapper その他のO/Rマッパー
アジェンダ 1. Kotlin Fest 2024の振り返り 2. 各種O/Rマッパーのおさらい 3. 色々なSQLを実装してみる 4.
まとめ
3. 色々なSQLを実装してみる
• Exposed • JOOQ • Ktorm ※ExposedはSQL DSLを使用します この3つのO/Rマッパーで色々なクエリを書いて 比較していきます
使用するテーブル(RDBはMySQLを使用) CREATE TABLE users ( id varchar(10) NOT NULL, name
varchar(50) NOT NULL, age int NOT NULL, PRIMARY KEY (id) ); CREATE TABLE user_purchase_histories ( id varchar(10) NOT NULL, user_id varchar(10) NOT NULL, purchase_date date NOT NULL, price int NOT NULL, PRIMARY KEY (id) );
基本的なCRUDのクエリ (Kotlin Festからの再掲)
transaction { // Insert Users.insert { it[id] = "kotlin" it[name]
= "Kotlin Fest" it[age] = 3 } // Update Users.update({ Users.id eq "kotlin" }) { it[age] = 4 } // Select val users = Users.select(Users.name, Users.age).where { Users.age greaterEq 3 } // Delete Users.deleteWhere{ id eq "kotlin" } } Exposed
// Insert dslContext.insertInto(USERS) .columns(USERS.ID, USERS.NAME, USERS.AGE) .values("kotlin", "Kotlin Fest", 3)
.execute() // Update dslContext.update(USERS) .set(USERS.AGE, 4) .where(USERS.ID.eq("kotlin")) .execute() // Select val users = dslContext.selectFrom(USERS).where(USERS.AGE.ge(3)).fetch() // Delete dslContext.deleteFrom(USERS) .where(USERS.ID.eq("kotlin")) .execute() JOOQ
// Insert database.insert(Users) { set(it.id, "kotlin") set(it.name, "Kotlin Fest" )
set(it.age, 3) } // Update database.update(Users) { set(it.age, 4) where { it.id eq "kotlin" } } // Select val users = database.from(Users).select().where { Users.age greaterEq 3 } // Delete database.delete(Users) { it.id eq "kotlin" } Ktorm
• Exposedはfromやupdate文のsetなど、キーワードを 削って抽象化している • JOOQはほぼ生のSQLと同じ構文で書ける • Ktormも生のSQLに近いが、fromの位置やinsertで使 うsetキーワードなど、少し差分がある
GROUP BY、ORDER BY、LIMIT、OFFSET
select age, count(*) from USERS group by age order by
count(*) desc limit 3 offset 1;
Users.select(Users.age, Users.id.count()) .groupBy(Users.age) .orderBy(Users.id.count(), SortOrder.DESC) .limit(3, offset = 1) Exposed
dslContext.select(USERS.AGE, count()) .from(USERS) .groupBy(USERS.AGE) .orderBy(count().desc()) .limit(1, 3) .fetch() JOOQ
dslContext.select(USERS.AGE, count()) .from(USERS) .groupBy(USERS.AGE) .orderBy(count().desc()) .limit(3) .offset(1) .fetch() offsetを別で渡すことも可能
database.from(Users) .select(Users.age, count()) .groupBy(Users.age) .orderBy(count().desc()) .limit(1, 3) Ktorm
database.from(Users) .select(Users.age, count()) .groupBy(Users.age) .orderBy(count().desc()) .limit(3) .offset(1) off offsetを別で渡すことも可能
• 3つとも書き方に大きな差分はない • ExposedがFromがない分短いくらい
JOIN
select * from USERS left join USER_PURCHASE_HISTORIES on USERS.id =
USER_PURCHASE_HISTORIES.user_id;
Users.leftJoin( UserPurchaseHistories , { Users.id }, { UserPurchaseHistories.userId } ).selectAll()
Exposed
dslContext.select() .from(USERS) .leftJoin(USER_PURCHASE_HISTORIES) .on(USERS.ID.eq(USER_PURCHASE_HISTORIES.USER_ID)) .fetch() JOOQ
database .from(Users) .leftJoin( UserPurchaseHistories , on = Users.id eq UserPurchaseHistories.userId)
.select() Ktorm
• Exposedはonのキーワードを使わず、少し抽象化して いる • JOOQとKtormは近いが、JOOQの方がonをチェーンし て書いてる分、より生のSQLに近い印象
サブクエリ
select * from USERS where exists( select * from USER_PURCHASE_HISTORIES
where USERS.id = USER_PURCHASE_HISTORIES.user_id );
Users.select(Users.columns) .where { exists( UserPurchaseHistories .select(UserPurchaseHistories.id).where{ Users.id eq UserPurchaseHistories.userId }
) } Exposed
dslContext.selectFrom(USERS) .whereExists( dslContext.selectOne() .from(USER_PURCHASE_HISTORIES) .where(USER_PURCHASE_HISTORIES.USER_ID.eq(USERS.ID)) ) .fetch() JOOQ
database .from(Users) .select() .where { exists( database.from(UserPurchaseHistories) .select(UserPurchaseHistories.id) .where {
UserPurchaseHistories.userId eq Users.id } ) } Ktorm
• JOOQだけwhereExists、selectOneなどの関数があり 少し抽象化されている • ExposedとKtormはwhere→exists→selectの階層に なっており、生のSQLに近い
JOINして絞り込む
select USERS.id, USERS.name, sum(USER_PURCHASE_HISTORIES.price) from USERS left join USER_PURCHASE_HISTORIES on
USERS.id = USER_PURCHASE_HISTORIES.user_id where USERS.age >= 20 group by USERS.id having sum(USER_PURCHASE_HISTORIES.price) >= 3000;
Users.leftJoin(UserPurchaseHistories , { Users.id }, { UserPurchaseHistories .userId }) .select(Users.id,
Users.name, UserPurchaseHistories .price.sum()) .where { Users.age greaterEq 20 } .groupBy(Users.id) .having { UserPurchaseHistories .price.sum() greaterEq 3000 } Exposed
dslContext.select(USERS.ID, USERS.NAME, DSL.sum(USER_PURCHASE_HISTORIES.PRICE)) .from(USERS) .leftJoin(USER_PURCHASE_HISTORIES) .on(USERS.ID.eq(USER_PURCHASE_HISTORIES.USER_ID)) .where(USERS.AGE.ge(20)) .groupBy(USERS.ID) .having(DSL.sum(USER_PURCHASE_HISTORIES.PRICE). ge(BigDecimal(3000)))
.fetch() JOOQ
database .from(Users) .leftJoin(UserPurchaseHistories, on = Users.id eq UserPurchaseHistories .userId) .select(Users.id,
Users.name, sum(UserPurchaseHistories.price)) .where { Users.age greaterEq 20 } .groupBy(Users.id, Users.name) .having { sum(UserPurchaseHistories.price) greaterEq 3000 } Ktorm
• 少し複雑で長いSQLになった分、構文で抽象化してい るExposedの短さがより際立っている • JOOQは生のSQLに近い構文な上、”DSL.”のように書か なければいけないキーワードが多いので長い
アジェンダ 1. Kotlin Fest 2024の振り返り 2. 各種O/Rマッパーのおさらい 3. 色々なSQLを実装してみる 4.
まとめ
4. まとめ
• 書きたいわけではないが、書く以外でいいソリュー ションがない • SQLだけ抽象化された状態では微妙 • 結果SQLっぽく書けるO/Rマッパーが好まれやすい エンジニアはSQLを書きたい?
• ExposedはFROMを省略したりJOINの書き方が簡略化 されたりと、少し短く書けるようになっている • JOOQは一番SQLに近い構文で書ける印象 • KtormもSQLに近いが、句の順序やJOINの書き方など 一部差分がある 各種O/Rマッパーの特性
Have a nice Server-side Kotlin!