search
LoginSignup
12
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

既存のSQLiteのデータベースをArchitecture CompontentsのRoomに移行してみた

概要

今までアプリをSQLiteで作ってきたが、今更ながらに重い腰を上げ、Roomベースに移行しました。

なお、ここでの移行は前データを引き継いで移行するという意味で、ユーザーのデータは削除せずに進めたいと思います。
あと、この際なのでKotlinにしようぜと意気込んだのでKotlinのコードとなります。

基本

基本はドキュメントです。
https://developer.android.com/training/data-storage/room/index.html

これ通りで作れば大体問題ないです。以下少し変えないといけなかった所。

ハマりどこ

1. データベースのファイル名

当たり前といえば当たり前なのですが、通常実装時とRoomでの実装時で名前を変えない。

元々自分のコードはSQLiteOpenHelperを使ってたのですが例えば、

SQLiteOpenHelper helper = new SQLiteOpenHelper(context, "app.db", null, 1);

こんなコードだった場合に、AppDatabaseを取得する際の名前は

Room.databaseBuilder(context, AppDatabase::class.java, "app.db").build()

となります。同時の自分がなぜそう考えたかわからないですが、.dbって勝手につくもんだと解釈し、"app"って入れてました。

2. 移行方法

最終的にやらなきゃいけなかったことは

  1. RoomでdatabaseBuilderを使ってDBにアクセスする際にMigrationを行わないといけない
  2. テーブルの内容とEntityを合わせないといけない

この2点のみ。簡単ですが1個ずつ説明します。

2-1. RoomでdatabaseBuilderを使ってDBにアクセスする際にMigrationを行わないといけない

まず、1についてですが、単純に何もしないMigrationを行う必要があります。どうも、Roomの内部でマスターのデータを持っているらしく、Migrationを行わないと以下のようなエラー文言がでます。

Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

ということで以下のような形で、何もしないMigrationを行います。

Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
    .addMigrations(object: Migration(1, 2){
        override fun migrate(database: SupportSQLiteDatabase) {
        }
    })
    .build();

2-2. テーブルの内容とEntityを合わせないといけない

そして2について。

上記のようなMigrationを書いて起動すると、恐らく以下のようなエラーが出ると思います。

Caused by: java.lang.IllegalStateException: Migration didn't properly handle

これはようは、既にあるDBファイルのバージョンをあげようとして、Migrationに失敗したということですね。上記で指定したように、Migraitonの内容は空なのになぜ失敗したか?その理由はその続きに書いてあります。

Expected:
 TableInfo{...}
Found:
 TableInfo{...}

このTableInfoの中身が重要です。ここにはDBのSchemaが書いてあり、Expected(今やろうとしているMigration後のSchema)とFound(現状のSchema)が合わないとMigrationがうまくできません。

※補足:
なお、Shchemaをあわせないといけないのは今回の場合、Migrationの内容が空だからです。逆にMigrationでちゃんと書いてあげるのも1つの手ですが、元のDBを引き継いで使うために行っているので、Migrationでは対応せずSchemaを合わせることで対応します。

合わせるにあたって、上記のExpectedとFoundの中身を一個一個見ていけばいいのですが、ハマりどころはこんなところでした。

◯テーブル名を合わせる

当たり前ですがテーブル名が合わないと駄目です。Entityのクラス名がテーブル名となります。テーブル名がPascalCaseじゃない場合には注意が必要です。Entityのアノテーションにテーブル名が指定できます。

@Entity(tableName = "user")
class User{
}

◯カラム名を合わせる

こちらも当たり前ですがカラム名が合わないと駄目です。Roomでは、Entityのクラス内にある変数名がカラム名となります。よって、変数名とカラム名が違う場合には以下のようにアノテーションで指定して上げる必要があります。

@ColumnInfo(name = "first_name")
val firstName: String? = null

◯notNullを合わせる

テーブルを作成する時に指定しなければ、基本はnotNull=trueになっていると思います。ここで紛らわしいのがnotNullがtrue、つまり、notNullを許容するとなります。よってCreate TableのSQL文で言うと、こんな感じです。

"CREATE TABLE user (
  id INTEGER PRIMARY KEY NOT NULL /* notNull=false */,
  first_name TEXT /* notNull=true */
);

そして、notNullをtrueにする方法ですが、StackOverflowなんかを見ていると、notNull=trueの変数名に対して、@NonNullをつけろと言うのが出てきます。

@NonNull
private int id;

今あえて、Javaで書いたのですが、これはjavaのときのみ有効です

Kotlinではそもそも言語側でNull Safeにかけるため、そこを指標に作ります(というか多分ですがKotlinから生成されるコードに@NonNullがつくんじゃないかと思う)。よってKotlinの場合は、こんな感じ。

var id: Int = 0 //notNull=false
var firstName: Int? = 0 //notNull=true

自分のテーブルでは無かったですが、他にはUniqueとかも別途で設定が必要なようです。

まとめ

以上で無事移行できるはずです。意外とまとまった情報が無かったので、お役に立てると嬉しいです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
12
Help us understand the problem. What are the problem?