SlideShare a Scribd company logo
1 of 29
Download to read offline
100%KotlinのORM
Ktormを試してみた
(Exposedとの⽐較アリ)
Server-Side Kotlin Study #1
2021/05/12
1
⾃⼰紹介
塚本啓太
株式会社イエソドエンジニア
KotlinとTypeScriptが⼤好きだけど、
たまにvalとconstを間違える。
Twitter: @tsukakei1012
GitHub: tsukakei
2
会社紹介
株式会社イエソド
SaaS統制プラットフォーム
『YESOD』
⼈・組織・情報と
SaaSをシームレスに
管理するためのSaaS
https://www.yesod.co/
KotlinとTypeScriptで
できています 3
今⽇話すこと
Ktormの紹介
Exposedとの⽐較
4
とは
100%Kotlinの軽量ORM
ConfigファイルもXMLもサードパーティ依存もなく軽量で使いやす
い
強く型付けされたSQL DSL
柔軟なクエリ、適度な粒度で思った通りのSQLが書ける
filter や map のようなKotlin nativeの感覚で書けるAPIを提供
拡張可能なデザイン
https://www.ktorm.org/
5
あれ、でもExposedは??
6
とは
JetBrains製の100% Kotlin ORM
JetBrains製という絶対正義感
アイコンは
Ktorm同様に軽量さと型付けを特徴としている
https://github.com/JetBrains/Exposed
Google TrendsではExposedの圧勝
https://kotlin.libhunt.com/compare-exposed-vs-ktorm
7
せっかくなので⽐較してみましょう
レポジトリを⽤意しました
https://github.com/tsukakei/KotlinOrmSample
8
ダウンロード⽅法
Ktorm
implementation("org.ktorm:ktorm-core:3.3.0")
Exposed
implementation("org.jetbrains.exposed", "exposed-core", "0.31.1")
implementation("org.jetbrains.exposed", "exposed-dao", "0.31.1")
implementation("org.jetbrains.exposed", "exposed-jdbc", "0.31.1")
implementation("org.jetbrains.exposed", "exposed-jodatime", "0.31.1")
implementation("mysql", "mysql-connector-java", "8.0.24")
9
DBへの接続とトランザクション
DBへの接続(基本項⽬はほぼ同じ、オプションが異なる)
// ktrom
val database = Database.connect("jdbc:mysql://localhost:3306/sample_db", user = "root", password = "root")
// exposed
val database = Database.connect("jdbc:mysql://localhost:3306/sample_db", user = "root", password = "root")
トランザクション
// ktrom
database.useTransaction { }
// exposed
transaction(database) { }
10
DSLの⽐較
テーブル定義
各種SQL
SELECT
AGGREGATION, UNION, JOIN
INSERT, UPDATE, DELETE
レポジトリ
https://github.com/tsukakei/KotlinOrmSample
11
テーブル定義(SQL)
CREATE TABLE t_department(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
location VARCHAR(128) NOT NULL
);
CREATE TABLE t_employee(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
job VARCHAR(128) NOT NULL,
hire_date DATE NOT NULL,
salary BIGINT NOT NULL,
department_id INT NOT NULL
);
12
テーブル定義(Ktorm)
object KtormDepartments : Table<Department>("t_department") {
val id = int("id").primaryKey().bindTo { it.id }
val name = varchar("name").bindTo { it.name }
val location = varchar("location").bindTo { it.location }
}
object KtormEmployees : Table<Employee>("t_employee") {
val id = int("id").primaryKey().bindTo { it.id }
val name = varchar("name").bindTo { it.name }
val job = varchar("job").bindTo { it.job }
val hireDate = date("hire_date").bindTo { it.hireDate }
val salary = long("salary").bindTo { it.salary }
val departmentId = int("department_id").references(KtormDepartments) { it.department }
}
13
テーブル定義(Exposed)
object ExposedDepartments : IntIdTable("t_department") {
val name = varchar("name", 50)
val location = varchar("location", 50)
}
object ExposedEmployees : IntIdTable("t_employee") {
val name = varchar("name", 50)
val job = varchar("job", 50).nullable()
val hireDate = date("hire_date")
val salary = long("salary")
val departmentId = reference("department_id", ExposedDepartments.id)
}
14
SELECT
// Ktorm
database
.from(KtormEmployees)
.select(KtormEmployees.name)
.where { (KtormEmployees.departmentId notEq 4) and (KtormEmployees.name like "%郎%") }
// Exposed
ExposedEmployees
.slice(ExposedEmployees.name)
.select { (ExposedEmployees.departmentId eq 1) and (ExposedEmployees.name like "%郎%") }
SQL: SELECT t_employee.`name` FROM t_employee WHERE (t_employee.department_id = 1) AND (t_employee.`name` LIKE '%郎%')
Ktorm: SQLそのまんま
Exposed: selectにwhere式を渡す& sliceで取得したい列を指定 15
AGGREGATION
// Ktorm
database.from(KtormEmployees)
.select(KtormEmployees.departmentId, avg(KtormEmployees.salary))
.groupBy(KtormEmployees.departmentId)
.having { avg(KtormEmployees.salary) greater 100.0 }
// Exposed
ExposedEmployees
.slice(ExposedEmployees.departmentId, ExposedEmployees.salary.avg())
.selectAll()
.groupBy(ExposedEmployees.departmentId)
.having { ExposedEmployees.salary.avg() greater 100.0 }
SQL: SELECT t_employee.department_id, AVG(t_employee.salary) FROM t_employee
GROUP BY t_employee.department_id HAVING AVG(t_employee.salary) > 100.0 16
UNION
// Ktorm
database
.from(KtormEmployees)
.select(KtormEmployees.id)
.unionAll(database.from(KtormDepartments).select(KtormDepartments.id))
// Exposed
ExposedEmployees
.slice(ExposedEmployees.id)
.selectAll()
.union(ExposedDepartments.slice(ExposedDepartments.id).selectAll())
SQL: SELECT t_employee.id FROM t_employee UNION SELECT t_department.id FROM t_department
17
JOIN
// Ktorm
database
.from(KtormEmployees)
.leftJoin(KtormDepartments, on = KtormEmployees.departmentId eq KtormDepartments.id)
.select(KtormEmployees.name, KtormDepartments.name)
.orderBy(KtormEmployees.id.asc())
// Exposed
ExposedEmployees
.join(ExposedDepartments, JoinType.LEFT)
.slice(ExposedEmployees.name, ExposedDepartments.name)
.selectAll()
.orderBy(ExposedEmployees.id to SortOrder.ASC)
SQL: SELECT t_employee.`name`, t_department.`name` FROM t_employee
LEFT JOIN t_department ON t_department.id = t_employee.department_id ORDER BY t_employee.id ASC
18
INSERT
database.insert(KtormEmployees) {
set(it.name, "Server-side 三郎")
set(it.job, "Fullstack")
set(it.hireDate, LocalDate.now())
set(it.salary, 10000)
set(it.departmentId, 1)
}
ExposedEmployees.insert {
it[name] = "Server-side 三郎"
it[job] = "Fullstack Engineer"
it[hireDate] = DateTime.now()
it[salary] = 10000
it[departmentId] = 1
}
19
UPDATE & DELETE
// Ktorm
database.update(KtormEmployees) {
set(it.salary, 100)
where { it.id eq 2 }
}
database.delete(KtormEmployees) { it.id eq 4 }
// Exposed
ExposedEmployees.update({ ExposedEmployees.id eq 2 }) {
it[salary] = 100
}
ExposedEmployees.deleteWhere { ExposedEmployees.id eq 4 }
20
DSLまとめ
KtormもExposedもメソッドチェイニングでスルスルと書ける
Ktormの⽅がよりSQLライク
Exposedはやりたいことをしたいときにドキュメントを読む必要が
あるように感じた
21
DAO
Entity定義
CRUD
レポジトリ
https://github.com/tsukakei/KotlinOrmSample
22
Entity定義(Ktorm)
interface Department : Entity<Department> {
companion object : Entity.Factory<Department>()
val id: Int
var name: String
var location: String
}
interface Employee : Entity<Employee> {
companion object : Entity.Factory<Employee>()
val id: Int
var name: String
var job: String
var hireDate: LocalDate
var salary: Long
var department: Department
}
23
Entity定義(Exposed)
class ExposedDepartment(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<ExposedDepartment>(ExposedDepartments)
var name by ExposedDepartments.name
var location by ExposedDepartments.location
}
class ExposedEmployee(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<ExposedEmployee>(ExposedEmployees)
var name by ExposedEmployees.name
var job by ExposedEmployees.job
var hireDate by ExposedEmployees.hireDate
var salary by ExposedEmployees.salary
var department by ExposedDepartment referencedOn ExposedEmployees.departmentId
}
24
CRUD (Ktorm)
// create
val department = Department {
name = "Sales"
location = "Osaka"
}
database.sequenceOf(KtormDepartments).add(department)
// update
department.run {
location = "Fukuoka"
flushChanges()
}
// read
database.sequenceOf(KtormDepartments).find { it.id eq department.id }?.also { println(it.location) }
// delete
department.delete()
25
CRUD (Exposed)
// create
val department = ExposedDepartment.new {
name = "Sales"
location = "Osaka"
}
// update
department.location = "Fukuoka"
// read
ExposedDepartment.findById(department.id)?.also { println(it.location) }
// delete
department.delete()
26
DAOまとめ
Ktormは少しDatabaseを意識しないといけない
その分、少し冗⻑に感じた
TableとEntityの扱い⽅が微妙に違う
ExposedはTable定義が先、Entityが後
KtormはEntityが先、Tableが後
27
KtormとExposedの⽐較まとめ
基本的な機能や型サポートについては⼤差ないように感じた
互いにMySQL・PostgreSQL・Oracle・SQL Serverをサポート
書き⽅やドキュメントについてはKtormの⽅が好み
しかしながら、JetBrains開発という安⼼感がExposedにはある
Exposed Ktorm
開発元 JetBrains 個⼈開発者(@vincentlauvlwj)
数 5.1K 1K弱
採⽤実績 YESOD, UPSIDER 皆無? 28
We're hiring!
モダンな静的型付け⾔語で
B2B SaaSを開発したい
エンジニア募集!
https://www.wantedly.com/pr
ojects/631289
Kotlin (Ktor + Exposed)
使ってます!!
29

