[Kotlin]Roomを用いたデータベース操作

# はじめに
 -  [公式はこちら](https://developer.android.com/training/data-storage/room?hl=ja)
 - Roomを用いて追加及び取得を行います 

# 画面
データ追加ボタンでデータを追加
データー表示でデータを表示します

<img width="375" alt="File.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199764/9903a4e3-aee1-7e90-94f9-8357638f1f84.png">

```actvity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/main_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="64dp"
        android:paddingHorizontal="16dp"
        android:text="Hello World!" />

    <Button
        android:id="@+id/main_add_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="32dp"
        android:text="データ追加" />

    <Button
        android:id="@+id/main_display_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="32dp"
        android:text="データ表示" />

</LinearLayout>
```

# 実装

```build.gradle(app)
apply plugin: 'kotlin-kapt' // kapt使う用

dependencies {
   〜

    // Room
    implementation "androidx.room:room-runtime:2.2.5"
    kapt "androidx.room:room-compiler:2.2.5"

    // kotlin coroutines 非同期、メインスレッド処理用
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
```

データ

```NotificationState.kt
@Entity(
    tableName = "notification",
    primaryKeys = ["id"]
)
data class NotificationState(
    @ColumnInfo(name = "id") val id: Int = 0,
    @TypeConverters(NotificationTypeConverter::class)
    @ColumnInfo(name = "type") val type: NotificationType,
    @ColumnInfo(name = "message") val message: String
)
```

データ操作

```NotificationDao.kt

@Dao
interface NotificationDao {
    // アイテム追加
    // onConflict = OnConflictStrategy.REPLACE しておくとデータが重複した場合でも自動でupdateにしてくれる insertといいつつupsertです
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(state: NotificationState)

    // 複数のアイテム追加
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(states: List<NotificationState>)

    // idを指定して取得
    @Query("SELECT * FROM notification WHERE id = :id LIMIT 1")
    fun select(id: String): NotificationState

    // 最後のid取得
    @Query("SELECT id FROM notification ORDER BY id DESC LIMIT 1")
    fun selectLastId(): Int

    // 全てのアイテム取得
    @Query("SELECT * FROM notification")
    fun selectAll(): List<NotificationState>

    // 全てのアイテム取得 LiveData
    @Query("SELECT * FROM notification")
    fun selectAllWithLiveData(): LiveData<List<NotificationState>>

    // データを全て削除
    @Query("DELETE FROM notification")
    fun deleteAll()
}
```

データベース

```RoomSampleDatabase.kt
@Database(
    entities = [NotificationState::class],
    version = 1
)
@TypeConverters(value = [NotificationTypeConverter::class])
abstract class RoomSampleDatabase : RoomDatabase() {

    abstract fun notificationDao(): NotificationDao

}
```

Activity

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

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

        val database = Room.databaseBuilder(
                this, RoomSampleDatabase::class.java, "room_sample_database.db")
                .fallbackToDestructiveMigration()
                .build()
        val notificationDao = database.notificationDao()

        main_add_button.setOnClickListener {
            GlobalScope.launch(Dispatchers.IO) { // 非同期処理
                val lastId = notificationDao.selectLastId()
                // 最後のidにプラス1して新しいデータを追加
                val state = NotificationState(lastId + 1, NotificationType.GOOD, "いいねをもらいました")
                notificationDao.insert(state)

                GlobalScope.launch(Dispatchers.Main) {  // main thread
                    Toast.makeText(this@MainActivity, "データ追加しました", Toast.LENGTH_SHORT).show()
                }
            }
        }

        main_display_button.setOnClickListener {
            GlobalScope.launch(Dispatchers.IO) { // 非同期処理
                val states = notificationDao.selectAll()
                GlobalScope.launch(Dispatchers.Main) {  // main thread
                    main_text.text = states.toString()
                }
            }

            // LiveDataだとデータに変更があった場合即時反映される
            // 取得をこちらに切り替えて、データ表示後、データ追加して即時表示が変わることを確認してください
//            notificationDao.selectAllWithLiveData().observe(this, Observer {
//                main_text.text = it.toString()
//            })
        }
    }
}
```

# 所感
Realmより書きやすい

ソースコードは[こちら](https://github.com/yoshihitoijichi/RoomSample)