Upgrade to Pro — share decks privately, control downloads, hide ads and more …

イマドキのKotlin 2018

イマドキのKotlin 2018

Taro Nagasawa

January 25, 2018
Tweet

More Decks by Taro Nagasawa

Other Decks in Programming

Transcript

  1. Kotlinとは • Java仮想マシンをターゲットとしたプログラミング言語 ◦ AndroidやJavaScript, LLVMもサポート • IntelliJ IDEAでおなじみのJetBrainsが開発 •

    2011年に発表され、2016年2月にver1.0がリリース • 静的型付けオブジェクト指向言語 • 簡潔、安全、Javaとの相互運用性 • 拡張関数やデータクラス、Null安全などの便利機能
  2. もくじ 1. Kotlin 1.1 2. KotlinConf 2017 3. Kotlin 1.2

    4. KotlinでAndroid 5. KotlinでWeb
  3. Kotlin 1.1で導入・改善された文法や機能 • コルーチン • 型エイリアス • メンバ参照(bound callable reference)

    • ローカル変数で委譲プロパティ • ラムダ式の引数を分解 • 数値のアンスコ区切り • 不要な変数を捨てる • データクラスで継承可能に • シールドクラスを同一ファイル内で継承可能に • alsoやtakeIfなどが標準ライブラリに追加
  4. コルーチン • 実験的にKotlinに導入されているAPI・機能 ◦ experimentalとはいえ、本番導入怖くない • 関数を途中で抜けたり、続きから始めたりできる • async/await→非同期処理を同期的に記述できる ◦

    コールバック地獄からの解放 suspend fun getMyUserId(): Long = ... suspend fun findFriends(id: Long): List<User> = ... button.onClickListener { launch(UI) { val id = getMyUserId() val friends = findFriends(id) friendsView.addFriends(friends) } }
  5. コルーチン • 実験的にKotlinに導入されているAPI・機能 ◦ experimentalとはいえ、本番導入怖くない • 関数を途中で抜けたり、続きから始めたりできる • async/await→非同期処理を同期的に記述できる ◦

    コールバック地獄からの解放 suspend fun getMyUserId(): Long = ... suspend fun findFriends(id: Long): List<User> = ... button.onClickListener { launch(UI) { val id = getMyUserId() val friends = findFriends(id) friendsView.addFriends(friends) } }
  6. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • ValueObject的なことはデータクラスで typealias EventListener

    = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String)
  7. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • ValueObject的なことはデータクラスで typealias EventListener

    = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String) 型の組み合わせに名前を付けるイメージ
  8. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • ValueObject的なことはデータクラスで typealias EventListener

    = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String) EventListenerに(Event) -> Unit オブジェクトを代入可能
  9. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • ValueObject的なことはデータクラスで typealias EventListener

    = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String) 簡単に代入ミスは起こる val city = "Tokyo" User(city)
  10. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • ValueObject的なことはデータクラスで typealias EventListener

    = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String) 型のご加護が val city = "Tokyo" User(city) //型不一致
  11. メンバ参照 (bound callable reference) • メンバ(メソッドやプロパティ)を、任意のオブジェクトと結びつけ て参照することができる、関数オブジェクト化することができる class UserService {

    private val log = LoggerFactory.getLogger("TEST") private fun log(users: List<User>) { users.map(User::toJson).forEach(log::info) } ... } オブジェクトを結びつけていない参照 以前よりサポート
  12. メンバ参照 (bound callable reference) • メンバ(メソッドやプロパティ)を、任意のオブジェクトと結びつけ て参照することができる、関数オブジェクト化することができる class UserService {

    private val log = LoggerFactory.getLogger("TEST") private fun log(users: List<User>) { users.map(User::toJson).forEach(log::info) } ... } オブジェクト(log)を結びつけた参照 ver 1.1よりサポート
  13. 実装だけでなく宣言も共有可能 // Common module expect fun hello() // JVM module

    actual fun hello() { println("Hello, JVM!") } // JS module actual fun hello() { println("Hello, JS!") }
  14. 実装だけでなく宣言も共有可能 // Common module expect fun hello() // JVM module

    actual fun hello() { println("Hello, JVM!") } // JS module actual fun hello() { println("Hello, JS!") }
  15. 可変長引数と配列リテラル @Bar(["hoge"]) annotation class Bar(vararg val value: String) @Bar(value =

    ["hoge"]) @Bar("hoge") @Bar(value = "hoge") @Bar(*["hoge"]) OK! Compilation error OK! OK! Deprecated
  16. bound callabel referenceの改善 object Foo { fun square(n: Int) =

    n * n fun squareOf4() = (this::square)(4) fun squareOf5() = 5.let(this::square) } object Foo { fun square(n: Int) = n * n fun squareOf4() = (::square)(4) fun squareOf5() = 5.let(::square) }
  17. class Foo { companion object { val name: String =

    "Foo" } } Foo.name //=> Foo コンパニオンオブジェクト コンパニオンオブジェクト
  18. class Foo { companion object { val name: String =

    "Foo" } } Foo.name //=> Foo コンパニオンオブジェクト staticっぽい! フィールドっぽい! →でも違う
  19. class Foo { companion object { @JvmField val name: String

    = "Foo" } } JVMと仲良くするアノテーション staticフィールドとして見えるようになる
  20. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } }
  21. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } インタフェースが1つとその実装が2つある
  22. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } Human has a Greeter
  23. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } }
  24. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } 型と実装をバインドする 同一の型であっても名前で区別可能
  25. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } 実装を注入する度に呼び出される
  26. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } シングルトン
  27. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } 実装をそれに依存しない形で注入
  28. 依存の注入 class MainActivity: AppCompatActivity() { private val injector = KodeinInjector()

    private val speaker: Speaker by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } }
  29. 依存の注入 class MainActivity: AppCompatActivity() { private val injector = KodeinInjector()

    private val speaker: Speaker by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } } おまじない
  30. 依存の注入 class MainActivity: AppCompatActivity() { private val injector = KodeinInjector()

    private val speaker: Speaker by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } } 委譲プロパティによる注入
  31. 依存の注入 class MainActivity: AppCompatActivity() { private val injector = KodeinInjector()

    private val speaker: Speaker by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } } 注入された実装を使うことができる
  32. 依存の注入 class MainActivity: AppCompatActivity(), KodeinInjected { override val injector =

    KodeinInjector() private val speaker: Speaker by instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } } これを実装すると ちょっとだけ記述量が減る!!
  33. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } }
  34. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } }
  35. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } 毎回生成されるやつ 名前を付けることも可能
  36. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } シングルトン もちろん名前を付けられる
  37. 依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter:

    Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } 実装をそれに依存しない形で注入
  38. 依存の注入 class MainActivity: AppCompatActivity() { private val speaker: Speaker by

    inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... button.setOnClickListener { speaker.speak() } } }
  39. 依存の注入 class MainActivity: AppCompatActivity() { private val speaker: Speaker by

    inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... button.setOnClickListener { speaker.speak() } } } 委譲プロパティで注入
  40. Kotlin x Spring • Spring Boot 2 (Spring Framework 5)から、Kotlinフレンド

    リ感がさらにアップ! ◦ Spring WebFluxの追加 ▪ ノンブロッキングでリアクティブなWebフレームワーク ▪ 従来のアノテーションによるハンドラの宣言に加え ▪ ラムダを使用したルーティング ◦ ラムダを使用したBean登録 ◦ KotlinフレンドリなAPI群の追加 • コルーチンの活用
  41. 従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }
  42. 従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }
  43. 従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }
  44. 従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }
  45. 従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }
  46. 従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }
  47. WebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')
  48. WebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')
  49. @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono<String>

    = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } WebFlux // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')
  50. @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono<String>

    = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } WebFlux // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')
  51. @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono<String>

    = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } WebFlux // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')
  52. @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono<String>

    = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } WebFlux // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')
  53. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'
  54. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'
  55. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'
  56. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'
  57. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'
  58. ハンドラメソッドをサスペンドに @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") suspend fun

    handle():String { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } return c.await().answer } } // dependencies compile 'org.springframework.kotlin:spring-webflux-kotlin-coroutine:0.3.1'
  59. ハンドラメソッドをサスペンドに @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") suspend fun

    handle():String { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } return c.await().answer } } // dependencies compile 'org.springframework.kotlin:spring-webflux-kotlin-coroutine:0.3.1'
  60. Exposed • Kotlin SQLライブラリ ◦ SQL DSL ◦ Data Access

    Object • JetBrainsが開発 https://github.com/JetBrains/Exposed • 有名なDBMSに対応: ポスグレ, MySQL, Oracle, ...
  61. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } }
  62. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } テーブル定義
  63. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } DBに接続
  64. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } トランザクション
  65. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } テーブル作成 テーブル削除
  66. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } インサート
  67. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } 取得
  68. 一対多 関連 object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } object Tasks: Table() { val id = long("id").autoIncrement().primaryKey() val content = text("content") val completedAt = datetime("completed_at").nullable() val teamId = long("team_id") references Teams.id }
  69. 一対多 関連 object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } object Tasks: Table() { val id = long("id").autoIncrement().primaryKey() val content = text("content") val completedAt = datetime("completed_at").nullable() val teamId = long("team_id") references Teams.id } 外部キー設定
  70. joinとprojectionとwhere Tasks.innerJoin(Teams) .slice(Team.name, Tasks.content) .select { Tasks.completedAt.isNull() } .forEach {

    row -> println("${row[Teams.name]}: ${row[Tasks.content]}") } id name 1 Kotlin id content completed_at team_id 1 release 1.1.60 2017-11-14T03:00+09:00 1 2 release 1.2.0 (null) 1 Teams Tasks Teams.name Tasks.content Kotlin release 1.2.0
  71. Data Access Object object Teams: LongIdTable() { val name =

    varchar("name", length = 100) } class Team(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Team.new { name = "Kotlin" } Team.all().forEach { team: Team -> println("id=${team.id}, name=${team.name}") } } }
  72. Data Access Object object Teams: LongIdTable() { val name =

    varchar("name", length = 100) } class Team(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Team.new { name = "Kotlin" } Team.all().forEach { team: Team -> println("id=${team.id}, name=${team.name}") } } }
  73. Data Access Object object Teams: LongIdTable() { val name =

    varchar("name", length = 100) } class Team(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name } fun main(args: Array<String>) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Team.new { name = "Kotlin" } Team.all().forEach { team: Team -> println("id=${team.id}, name=${team.name}") } } }
  74. 一対多 関連 object Teams: LongIdTable() { val name = varchar("name",

    length = 100) } object Tasks: LongIdTable() { val content: text("content") val completedAt: datetime("completed_at").nullable() val team = reference("team", Teams) } class Team(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name val tasks by Tasks.referrersOn(Tasks.team) } class Task(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Task>(Tasks) var content by Tasks.content var completedAt by Tasks.completedAt var team by Team.referencedOn(Tasks.team) }
  75. 一対多 関連 object Teams: LongIdTable() { val name = varchar("name",

    length = 100) } object Tasks: LongIdTable() { val content: text("content") val completedAt: datetime("completed_at").nullable() val team = reference("team", Teams) } class Team(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name val tasks by Tasks.referrersOn(Tasks.team) } class Task(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Task>(Tasks) var content by Tasks.content var completedAt by Tasks.completedAt var team by Team.referencedOn(Tasks.team) } 関連を定義
  76. 取得 Task .find { Tasks.completedAt.isNull() } .forEach { task: Task

    -> val team: Team = task.team println("${team.name}: ${task.content}") }
  77. 取得 Task .find { Tasks.completedAt.isNull() } .forEach { task: Task

    -> val team: Team = task.team println("${team.name}: ${task.content}") } N + 1 のおそれ
  78. 無理やりまとめると • コルーチンすごい ◦ Androidはもちろん、サーバサイド(WebFluxなど)でも便利に使える • サーバサイドでもKotlin ◦ KotlinConfでのセッション、多い ◦

    Spring ♡ Kotlin ◦ Exposed...JetBrainsによるKotlin製ORM ◦ (Ktor...JetBrainsによるKotlin製Webマイクロフレームワーク) • 3rd party製 Kotlinライブラリ、フレームワーク ◦ KODEIN ◦ KOIN