More Related Content

What's hot

What's hot (20)

例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
GraphQLのsubscriptionで出来ること
GraphQLのsubscriptionで出来ることGraphQLのsubscriptionで出来ること
GraphQLのsubscriptionで出来ること
 
世界でいちばんわかりやすいドメイン駆動設計
世界でいちばんわかりやすいドメイン駆動設計世界でいちばんわかりやすいドメイン駆動設計
世界でいちばんわかりやすいドメイン駆動設計
 
SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021
 
Java EE から Quarkus による開発への移行について
Java EE から Quarkus による開発への移行についてJava EE から Quarkus による開発への移行について
Java EE から Quarkus による開発への移行について
 
マイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチマイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチ
 
初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!
 
backlogsでもCI/CDする夢を見る
backlogsでもCI/CDする夢を見るbacklogsでもCI/CDする夢を見る
backlogsでもCI/CDする夢を見る
 
単なるキャッシュじゃないよ!?infinispanの紹介
単なるキャッシュじゃないよ!?infinispanの紹介単なるキャッシュじゃないよ!?infinispanの紹介
単なるキャッシュじゃないよ!?infinispanの紹介
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説
 
ぱぱっと理解するSpring Cloudの基本
ぱぱっと理解するSpring Cloudの基本ぱぱっと理解するSpring Cloudの基本
ぱぱっと理解するSpring Cloudの基本
 
