【Android/Kotlin】最小構成でRoomを使う

# 環境
- Mac
- AndroidStudio4.1
- Kotlin

# 前提知識
- RDB
- SQL
- [ViewModel](https://qiita.com/yuki-kamikita/items/4cc2959aeca6da3b8d1b)
- [LiveData](https://qiita.com/yuki-kamikita/items/ab687a8128eae292ead6)

# 結論
[ソースコード全文 - Github](https://github.com/yuki-kamikita/RoomSample)
[AndroidStudioでGitHubを使う - Qiita](https://qiita.com/yuki-kamikita/items/26b17f45a8505e5928b6)

- Entity、DAO、Databaseの３つで構成される
- それぞれの内容は下記

#### Entity
テーブルの定義

#### DAO（Data Access Object）
クエリの作成

#### Database
どのEntity（テーブル）とDao（クエリ）を使うか指定


# コード解説
#### build.gradle
バージョンとか変わってると思うので、動かなかったら
[Room を使用してローカル データベースにデータを保存する - Android Developers](https://developer.android.com/training/data-storage/room?hl=ja)
からコピペした方が良さそう

```xml:build.gradle(app)
dependencies {
    // ViewModel
    implementation "androidx.activity:activity-ktx:1.1.0" // Activityから使う時

    // LiveData
    def lifecycle_version = '2.0.0'
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"

    // room
    def room_version = "2.2.5"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
}
```

#### Entity
テーブルやカラムの指定
`autoGenerate`にしとくと自動で最小の数字を入れてくれる

```kotlin:Entity.kt
@Entity(tableName = "table_name")
data class SampleEntity(
    @PrimaryKey(autoGenerate = true) val id: Int, // autoGenerate = true でAutoIncrement
    @ColumnInfo(name = "word") val word: String?
)
```

#### Dao
SQL文を作成

```kotlin:Dao.kt
@Dao
interface SampleDao {
    @Query("SELECT * FROM table_name")
    fun loadLiveData(): LiveData<List<SampleEntity>>

    @Insert
    fun insert(vararg users: SampleEntity)

    @Query("DELETE FROM table_name")
    fun deleteAll()
}
```

#### Database
どのEntityとDAOを使うか指定

```kotlin:Database.kt
@Database(entities = [SampleEntity::class], version = 1) // 使用するEntityを指定
abstract class SampleDatabase : RoomDatabase() {
    // 使用するDaoを指定
    abstract fun sampleDao(): SampleDao

    // データベースのビルド
    // 書く場所はここじゃなくてもいい
    companion object {
        fun buildDatabase(context: Context): SampleDatabase {
            return Room.databaseBuilder(
                    context,
                    SampleDatabase::class.java, "database-name"
            ).build()
        }
    }

}
```

#### MainActivity
ボタンをクリックしたらDBに保存したり削除したりする

```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val viewModel: SampleViewModel by viewModels()


        // Roomの変更を検知し、変更時にtextViewの内容を変更する
        viewModel.wordLiveData.observe(this, Observer { value ->
            value?.let { textView.text = it.toString() }
        })

        // データ挿入
        buttonInsert.setOnClickListener {
            viewModel.insert(wordBox.text.toString())
        }

        // 全削除
        buttonDelete.setOnClickListener {
            viewModel.deleteAll()
        }
    }

}
```

#### ViewModel
Activity → ViewModel → Daoの順でアクセスしている
DBのビルド（どのDBを使うよーって宣言する）を一度だけ行いたいので、Activityと分けて記述

```kotlin:SampleViewModel
class SampleViewModel(application: Application) : AndroidViewModel(application) {

    private val dao: SampleDao
    init {
        val db = SampleDatabase.buildDatabase(application) // DBにアクセスするclassで一度だけDBをビルドする
        dao = db.sampleDao() // 使用するDaoを指定
    }

    // DBに保管された内容を表示
    val wordLiveData = dao.loadLiveData()

    // DBにデータを保存
    fun insert(word: String) {
        viewModelScope.launch(Dispatchers.IO) {
            dao.insert(
                SampleEntity(
                    id = 0, // autoGenerate で自動的にIDを入れるときは0を入れる
                    word = word
                )
            )
        }
    }

    // DBの内容を全て消す
    fun deleteAll() {
        viewModelScope.launch(Dispatchers.IO) {
            dao.deleteAll()
        }
    }

}
```


# 参考にした記事
[公式サンプル](https://github.com/android/architecture-components-samples/tree/master/PagingSample)
[[Android] Room初心者が1から導入してみた手順](https://friegen.xyz/android-room-introduction-procedure/)
[【Android】はじめてのRoom](https://qiita.com/iTakahiro/items/7e0d63140ae4dac10d18)
[ORMラッパーライブラリRoomをKotlinで使ってみる](https://qiita.com/niusounds/items/fff5e83489e69d7924fd)
[RoomでPrimaryKeyがAutoIncrementの場合のInsert時のid指定](https://medium.com/@tak.k1u2r2o4/roomでprimarykeyがautoincrementの場合のinsert時のid指定-a3f41e4b4a8f)