問題現象
Android開発(Java)でDBアクセスにRoomライブラリを使ってます。 (roomバージョンは2.5.0です)
各Entityクラスのアノテーションで、DBスキーマがNotNullなら、@NonNullアノテーションをつける必要があります。
逆に、null許容なら、アノテーションをつける必要はないです。
今回操作したいテーブルのスキーマは以下です。
CREATE TABLE [Tbl]( [ID] INTEGER NOT NULL ,[clm1] TEXT ,[clm2] INTEGER , PRIMARY KEY ([ID]) )
上記スキーマに対応して以下のようなEntityクラスを作りました。
@Entity(tableName = "Tbl") public class Tbl { @PrimaryKey @NonNull @ColumnInfo(name = "ID") public int ID; @ColumnInfo(name = "clm1") public String clm1; @ColumnInfo(name = "clm2") public int clm2; }
これで実行すると、DBアクセスで以下ような例外が発生します。 Expected が期待するスキーマ、Foundが実際のスキーマです。
java.util.concurrent.ExecutionException: java.lang.IllegalStateException: Pre-packaged database has an invalid schema: Tbl(com.xxx.app.yyy.Tbl). Expected: TableInfo{name='Tbl', columns={ ID=Column{name='ID', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, clm1=Column{name='clm1', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, clm2=Column{name='clm2', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'} }, foreignKeys=[], indices=[]} Found: o TableInfo{name='Tbl', columns={ ID=Column{name='ID', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, clm1=Column{name='clm1', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, clm2=Column{name='clm2', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='undefined'} }, foreignKeys=[], indices=[]}
clm2が、Entityでは@NonNullアノテーションをつけていないのに、期待するスキーマではnotnullになってしまっています。
これにハマってしまいました。
調査
調査のため、Roomのスキーマを出力することにします。
DataBaseクラスのアノテーションの exportSchema を true にします。
@Database(entities = {Tbl1.class}, version = 2, exportSchema = true) public abstract class AppDatabase extends RoomDatabase { //DAO定義省略 }
app側の build.gradle に以下設定を追加します。
android { defaultConfig { //いろいろ javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } } } }
これで、ビルドすると app/schemas/パッケージ名.AppDatabase/2.json にスキーマが出力されます。 スキーマを見ると、やはり notNull になっていました。
"fields": [ { "fieldPath": "ID", "columnName": "ID", "affinity": "INTEGER", "notNull": true }, { "fieldPath": "clm1", "columnName": clm1", "affinity": "TEXT", "notNull": false }, { "fieldPath": "clm2", "columnName": clm2", "affinity": "INTEGER", "notNull": true },
よく見ると INTEGER だけ notnull となっています。
調べると、null許容にするにはエンティティのフィールドの型がnull許容型でないといけないということがわかりました。 intはnull許容型でないので、Room自動的にnotnullにするようです。
対策
ということで対策としては、エンティティのフィールドをnullが使える型にすることで解決しました。
今回は int → Integer となります。使用するときはnullチェックを忘れないようにしないといけないですが。
@Entity(tableName = "Tbl") public class Tbl { @PrimaryKey @NonNull @ColumnInfo(name = "ID") public int ID; @ColumnInfo(name = "clm1") public String clm1; @ColumnInfo(name = "clm2") public Integer clm2; }
これに気付くのに結構時間かかったので、ビルド時に警告とかで出してほしいなーとおもいました。