Apache Avro vs Protocol Buffers
Apache Avro vs Protocol BuffersApache Avro vs Protocol Buffers
Apache Avro vs Protocol Buffers
 
分散トレーシング技術について(Open tracingやjaeger)
分散トレーシング技術について(Open tracingやjaeger)分散トレーシング技術について(Open tracingやjaeger)
分散トレーシング技術について(Open tracingやjaeger)
 
実運用して分かったRabbit MQの良いところ・気をつけること #jjug
実運用して分かったRabbit MQの良いところ・気をつけること #jjug実運用して分かったRabbit MQの良いところ・気をつけること #jjug
実運用して分かったRabbit MQの良いところ・気をつけること #jjug
 
人生がときめくAPIテスト自動化 with Karate
人生がときめくAPIテスト自動化 with Karate人生がときめくAPIテスト自動化 with Karate
人生がときめくAPIテスト自動化 with Karate
 
組織にテストを書く文化を根付かせる戦略と戦術
組織にテストを書く文化を根付かせる戦略と戦術組織にテストを書く文化を根付かせる戦略と戦術
組織にテストを書く文化を根付かせる戦略と戦術
 
インフラエンジニアの綺麗で優しい手順書の書き方
インフラエンジニアの綺麗で優しい手順書の書き方インフラエンジニアの綺麗で優しい手順書の書き方
インフラエンジニアの綺麗で優しい手順書の書き方
 
BigQueryの課金、節約しませんか
BigQueryの課金、節約しませんかBigQueryの課金、節約しませんか
BigQueryの課金、節約しませんか
 

Similar to 100%Kotlin ORM Ktormを試してみた

T sql の parse と generator
T sql の parse と generatorT sql の parse と generator
T sql の parse と generator
Oda Shinsuke
 
HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLHandlerSocket plugin for MySQL
HandlerSocket plugin for MySQL
akirahiguchi
 
【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ
Developers Summit
 
[CB16] (P)FACE :アップルのコアへ、そしてルート権限へのエクスプロイト by Moony Li & Jack Tang
[CB16] (P)FACE :アップルのコアへ、そしてルート権限へのエクスプロイト by Moony Li & Jack Tang[CB16] (P)FACE :アップルのコアへ、そしてルート権限へのエクスプロイト by Moony Li & Jack Tang
[CB16] (P)FACE :アップルのコアへ、そしてルート権限へのエクスプロイト by Moony Li & Jack Tang
CODE BLUE
 
