以下の定義のテーブルに対してINSERT
をすることでデータがどのように更新されていくかを確認する。
@Entity(tableName = "Sample") data class SampleEntity( @PrimaryKey(autoGenerate = true) val id: Int = 0, val name: String )
環境は以下の通り
- Android Studio 4.1 RC3
- Android Gradle Plugin 4.1.0-rc03
- Room 2.2.5
- 検証端末OSはAndroid11
- SQLite version3.28.0
利用しているコード(検証用アプリ)はこちら
autoGenerate=trueとは
公式の説明から以下のことがわかる PrimaryKey | Android Developers
- SQLiteが一意のIDを生成する
- データの型は
INTEGER
になる - INSERT時に
0
またはNULL
は「値がセットされてない」とみなされる
Shema確認
RoomDatabase
でexportSchema = true
設定し、コンパイルオプションでroom.schemaLocation
を指定した状態でビルドをすると、スキーマがJSON形式でファイル出力される。上記Sample
テーブルのCREATE文は以下のようになっている。
CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL )
id
カラムがAUTOINCREMENT
指定されている。
AUTOINCREMENT
公式を読む。
INTEGER PRIMARY KEY
で修飾されるカラムはROWID
のエイリアスであるROWID
またはINTEGER PRIMARY KEY
に対する値が明示的にセットされていない場合にはSQLiteが未使用の数値を自動で割り当てる- この数値は通常
利用されている数値の最大値 + 1
になる - この挙動は
AUTOINCREMENT
があってもなくても同じである
- この数値は通常
AUTOINCREMENT
が指定されていると「DBのライフサイクルを通してIDの再利用をしない」というようなアルゴリズムになる- つまり「過去に削除されたレコードがもっていたIDの使用を避ける」ということである
Summaryを読めばだいたいのことがわかる(この記事ではROWID
の説明は省略)。
INSERT
次のようなDao
メソッドを用意して、SampleEntity
のid
を未設定(デフォルト値である0
)でINSERT
を行ってみる。
@Insert suspend fun insertIncrementId(sampleEntity: SampleEntity)
すると、id
が1, 2,3.....と順に振られてレコードがinsertされていくのがわかる。期待通りの動作だ。
複数のデータをinsertする場合はどうだろうか。
@Insert suspend fun updateIncrementId(sampleEntity: List<SampleEntity>)
レコードを3つずつinsertしてもid
は順にincrementされていくのがわかる
また一度全レコードをDELETE
した後にinsertすれば、「DBのライフサイクルを通してIDの再利用をしない」ことも確認できる。
@Query("DELETE FROM Sample") suspend fun deleteAll()
なおid
を明示的に指定してINSERT
した場合の挙動はOnConflictStrategy
に従うがここでは深入りしない。
クエリの確認
Room
はアノテーションプロセッサでビルド時にコードを生成する。@Insert
アノテーションからはINSERT
クエリが生成される。
INSERT OR ABORT INTO `Sample` (`id`,`name`) VALUES (nullif(?, 0),?);
OR ABORT
の部分はOnConflictStrategy
によって変わるが、だいたいこのようなクエリになる。生成されるのはJavaのコードなので、nullIf(?, 0)
の部分はInteger
(正確にはKotlinのInt)がnullの場合を考慮していると思われる。
所感
- 主キーになるような明示的なカラムがなく、一意の整数値IDを付与したい場合には有効
- また何らかの理由でinsert順を保持したい場合にも有効かもしれない
- 一方で、
INTEGER PRIMARY KEY AUTOINCREMENT
なカラムがあると他にPrimaryKey
を追加することはできないため、明に主キーになるカラムがあるのであればそちらを使うべきだと思われる- 逆言えば、このとき
AUTOINCREMENT
は使えなくなるので、必要があれば何らかの手段で代替する必要があるだろう
- 逆言えば、このとき