6. SQLiteデータベースの使い方
公式の開発者向けサイトでは、Room というライブラリを使ってデータベースアクセスを行うことが推奨されています。
もちろんこれにしたがって Room を使っても良いのですが、今回はまずはシンプルに自分でSQLクエリーを発行してデータベース処理を実装していくことにしました。
SQLiteOpenHelperを使う
SQLiteデータベースの初期化と接続の管理を行うには、SQLiteOpenHelperを継承したクラスを作成するのが便利です。
--- DatabaseHelper.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class DatabaseHelper extends SQLiteOpenHelper { | |
// データーベース名 | |
static final String DATABASE_NAME = "words.db"; | |
// データベースのバージョン (カラムの追加などで変更を加えた場合にカウントアップする) | |
static final int DATABASE_VERSION = 1; | |
public DatabaseHelper(Context context) { | |
super(context, DATABASE_NAME, null, DATABASE_VERSION); | |
} | |
// テーブルの作成 & 初期データの投入 | |
@Override | |
public void onCreate( SQLiteDatabase db ) { | |
// テーブルを作成 | |
db.execSQL( | |
"CREATE TABLE IF NOT EXISTS " + WordsRepository.TABLE_NAME + " (" | |
+ WordsRepository.COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL " | |
+ ", " + WordsRepository.COL_ENGLISH + " TEXT NOT NULL " | |
+ ", " + WordsRepository.COL_JAPANESE + " TEXT NOT NULL " | |
+ ", " + WordsRepository.COL_DONE + " INTEGER NOT NULL " | |
+ ");" | |
); | |
// ここで、初期データ挿入可能 | |
} | |
// データベースのバージョンアップ時の処理 | |
@Override | |
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ) { | |
// if( oldVersion == 1 && newVersion == 2 ){ | |
// db.execSQL(""); | |
// } | |
} | |
} |
---
テーブル名、カラム名などの固定文字列は、後述するWordsRepositoryクラス内に定数として定義してあるので、二重で定義しないようにそちらを参照しています。
今後データベースにテーブルを追加したりカラムを追加したりする場合は、データベースのバージョン番号を上げて、onUpgrade() メソッド内で必要なSQLを発行する処理を実行します。今は最初のバージョンなのでまだ何も実装していません。
アプリケーション全体を通してデータベース接続を保持する
では、上で作成したDatabaseHelperクラスをどのように使うかを見てみましょう。
DatabaseHelper dbHelper = new DatabaseHelper(this);SQLiteDatabase db = dbHelper.getWritableDatabase();try(final Cursor cursor = db.rawQuery("SELECT * FROM words", null)){while (cursor.moveToNext()){(...)}}
tryのカッコ内で Cursor をオープンすると、tryを抜けたときに自動でクローズしてくれるので便利ですね。
このようにデータベースアクセスが必要になる度に毎回 DatabaseHelperのインスタンスを生成してから getWritableDatabase()メソッドを呼んでも構わないのですが、この方法だとデータベースのオープン/クローズ処理が毎回行われることになります。
今回のサンプルアプリケーションでは毎回データベース接続をオープン/クローズするのではなく、「カスタムアプリケーションクラス」を使ってアプリケーション全体を通してデータベース接続を保持する方法を使うことにします。
🎬[Android]Applicationクラスとは
--- MyApplication.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyApplication extends Application { | |
// SQLiteデータベースへの接続を保持するインスタンス変数。 | |
private DatabaseHelper mDbHelper = null; | |
// 自分自身のインスタンスを保持するスタティック変数。 | |
private static MyApplication sInstance; | |
// アプリケーションのどこからでも参照可能なように自分自身のインスタンスへの参照を返す。 | |
// これによって MyApplication.getInstance().getWordsRepositoy().getList() のようにDBアクセスが可能になる。 | |
public static MyApplication getInstance() { | |
return sInstance; | |
} | |
// アプリケーション開始時に一度だけ呼ばれる処理。 | |
@Override | |
public void onCreate() { | |
super.onCreate(); | |
// 自分自身への参照をスタティック変数として保持しておく。 | |
sInstance = this; | |
} | |
// アプリケーションのプロセスが終了する時に呼ばれる処理。 | |
@Override | |
public void onTerminate() { | |
// アプリケーション終了時にデータベース接続を閉じる。 | |
mDbHelper.close(); | |
mDbHelper = null; | |
super.onTerminate(); | |
} | |
// データベースへの接続を返す。 | |
private SQLiteDatabase getDb() { | |
if (mDbHelper == null) { | |
// まだインスタンスが生成されていない場合のみ新規インスタンスを生成。 | |
mDbHelper = new DatabaseHelper(this); | |
} | |
// 未Openの場合はOpenし、Open済みの場合は既存の接続を返す。 | |
return mDbHelper.getWritableDatabase(); | |
} | |
// Wordsリポジトリクラスのインスタンスを返す。 | |
// アプリケーションのどこからでも MyApplication.getInstance().getWordsRepositoy().getList() のようにDBアクセスが可能。 | |
public WordsRepository getWordsRepository() { | |
return new WordsRepository(getDb()); | |
} | |
} |
---
Applicationクラスのインスタンスはアプリケーションのプロセスが動いているかぎり破棄されることはないので、このクラス内のインスタンス変数として変数を宣言しておけばアプリケーションのどこからでも共通に使うことができます。
今回はこれを利用してDatabaseHelperのインスタンスをApplicationクラスで保持することにしました。getDb()というメソッドが初めて呼ばれたときにデータベースをオープンします。
また、アプリケーションが終了するときに onTerminate() が呼ばれるので、このタイミングでデータベース接続をクローズしています。
カスタムアプリケーションクラスが起動時に正しく呼ばれるようにするには、マニフェストファイルでクラス名を指定しておく必要があります。
リポジトリクラスを使ってデータベース処理を一箇所にまとめる
次に、実際にデータベースに対してSQLクエリーを発行してデータを取得したり更新したりする処理を実装します。これらの処理はテーブル単位で実行されることが多いので、その単位で「リポジトリクラス」を作ってまとめるのが良いでしょう。
今回のサンプルアプリケーションでは、WordsRepository というクラスを作って単語データに関するデータベース処理を記述しました。
全件を取得する
public List<Word> getList() {ArrayList<Word> list = new ArrayList<>();// tryの括弧内でCursorを生成することで自動的にcloseされる。try(final Cursor cursor = mDb.rawQuery("SELECT * FROM " + TABLE_NAME + " ORDER BY " + COL_ID, null)){while (cursor.moveToNext()){final Word word = buildWordFromCursor(cursor);list.add(word);}}return list;}
1件だけを取得する
// idで指定された単語を返す。idが0の場合は新規インスタンスを生成して返す。idが見つからない場合はnullを返す。public Word getById(int id) {if (id == 0){return new Word(0, "", "");}Word word = null;String[] args = { Integer.toString(id) };// tryの括弧内でCursorを生成することで自動的にcloseされる。try (final Cursor cursor = mDb.rawQuery("SELECT * FROM " + TABLE_NAME + " WHERE " + COL_ID + " = ?", args)) {// 主キーで絞っているため結果は1行か0行かのどちらかなのでwhileでループする必要はない。if (cursor.moveToFirst()){word = buildWordFromCursor(cursor);}}return word;}
単語を追加または更新する
// idが0の場合は新規追加、0以外の場合は更新処理を行う。public void save(Word word) throws InvalidKeyException, SQLException {if (word._id == 0) {String sql = "INSERT INTO " + TABLE_NAME + " ("+ COL_ENGLISH + ", "+ COL_JAPANESE + ", "+ COL_DONE+ ") VALUES (?, ?, ?) ";String[] args = { word.english, word.japanese, boolToString(word.done) };mDb.execSQL(sql, args);return;}Word existing = getById(word._id);if (existing == null) {throw new InvalidKeyException("");}String sql = "UPDATE " + TABLE_NAME + " SET "+ COL_ENGLISH + " = ?, "+ COL_JAPANESE + " = ?, "+ COL_DONE + " =? "+ " WHERE (" + COL_ID + " = ?) ";String[] args = { word.english, word.japanese, boolToString(word.done), Integer.toString(word._id) };mDb.execSQL(sql, args);}
単語を削除する
// idで指定された単語を削除する。単語が見つからない場合は何もしない。public void delete(int id) throws SQLException {Word existing = getById(id);if (existing == null) return;String sql = "DELETE FROM " + TABLE_NAME + " WHERE (" + COL_ID + " = ?) ";String [] args = { Integer.toString(id)};mDb.execSQL(sql, args);}
今回はSQLiteデータベースの使い方について見てみました。
ひとまずこれで単語帳アプリのVersion 0.1が動くようになりました。
ここまでのソースコードは下のURLで公開していますので良ければプロジェクト全体をクローンして動かしてみてください。
Androidで単語帳を作ろう - 目次
Androidで単語帳を作ろう(3)- SQLiteデータベースの使い方
🍻