逆引きRoom的ななにか(使ったところを加筆中)

# Entityクラスのなにか
## 宣言時の設定など
```kotlin
@Entity(tableName = "hoge_hoge", // Table名
		  Index[] indices = [], // index
        foreignKeys = [ForeignKey( // 外部キー制約
                entity = SuperHoge::class, // 親のEntityクラスを指定
                String[] primaryKeys = [], // primaryKey指定
                parentColumns = arrayOf("id"), // 親Entityの対応するカラム
                childColumns = arrayOf("super_hoge_id"), // 自分の対応するカラム
                onDelete = ForeignKey.CASCADE) // Deleteされたときの動作
        ])
class HogeHogeEntity(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id", index = true)
    var id: Long = 0,
    @ColumnInfo(name = "username")
    var username: String?
    @ColumnInfo(name = "order")
    var order: Int?
)
```

- index指定はカラムに直接アノテーションで指定できるので自分はそっちを使ってます。

## POJO以外のData型を使うとき
- コンバーター使う
- RoomDatabase継承クラスのとこで以下のように指定

```kotlin
@TypeConverters(DateTypeConverter::class, StringListTypeConverter::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun myModelDao(): MyModelDao
}
```

- Entityクラスで直接カラムを指定して書ける

```kotlin
@Entity(tableName = "hoge")
class HogeEntity(
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "id", index = true)
        var id: Long,
		  @ColumnInfo(name = "tag_names")
        @TypeConverters(StringListTypeConverter::class) // Converterの指定
        var tagNames: List<String>?
)
```

### Stringの配列

```kotlin
class StringListTypeConverter {
    @TypeConverter
    fun fromStringList(strings: List<String>?): String? {
        if (strings == null) {
            return null
        }

        val result = StringWriter()
        val json = JsonWriter(result)

        try {
            json.beginArray()

            strings.forEach {
                json.value(it)
            }

            json.endArray()
            json.close()
        } catch (e: IOException) {
            Crashlytics.logException(e)
        }

        return result.toString()
    }

    @TypeConverter
    fun toStringList(strings: String?): List<String>? {
        if (strings == null) {
            return null
        }

        val reader = StringReader(strings)
        val json = JsonReader(reader)
        val result = mutableListOf<String>()

        try {
            json.beginArray()

            while (json.hasNext()) {
                result.add(json.nextString())
            }

            json.endArray()
        } catch (e: IOException) {
            Crashlytics.logException(e)
        }

        return result
    }
}
```

### Date型
```kotlin
class DateTypeConverter {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return if (value == null) null else Date(value)
    }

    @TypeConverter
    fun toTimestamp(date: Date?): Long? {
        return date?.*time*
    }
}
```

### Enum型
```kotlin
class HogeStatusConverter {
    @TypeConverter
    fun fromString(typeString: String): HogeStatus {
        return BookshelfStatus.valueOf(typeString)
    }

    @TypeConverter
    fun fromStatus(status: HogeStatus): String {
        return status.toString()
    }
}

```

- ハマったとこ
enum class 側で値に `@SerializedName(“in_progress”)` みたいに指定してるとうまくいかない。そのときは `valueOf()` や `toString()` しているところを切り替える

## 構造体？を内包する
- `@Embeded`を使う

```kotlin
class EmbededHoge {
    var id: Long
    var content: String
}
```
```kotlin
@Entity(tableName = "hoge")
class HogeEntity(
   ~~~~~~省略~~~~~
    @Embeded(prefix = "prefix_")
    var eHoge : EmbededHoge
)
```

これで `EmbededHoge` 構造体が `eHoge` にマッピングされます。  
Relationalなマッピングではなく、あくまでもHogeEntity内にEmbededHogeのカラムが `prefix_id` や `prefix_ content` といった形で追加されます。


# Daoクラスのなにか
## 宣言時の設定など
```kotlin

@Dao
interface HogeDao {
    /* ===== SELECT ===== */
    @Query("select * from hoge order by order asc")
    fun getAll(): List<Hoge>

    @Query("select * from hoge where username=:username order by order asc, username asc")
    fun findByUserName(username: String): List<Hoge>

    /* ===== INSERT ===== */
    @Insert(onConflict = OnConflictStrategy.ROLLBACK) // コンフリクト時の設定
    fun insert(entity: HogeEntity)

    @Insert(onConflict = OnConflictStrategy.ABORT)
    fun insertAll(hogeEntities: List<HogeEntity>)

    /* ===== UPDATE ===== */
    @Update
    fun update(entity: HogeEntity)

    @Query("update hoge set order=:order where id=:id")
    fun updateOrder(id: Long, order: Int)

    /* ===== DELETE ===== */
    @Delete
    fun delete(entity: HogeEntity)

    @Query("delete from hoge where id in (:ids)")
    fun deleteInCategoryIds(ids: List<Long>)
}
```

- 基本的には見たまま
- `username=:username` みたいに変数は `:hogehoge` と書く
- 配列のinsertやらdeleteもいける。primaryKeyないテーブルはとうまくいかない(と思う)

### ちょっと複雑なことは基本 `@Query` で普通にsql書く
- ただし、`LiveData` などで変更通知を監視しているときは注意。
`UPDATE/DELETE/INSERT`  をトリガーとしているのでQueryで更新系書くと変更通知されない

- `where in` もいける。List 渡して `where id in (:ids)` みたいに書く

## UpSertしたいとき
`@Insert(onConflict = OnConflictStrategy.REPLACE)` をinsertメソッドに追記。
レコードが存在したら置き換え、存在しなければインサートしてくれる。

- 注意
以下のように外部キー制約が設定されているとHogeParent側でリプレースされたときにCASCADEでHogeEntityが消える(当たり前だけど

```kotlin
@Entity(tableName = "hoge",
	foreignKeys = [ForeignKey(
        entity = HogeParent::class,
        parentColumns = arrayOf("id"),
        childColumns = arrayOf("parent_id"),
        onDelete = ForeignKey.CASCADE)
])
class HogeEntity {
	~~~~~~~~~
}
```

# Databaseクラスのなにか
## 宣言時の設定など
```kotlin
@Database(
        entities = [ // Entityクラスをここに記載
            HogeEntity::class,
            FugaEntity::class
        ],
        version = 1, // Versionを記述。変わる場合は必要に応じてmigrateを書く
        exportSchema = false // schemaのExportを出力するか
) 
@TypeConverters(DateTypeConverter::class, StringListTypeConverter::class) // Converter
abstract class AppDatabase : RoomDatabase() {
    abstract fun hogeDao(): HogeDao // Daoを記載
    abstract fun fugaDao(): FugaDao
}
```

## exportSchema について
- ` = true` にしておくとapp/schemasの下にschemaのjsonを吐いてくれるらしい
- Gradleファイルにも以下のような設定が必要  
※どこかで見て参考にさせていただきました。リソース明記できなくてすいません。

```build.gradle
defaultConfig {
    ~~~~~~~~~~省略~~~~~~~~~~~
    javaCompileOptions {
        annotationProcessorOptions {
            arguments += [“room.schemaLocation”:”$projectDir/schemas”.toString()]
        }
    }
}
```

- 他にもMigration Testのときに必要になってくるらしい(まだやってない)