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

イマドキのKotlin 2018

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

イマドキのKotlin 2018

Avatar for Taro Nagasawa

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