[A31]AWS上でOracleを利用するためのはじめの一歩!by Masatoshi Yoshida
[A31]AWS上でOracleを利用するためのはじめの一歩!by Masatoshi Yoshida[A31]AWS上でOracleを利用するためのはじめの一歩!by Masatoshi Yoshida
[A31]AWS上でOracleを利用するためのはじめの一歩!by Masatoshi Yoshida
Insight Technology, Inc.
 

Similar to 100%Kotlin ORM Ktormを試してみた (20)

Parquetはカラムナなのか?
Parquetはカラムナなのか?Parquetはカラムナなのか?
Parquetはカラムナなのか?
 
PostgreSQL 12の話
PostgreSQL 12の話PostgreSQL 12の話
PostgreSQL 12の話
 
監査要件を有するシステムに対する PostgreSQL 導入の課題と可能性
監査要件を有するシステムに対する PostgreSQL 導入の課題と可能性監査要件を有するシステムに対する PostgreSQL 導入の課題と可能性
監査要件を有するシステムに対する PostgreSQL 導入の課題と可能性
 
Introduction of Oracle Database Architecture
Introduction of Oracle Database ArchitectureIntroduction of Oracle Database Architecture
Introduction of Oracle Database Architecture
 
T sql の parse と generator
T sql の parse と generatorT sql の parse と generator
T sql の parse と generator
 
HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLHandlerSocket plugin for MySQL
HandlerSocket plugin for MySQL
 
【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ
 
[ウェビナー] Build 2018 アップデート ~ データ プラットフォーム/IoT編 ~
[ウェビナー] Build 2018 アップデート ~ データ プラットフォーム/IoT編 ~[ウェビナー] Build 2018 アップデート ~ データ プラットフォーム/IoT編 ~
[ウェビナー] Build 2018 アップデート ~ データ プラットフォーム/IoT編 ~
 
AWS で Presto を徹底的に使いこなすワザ
AWS で Presto を徹底的に使いこなすワザAWS で Presto を徹底的に使いこなすワザ
AWS で Presto を徹底的に使いこなすワザ
 
ServerlessConf Tokyo2018 サーバーレスなシステムのがんばらない運用監視
ServerlessConf Tokyo2018 サーバーレスなシステムのがんばらない運用監視ServerlessConf Tokyo2018 サーバーレスなシステムのがんばらない運用監視
ServerlessConf Tokyo2018 サーバーレスなシステムのがんばらない運用監視
 
TreeFrog Frameworkの紹介
TreeFrog Frameworkの紹介TreeFrog Frameworkの紹介
TreeFrog Frameworkの紹介
 
Apache Torqueについて
Apache TorqueについてApache Torqueについて
Apache Torqueについて
 
Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用
 
CS-CART addon
CS-CART addonCS-CART addon
CS-CART addon
 
[CB16] (P)FACE :アップルのコアへ、そしてルート権限へのエクスプロイト by Moony Li & Jack Tang
[CB16] (P)FACE :アップルのコアへ、そしてルート権限へのエクスプロイト by Moony Li & Jack Tang[CB16] (P)FACE :アップルのコアへ、そしてルート権限へのエクスプロイト by Moony Li & Jack Tang
[CB16] (P)FACE :アップルのコアへ、そしてルート権限へのエクスプロイト by Moony Li & Jack Tang
 
[A31]AWS上でOracleを利用するためのはじめの一歩!by Masatoshi Yoshida
[A31]AWS上でOracleを利用するためのはじめの一歩!by Masatoshi Yoshida[A31]AWS上でOracleを利用するためのはじめの一歩!by Masatoshi Yoshida
[A31]AWS上でOracleを利用するためのはじめの一歩!by Masatoshi Yoshida
 
JAWS DAYS 2018 | IoT時代におけるデバイスのファームウェアとクラウドのいい関係
JAWS DAYS 2018 | IoT時代におけるデバイスのファームウェアとクラウドのいい関係JAWS DAYS 2018 | IoT時代におけるデバイスのファームウェアとクラウドのいい関係
JAWS DAYS 2018 | IoT時代におけるデバイスのファームウェアとクラウドのいい関係
 
GoodBye AD FS - Azure Active Directory Only の認証方式へ切り替えよう!
GoodBye AD FS - Azure Active Directory Only の認証方式へ切り替えよう!GoodBye AD FS - Azure Active Directory Only の認証方式へ切り替えよう!
GoodBye AD FS - Azure Active Directory Only の認証方式へ切り替えよう!
 
The overview of Server-ide Bulk Loader
 The overview of Server-ide Bulk Loader The overview of Server-ide Bulk Loader
The overview of Server-ide Bulk Loader
 
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
Cisco Modeling Labs (CML)を使ってネットワークを学ぼう!(DevNet編)
 

100%Kotlin ORM Ktormを試してみた