How to manage SQLite's database file names

SQLite名前空間に関する問題

AndroidSQLite を扱う場合、SQLiteOpenHelper を利用するのが一般的だと思いますが、その際にデータベースファイル名を指定する必要があります。*1*2

データベースファイル名は同一アプリケーション内部で共有されるため、アドホックに管理すると、意図しない名前衝突が起こる可能性があります。*3

今回は、この名前衝突を回避する方法を模索してみようと思います。

名前衝突を回避する方法の模索

データベースをアプリケーションで1つにする方法

1アプリ1データベースという方式にすれば、アプリ内部でのDB名の衝突は起こりません。この方法は、データ構造が単純かつ、データの管理をひとりで行うような場合はアリかもしれません。しかし、時間の経過とともにデータ構造が複雑化されていくことも十分に考えられるため、分割統治する仕組みがあったほうが無難かもしれません。

データベースファイル名を集中管理し、データベース自体は個別管理する方法

データベースファイル名を集中管理し、データベース自体は個別管理する方法では、データベースファイル名の一元管理と、複数のデータベースの相互非依存な個別管理が可能になります。

どちらの方法を採用するか?

特にDBを分ける明確な理由がない間は単一DBで運用し、必要が生じたときにDBを分割するという方法がよさそうです。DBを分割してしまうと、追加の複雑さを導入してしまうというデメリットもありますが、それ以上に、異なるDB間でテーブルのjoinができなくなるという点が致命的。開発初期は相互に無関係のデータだと思っていても、後になってjoinする必要性が出てくるということはありうるし、それは未来予知能力がない限りわからんですよね。

実現方法

enum によるデータベース名の集中管理

データベース名の集中管理を行うための enum を作成します。enum として扱うことにより、実装の分散を防ぎます。

public enum SQLiteDatabaseIdentifier {
    TEST_DATABASE(1);

    private final int version;

    SQLiteDatabaseIdentifier(int version) {
        this.version = version;
    }

    public int version() {
        return version;
    }
}

SQLiteDatabaseIdentifier を扱う SQLiteOpenHelper の作成

SQLiteDatabaseIdentifier を扱うことを強制された SQLiteOpenHelper を作成します。これを利用することで、データベースファイル名の一元管理と複数のデータベースの相互非依存な個別管理が可能になります。

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import static android.database.sqlite.SQLiteDatabase.CursorFactory;

public abstract class SQLiteDatabaseIdentifierAwareSQLiteOpenHelper extends SQLiteOpenHelper {
    public SQLiteDatabaseIdentifierAwareSQLiteOpenHelper(Context context, SQLiteDatabaseIdentifier id, CursorFactory factory) {
        super(context, id.name(), factory, id.version());
    }
}

SQLiteDatabaseIdentifierAwareSQLiteOpenHelper の実装例

package com.objectfanatics.example;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class DbHelper extends SQLiteDatabaseIdentifierAwareSQLiteOpenHelper {
    private static final SQLiteDatabaseIdentifier sqLiteDatabaseIdentifier = SQLiteDatabaseIdentifier.TEST_DATABASE;

    public DbHelper(Context context) {
        super(context, sqLiteDatabaseIdentifier, null);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        String sql1 = "create table User (name text primary key)";
        sqLiteDatabase.execSQL(sql1);

        String sql2 = "insert into User (name) values ('John Smith')";
        sqLiteDatabase.execSQL(sql2);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i2) {
    }

    public String getName() {
        Cursor c = getReadableDatabase().rawQuery("select name from User", null);
        c.moveToFirst();
        return c.getString(c.getColumnIndex("name"));
    }
}

まとめ

今回は、データベース名(とバージョン)の一元管理を行いつつ、各データベースの実装を相互非依存にするための簡単な方法をまとめました。

今回は思い付きベースでまとめてみただけなので、もっといいアイデアがあればぜひ教えてください(^^;

*1:in-memory database を利用しない限りは

*2:SQLiteOpenHelper を使わなくても SQLite を使う限りはデータベースファイル名は必要ですよね。

*3:名前衝突が行わなかったとしても名前が分散している状態というのは精神衛生上よろしくないですよね。衝突してもコンパイル時に検出できないという恐怖も常に付きまとうし、、、。