日々是好日

プログラミングについてのあれこれ、ムダ知識など

RoomによるローカルDBの実装

先週某社の採用フローにエントリしたところ、1週間で簡易的な Todo アプリ作成の課題を与えられました。

その際、初めて Room を使用しローカルDBを構築、さらに ViewModel に埋め込んで連携したので、備忘的にメモしておきます。

Room とは

SQLite データベースへのアクセスに便利な抽象化レイヤーを提供するライブラリです。 Android Architecture Components のライブラリ群の一つとして Google I/O 2017 で公開されました。

実装のステップ

おおむね次の順番で実装を行うとスムーズかもしれません。

  1. Entity の作成
  2. Data Access Object(Dao)の作成
  3. Database の作成
  4. DataSource インターフェースの定義
  5. Database の利用

導入したライブラリ

implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"

// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"

Room の実装

次のような Todo リスト用のテーブルをもった DB を作ってみます。

id taskName isCompleted
1 hoge Active
2 fuga Completed

Entity の作成

@Entityアノテーションを付与した data class を作成します。 これが上記のテーブルにあたります。

@Entity(tableName = "tasks")
data class Task(
  @PrimaryKey(autoGenerate = true)
  val id: Long,
  val name: String,
  val isCompleted: String
)

idを主キーとして@PrimaryKeyを付与しています。 autoGenerate = trueとすることで勝手に値を振ってくれますが、レコードを追加するときにid = 0またはid = nullを指定する必要があるのでご注意を。

PrimaryKey  |  Android Developers

Data Access Object(Dao)の作成

@Daoアノテーションを付与したインターフェースを定義します。

@Dao
interface TasksDao {
    @Query("SELECT * FROM tasks WHERE name LIKE :taskName")
    fun find(taskName: String): List<Task>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(task: Task)

    @Update
    fun update(task: Task): Int

    @Delete
    fun delete(task: Task): Int
}

データにアクセスするためのメソッドを定義し、そのメソッドの呼び出しにより実行するクエリを記述します。 付与できるアノテーションは次の4つです。

  • @Query
  • @Insert
  • @Update
  • @Delete

@Insert, @Update, @Deleteでは引数で Task オブジェクトを渡してやることにより、データの修正や追加、削除を実行します。 @Queryでは SELECT 文を記述することで自由にクエリを実行できます。

Database の作成

@Databaseアノテーションを付与した抽象クラスを作成します。 このとき、抽象クラスは RoomDatabase を継承したクラスとします。

Room.databaseBuilderによりデータベースを生成します。

@Database(entities = [Task::class], version = 1, exportSchema = false)
abstract class TasksLocalDatabase : RoomDatabase() {
  abstract fun tasksDao(): TasksDao

  companion object {
    private var INSTANCE: TasksLocalDatabase? = null
    private val lock = Any()

    fun getInstance(context: Context): TasksLocalDatabase =
      INSTANCE ?: synchronized(lock) {
        INSTANCE ?: Room.databaseBuilder(
          context.applicationContext,
          TasksLocalDatabase::class.java, "Tasks.db"
        )
          .build()
          .also { INSTANCE = it }
      }

    fun destroyInstance() {
      INSTANCE = null
    }
  }
}

companion objectによりシングルトンなオブジェクトとして定義しましょう。

Database の利用

最後に Room で構築した DB の利用です。

val localDatabase = TasksLocalDatabase.getInstance(context)
val dao = localDatabase.tasksDao()

// Dao のメソッドをコールすることにより DB の操作を実行
val tasks = dao.find("hoge")
dao.insert(newTask)
dao.delete(delTask)
...

アプリに MVVM を採用している場合は、ViewModelFactory で ViewModel を生成するときにデータベースを埋め込んでしまった方が便利でしょう。

kcpoipoi.hatenablog.com

おわりに

なお某社は落ちました(ぇ

これで転職活動は振り出しに……( ^ω^)