問題現象
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 {
}
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;
}
これに気付くのに結構時間かかったので、ビルド時に警告とかで出してほしいなーとおもいました。