Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

100%Kotlin ORM Ktormを試してみた

「Server-Side Kotlin Study #1」の発表資料です
https://server-sider-kotlin.connpass.com/event/210853/

  • Be the first to comment

  • Be the first to like this

100%Kotlin ORM Ktormを試してみた

  1. 1. 100%KotlinのORM Ktormを試してみた (Exposedとの⽐較アリ) Server-Side Kotlin Study #1 2021/05/12 1
  2. 2. ⾃⼰紹介 塚本啓太 株式会社イエソドエンジニア KotlinとTypeScriptが⼤好きだけど、 たまにvalとconstを間違える。 Twitter: @tsukakei1012 GitHub: tsukakei 2
  3. 3. 会社紹介 株式会社イエソド SaaS統制プラットフォーム 『YESOD』 ⼈・組織・情報と SaaSをシームレスに 管理するためのSaaS https://www.yesod.co/ KotlinとTypeScriptで できています 3
  4. 4. 今⽇話すこと Ktormの紹介 Exposedとの⽐較 4
  5. 5. とは 100%Kotlinの軽量ORM ConfigファイルもXMLもサードパーティ依存もなく軽量で使いやす い 強く型付けされたSQL DSL 柔軟なクエリ、適度な粒度で思った通りのSQLが書ける filter や map のようなKotlin nativeの感覚で書けるAPIを提供 拡張可能なデザイン https://www.ktorm.org/ 5
  6. 6. あれ、でもExposedは?? 6
  7. 7. とは JetBrains製の100% Kotlin ORM JetBrains製という絶対正義感 アイコンは Ktorm同様に軽量さと型付けを特徴としている https://github.com/JetBrains/Exposed Google TrendsではExposedの圧勝 https://kotlin.libhunt.com/compare-exposed-vs-ktorm 7
  8. 8. せっかくなので⽐較してみましょう レポジトリを⽤意しました https://github.com/tsukakei/KotlinOrmSample 8
  9. 9. ダウンロード⽅法 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
  10. 10. 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
  11. 11. DSLの⽐較 テーブル定義 各種SQL SELECT AGGREGATION, UNION, JOIN INSERT, UPDATE, DELETE レポジトリ https://github.com/tsukakei/KotlinOrmSample 11
  12. 12. テーブル定義(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
  13. 13. テーブル定義(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
  14. 14. テーブル定義(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
  15. 15. 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
  16. 16. 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
  17. 17. 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
  18. 18. 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
  19. 19. 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
  20. 20. 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
  21. 21. DSLまとめ KtormもExposedもメソッドチェイニングでスルスルと書ける Ktormの⽅がよりSQLライク Exposedはやりたいことをしたいときにドキュメントを読む必要が あるように感じた 21
  22. 22. DAO Entity定義 CRUD レポジトリ https://github.com/tsukakei/KotlinOrmSample 22
  23. 23. 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
  24. 24. 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
  25. 25. 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
  26. 26. 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
  27. 27. DAOまとめ Ktormは少しDatabaseを意識しないといけない その分、少し冗⻑に感じた TableとEntityの扱い⽅が微妙に違う ExposedはTable定義が先、Entityが後 KtormはEntityが先、Tableが後 27
  28. 28. KtormとExposedの⽐較まとめ 基本的な機能や型サポートについては⼤差ないように感じた 互いにMySQL・PostgreSQL・Oracle・SQL Serverをサポート 書き⽅やドキュメントについてはKtormの⽅が好み しかしながら、JetBrains開発という安⼼感がExposedにはある Exposed Ktorm 開発元 JetBrains 個⼈開発者(@vincentlauvlwj) 数 5.1K 1K弱 採⽤実績 YESOD, UPSIDER 皆無? 28
  29. 29. We're hiring! モダンな静的型付け⾔語で B2B SaaSを開発したい エンジニア募集! https://www.wantedly.com/pr ojects/631289 Kotlin (Ktor + Exposed) 使ってます!! 29

×