Roomで動的に生成したクエリ(SQL文)を扱う方法

##はじめに

Roomを用いてDBを扱う際に、動的に生成したクエリを扱う必要があったため、その時に調べたことについてまとめてみました。

方法としては以下の２つ

* `@RawQuery`アノテーションを使用
* `RoomDatabase#query`メソッドを使用


### 今回使用するデータベース

以下のようなメモを保存するテーブルを持つDBを例に説明していきます。

```kotlin
@Entity
data class Memo(
    @PrimaryKey(autoGenerate = true)
    var id: Int,
    val memo: String
)
```

また、事前に以下のデータを持っているものとします。

|  id  |  memo  |
| ---- | ---- |
|  1  |  メモ1  |
|  2  |  メモ2  |
|  3  |  メモ3  |


## `@RawQuery`アノテーションを使用する場合

まず、DAOインタフェース内に関数を定義しておきます。

```kotlin
@Dao
interface MemoDAO {
    @RawQuery
    fun getMemoViaQuery(query: SupportSQLiteQuery): String
}
```

次にDBにアクセスしたい箇所において`SimpleSQLiteQuery()`を用いてクエリを生成し、DAOインタフェース内に定義した関数の引数に渡してやれば実行できます。

```kotlin
val query = SimpleSQLiteQuery("select memo from Memo WHERE id = 2")

launch(Dispatchers.IO){
    val list = dao.getMemoViaQuery(query)
    Log.i("tag", memo)
}
```

ログ：該当のメモが取り出せてることが確認できました。

```
I/tag: メモ2
```

コード全体としては以下のような感じです。簡略化ためActivity内で実装してます。

```kotlin
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {

    private lateinit var db: MemoDatabase
    private lateinit var dao: MemoDAO

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

        this.db = Room.databaseBuilder(
            applicationContext,
            MemoDatabase::class.java,
            "memo.db"
        ).build()
        this.dao = db.memoDAO()

        val query = SimpleSQLiteQuery("select memo from Memo WHERE id = 2")

        launch(Dispatchers.IO){
            val memo = dao.getMemoViaQuery(query)
            Log.i("tag", memo)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        cancel() // cancel is extension on CoroutineScope
    }
}
```

## `RoomDatabase#query`メソッドを使用する場合

`RoomDatabase#query`の返り値がCursorオブジェクトであることに気をつけて値を取り出します。

```kotlin
val query = SimpleSQLiteQuery("select memo from Memo WHERE id = 3")

launch(Dispatchers.IO){
    val cursor =  db.query(query)
    cursor.moveToNext()
    val idxMemo = cursor.getColumnIndex("memo")
    val memo = cursor.getString(idxMemo)
    Log.i("tag", memo)
}  
```

ログ：該当のメモが取り出せてることが確認できました。

```
I/tag: メモ3
```

コード全体

```kotlin
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {

    private lateinit var db: MemoDatabase
    private lateinit var dao: MemoDAO

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

        this.db = Room.databaseBuilder(
            applicationContext,
            MemoDatabase::class.java,
            "memo.db"
        ).build()
        this.dao = db.memoDAO()

        val query = SimpleSQLiteQuery("select memo from Memo WHERE id = 3")

        launch(Dispatchers.IO){
            val cursor =  db.query(query)
            cursor.moveToNext()
            val idxMemo = cursor.getColumnIndex("memo")
            val memo = cursor.getString(idxMemo)
            Log.i("tag", memo)
        }  
    }

    override fun onDestroy() {
        super.onDestroy()
        cancel() // cancel is extension on CoroutineScope
    }
}
```

Roomで動的に生成したクエリを扱うことについて説明しました。
以上です。

## 参考にしたサイト

[RawQuery - Android Developers](https://developer.android.com/reference/kotlin/androidx/room/RawQuery)
[RoomDatabase - Android Developers](https://developer.android.com/reference/androidx/room/RoomDatabase)