エラーに直面した：android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY[787])

表題のエラーに直面して解決まで辿り着いたので共有します。

# 環境
Android Studio Hedgehog | 2023.1.1 Patch 2

# 経緯

Androidアプリの開発の中で、Roomを実装中にこのエラーに直面しました。

## クラッシュした箇所

```Kotlin

suspend fun operate() {
        getCityCodeJsonDataSource.fetchResponseAsFlow().collect { list ->
list.forEachIndexed { i, data ->

                val prefecture = Prefecture(
                    i,
                    data.prefectureCode!!,
                    data.prefectureName!!
                )

                //logをはくメソッド
                footprint("prefecture: $prefecture")
                MainApplication().appDatabase.prefectureAndCityDao().upsertPrefecture(prefecture)

                val cities = data.municipals
                cities?.forEachIndexed { j, city ->
                    val c = City(
                        j,
                        city.code,
                        city.name,
                        city.kana,
                        city.isDisabled,
                        i
                    )

                    //logをはくメソッド
                    footprint("city: $c")
                    //***ここでクラッシュ(android.database.sqlite.SQLiteConstraintException...)***
                    MainApplication().appDatabase.prefectureAndCityDao().upsertCity(c)
                }

            }
    

    }

```

## 原因

端的にいうと「『親』の準備が完了する前に『子』のデータを入れるな！」ということのようです。
実は下記のように２つのモデルは親子関係にあります。

```Kotlin

import androidx.room.ColumnInfo
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import androidx.room.Relation

@Entity(tableName = "prefecture")
data class Prefecture(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    @ColumnInfo(name = "prefecture_name")
    val prefectureName: String,
    @ColumnInfo(name = "prefecture_code")
    val prefectureCode: String,
)

@Entity(
    tableName = "city",
    foreignKeys = [ForeignKey(
        entity = Prefecture::class,
        parentColumns = arrayOf("id"),
        childColumns = arrayOf("p_id"),
        onDelete = ForeignKey.CASCADE
    )]
)
data class City(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "c_id")
    val cId: Int,
    val code: String?,
    val name: String?,
    @ColumnInfo(name = "name_kana")
    val nameKana: String?,
    @ColumnInfo(name = "prefecture_code")
    val isDisabled: Boolean?,
    @ColumnInfo(name = "p_id")
    val pId: Int
)

data class PrefectureAndCity(
    @Embedded
    val prefecture: Prefecture,
    @Relation(
        parentColumn = "id",
        entityColumn = "p_id"
    )
    val cities: List<City>
)

```

## 解法

以上のことを踏まえると、データを入れる順番に問題があったことになるので、下記のように親（Prefecture）のデータを全て入れ終わった後に子（City）を入れてあげると上手くいきます。

```Kotlin

suspend fun operate() {
        getCityCodeJsonDataSource.fetchResponseAsFlow().collect { list ->

            list.forEachIndexed { i, data ->

                val prefecture = Prefecture(
                    i + 1, data.prefectureCode!!, data.prefectureName!!
                )

                footprint("prefecture: $prefecture")
                MainApplication().appDatabase.prefectureAndCityDao().upsertPrefecture(prefecture)

            }

            list.forEachIndexed { i, data ->

                val cities = data.municipals
                cities?.forEachIndexed { j, city ->
                    val c = City(
                        j + 1,
                        city.code,
                        city.name,
                        city.kana,
                        city.isDisabled,
                        i + 1
                    )
                    footprint("city: $c")
                    MainApplication().appDatabase.prefectureAndCityDao().upsertCity(c)
                }

            }

        }
    }

```