Kotlin Exposedでデータ取得（Kotlin × Spring Boot

## 前提
言語はkotlin
DBはPostgres
org.jetbrains.exposedの0.38.2を利用。

https://github.com/JetBrains/Exposed

以下の構成を想定しての実装解説となります
![スクリーンショット 2022-11-30 20.33.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323088/7cb3636c-1cb6-3b44-0bb6-aa7ce9ec57a6.png)

エンティティ周りのクラス構成についてはこちらの記事のものを利用してますのでこの機会にぜひこちらの記事もよろしくお願いします
https://qiita.com/yuya_sega/items/0d7cb7ae7ce531fb9eba

```kotlin
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.jodatime.date
import org.jetbrains.exposed.sql.jodatime.datetime

object Users: IntIdTable("users") {
    var name = varchar("name", 50)
    var address = varchar("address", 255).nullable()
    var isActive = bool("is_active").default(false)
    var enteredOn = date("entered_on")
    var createdAt = datetime("created_at")
}
```
```kotlin
import org.jetbrains.exposed.dao.id.IntIdTable

object Orders: IntIdTable("orders") {
    var user = reference("user_id", Users)
    var code = integer("code")
}
```
```kotlin
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID

class User(
        id: EntityID<Int>
): IntEntity(id) {
    companion object: IntEntityClass<User>(Users)
    var name by Users.name
    var address by Users.address
    var isActive by Users.isActive
    var enteredOn by Users.enteredOn
    var createdAt by Users.createdAt
}
```

```kotlin
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID

class Order(
        id: EntityID<Int>
): IntEntity(id) {
    companion object: IntEntityClass<Order>(Orders)
    var user by User referencedOn Orders.user
    var code by Orders.code
}
```

## select
### 1件引き
```kotlin
// primary keyによる1件引き
val user: User? = User.findById(1)

// その他キーによる1件引き
val user: User? = User.find {
    Users.name eq "テスト太郎"
}.singleOrNull()
```
### 複数件引き
```kotlin
// 全件引き
User.all()
// List<User>で取るにはtoListします
User.all().toList()
// 条件の指定方法は1件引きと同じ
User.find{
    Users.name eq "テスト太郎"
}.toList()
```
## insert
```kotlin
// DAOパターン
User.new {
    this.name = "テスト太郎"
    this.enteredOn = org.joda.time.DateTime.now()
}

// DSL
Users.insert {
    it[Users.name] = "テスト太郎"
    it[Users.enteredOn] = org.joda.time.DateTime.now()
} get Users.id
```
## update
```kotlin
// 事前準備
var user = User.new {
    this.name = "テスト太郎"
    this.enteredOn = org.joda.time.DateTime.now()
}
// DAO update
user = user.also {
    it.name = "テスト次郎"
}
// DSL update
Users.update({ Users.id eq user.id}) {
    it[Users.name] = "テスト次郎"
}
```
## delete
```kotlin
User.findById(1).delete()
```

## inner join
join先のデータをwhere条件に指定したいときに利用しました
```kotlin
Order.wrapRows(
    Orders.innerJoin(Users).select {
        Orders.code eq "test"
    }
)
.with(Order::user)
```
## left join
inner joinと書き方変わらないです
```kotlin
Order.wrapRows(
    Orders.leftJoin(Users).select {
        Orders.code eq "test"
    }
)
.with(Order::user)
```
## referencedOnをメンバとして持っている場合のjoin
withを使わない場合、フィールドアクセス時にSQLが実行されるのでパフォーマンス意識する場合はwith使ってあらかじめデータ取得しておくことをおすすめします
```kotlin
Order.find{
    Orders.code eq "test"
}
.with(Order::user)

```

## and
```kotlin
val param: Op<Boolean> = Op.build {
    (User.name eq "テスト太郎") and
    (User.isActive eq true)
}
User.find(param)
```
## or
```kotlin
val param: Op<Boolean> = Op.build {
    (User.name eq "テスト太郎") or
    (User.isActive eq true)
}
User.find(param)
```

ちなみにandとorの混合はこんなふうに書きました
```kotlin
val param: Op<Boolean> = Op.build {
    (User.name eq "テスト太郎") and
    Op.build {(User.isActive eq true) or (User.address.isNull())}
}
User.find(param)
```
## in
```kotlin
val names: List<String> = listOf("テスト太郎", "テスト次郎", "テスト三郎")
User.find {
    Users.name.inList(names)
}
```

## lessEq / greaterEq
```kotlin
val enteredOn = org.joda.time.DateTime.now()
// greaterEq
User.find {
    Users.enteredOn greaterEq enteredOn
}
// lessEq
User.find {
    Users.enteredOn lessEq enteredOn
}
```

## is null / is not null
```kotlin
// isNull
User.find {
    Users.address.isNull()
}
// isNotNull
User.find {
    Users.address.isNotNull()
}
```

## order by
```kotlin
User.all().orderBy(Users.id to SortOrder.ASC)
```

## limit
```kotlin
User.all().limit(1)
```