SQLiteでデータベース
アンドロイドでは、SQLiteというデータベースが、標準でサポートされています。
SQLiteはWindow等の他のOSでも使う事ができ、JavaでもJDBCを使ってアクセスする事ができます。
SQLiteになじみの無い方は、 先に「JavaとSQLiteデータベース」 を参照して、まず、SQLite単体での使い方に、慣れてみると良いと思います。
データベースの作成とオープン - SQLiteOpenHelperクラスとSQLiteDatabaseクラス
アンドロイドでは、SQLiteDatabaseオブジェクトを使って、データベースを操作します。
SQLiteDatabaseオブジェクトを取得するには、通常、ヘルパークラスとして、SQLiteOpenHelperクラスを継承したクラスを作成して、 このクラスよりSQLiteDatabaseオブジェクトを取得します。
以下に、その例を示します。
リスト1(1)-ヘルパークラス(PersonOpenHelper.java)
リスト1(2)-アクティビティクラス(SQLiteSample1.java)
このプログラムを実行すると、以下のような画面がに表示されます。
リスト1(1)で、ヘルパークラス「PersonOpenHelper.java」を定義しています。
ヘルパークラスは、SQLiteOpenHelperクラスを継承しています。
ヘルパークラスではコンストラクタで、スーパクラスSQLiteOpenHelperのコンストラクタを呼び出しています。(11行目)
SQLiteOpenHelperクラスのコンストラクタには, 引数が4つあります。
第1引数は、データベースを所有するコンテキストオブジェクトを指定します。
第2引数は、データベースファイルの名前です。
この引数にnullを指定すると、データベースはメモリー上に作られます。
第3引数は、Cursorオブジェクトを作成するのに使われるFactoryオブジェクトを指定するらしいのですが、 どのようなものを指定するのかよくわかりません。
とりあえず無視して、常にnullを指定するという事で問題無いようです。
第4引数は、データベースのバージョンを指定します。
リスト1(1)では、データベースの最初のバージョンを示す1を指定しています。
データベースのバージョンは、途中でデータベースの構成が変更になった等の場合に、これを管理するために使われます。
SQLiteOpenHelperクラスはabstractクラスで、onCreateメソッドとonUpgradeメソッドを実装する必要があります。
onCreateメソッドは、データベースが最初に作られる時に呼ばれます。
リスト1(1)では、人を管理する「person_table」テーブルの作成と、3人分の初期データをテーブルに挿入しています。
ここでSQL文を実行するために、execSQLメソッドを実行しています。
execSQLメソッドについては、後述の「SQL文の実行-execSQLメソッド」を参照して下さい。
onUpgradeメソッド(31行目)は、データベースが既に存在していて、そのデータベースのバージョンがコンストラクタで指定されたバージョンと異なる時に呼ばれるようです。
ここでは、onUpgradeメソッドでは何も処理をしていません。
後で、データベースの変更が生じた場合は、ここに処理を記述します。
onUpgradeメソッドとデータベースのバージョンの管理の例については、後述の「データベースのバージョンアップのシナリオ」を参照して下さい。
リスト1(2)のアクティビティクラス「SQLiteSample1.java」では、 ヘルパークラスよりSQLiteDatabaseオブジェクトを取得して、 queryメソッドにより、「person_table」テーブルに問い合わせをおこない、 取得したデータの行数分だけTextViewにデータを表示しています。
queryメソッドについては後述のSQLiteのCRUD(2)-データの検索とカーソルを参照して下さい。
21行目で、SQLiteDatabaseオブジェクトを取得するのに、getReadableDatabaseメソッドを使用していますが、 SQLiteDatabaseオブジェクトを取得するには、getWritableDatabaseメソッドもあります。
getReadableDatabaseメソッドとgetWritableDatabaseメソッドの違いは、 getReadableDatabaseメソッドが読込み用にデータベースをオープンするのに対して、 getWritableDatabaseメソッドは読み書き用にデータベースをオープンすることです。
getReadableDatabaseメソッドやgetWritableDatabaseメソッドを使ってオープンしたデータベースは、 closeメソッドを使ってデータベースをクローズする事を忘れないようにして下さい。(32行目)
SQLiteのCRUD(1)-行の挿入・更新・削除
SQLiteデータベースのCRUDを実行するには、いろいろなパターンがあります。
SQL文の実行-execSQLメソッド
SQLiteDatabaseオブジェクトよりSQL文を実行するには、引数にSQL文を示す文字列を指定して、execSQLメソッドを実行します。
execSQLメソッドは結果を返す事ができないので、select文を使ってデータを取得する等の用途には使う事はできませんが、 それ以外のいろいろなSQL文を実行できます。例えば、リスト1(1)のonCreateメソッドでは、テーブルを作成するのにSQL文の「CREATE TABLE」を指定して、execSQLメソッドを実行しています。(17行目)
また、テーブルにデータ行を挿入するのにも、SQL文の「INSERT INTO」を指定して、execSQLメソッドを実行しています。(25,26,27行目)
リスト1(1)のonCreateメソッド内のコードを、以下に再掲します。
execSQLメソッドには、もう1つオーバオードされたメソッドが存在し、これを使うとSQL文に変数をバインドする事ができます。
以下のコードは、このオーバオードされたメソッドを使ってリスト1(1)の25行目を書き換えた例です。
execSQLメソッドにSQL文を指定する事で、行の挿入・更新・削除をおこなう事ができますが、 次に示すように専用のメソッドも用意されています。
行の挿入-insertメソッド
テーブルの行にデータを挿入するには、insertメソッドが用意されています。
insertメソッドを使うには、ContentValuesオブジェクトに挿入するデータをセットします。
ContentValuesオブジェクトは、Hashmapのようなもので、列に対応するキーと値のペアを持ちます。
以下に、リスト1(1)の25行目を、insertメソッドを使って書き換えた例を示します。
insertメソッドの第1引数には、テーブル名を指定します。
第2引数の意味は、理解できていませんが、とりあえずnullを指定しておけば良いようです。
insertメソッドは、戻り値として行のIDを返します。
行のIDの意味については、ROWID - テーブルの作成 - SQLite入門が参考になると思います。
行の更新-updateメソッド
行を更新するには、updateというメソッドも用意されています。
以下にupdateメソッドの例を示します。
updateメソッドも、更新するデータをContentValuesオブジェクトにセットして使います。
updateメソッドの第1引数には、insertメソッドと同様に、テーブル名を指定します。
第2引数には、更新するデータをContentValuesオブジェクトで、指定します。
第3引数には、更新するデータのwhere条件を指定します。
上記の例では、name列のデータが「本田 圭佑」である行の年齢を、100才に更新します。
この値をnullに指定すると、すべての行が更新対象になります。
第4引数には、更新するデータのwhere条件を「?」を使ってパラメータで指定した場合の、パラメータ値をString配列で指定します。
where条件にパラメータが無い場合は、上記の例のようにnullを指定します。
以下に、where条件にパラメータを指定した例を示します。
行の削除-deleteメソッド
行を削除するには、deleteメソッドという、削除専用のメソッドも用意されています。
以下に、deleteメソッドの例を示します。
deleteメソッドの第1引数も、同様にテーブル名を指定します。
第2引数には、削除する行のwhere条件を指定します。
この値をnullに指定すると、すべての行が削除されます。
第3引数には、更新するデータのwhere条件を「?」を使ってパラメータで指定した場合の、パラメータ値をString配列で指定します。
where条件のパラメータが無い場合は、上記例のようにnullを指定します。
以下に、where条件にパラメータを指定した例を示します。
SQLiteのCRUD(2)-データの検索とカーソル
execSQLメソッドは値を返さないため、データベースのデータを取得する事はできません。
代わりに、queryメソッドかrawQueryメソッドを使います。
queryメソッドは、単純なSQLによるデータを取得には便利ですが、複雑なSQLをおこなうには、rawQueryメソッドを使った方が便利です。
queryメソッド
queryメソッドには、幾つか、オーバロードされたメソッドが存在しますが、通常よく使われるのは、以下の引数のメソッドです。
queryメソッドの第1引数には、テーブル名を指定します。
第2引数には、取得するテーブルの列を、文字列配列で指定します。
第3引数には、SQLのwhere条件を指定します。
where条件には、パラメータ?を使う事ができ、パラメータ値を文字列配列で、第4引数で指定します。
パラメータを使わない場合は、第4引数にはnullを指定します。
第5引数には、SQLの「GROUP BY」条件,第6引数には「HAVING」条件,第7引数には「ORDER BY」条件を指定します。
以下に、オーバロードされたqueryメソッドの、構文を列挙します。
メソッドの引数の変数名をみれば、SQL文と対比して、使い方はだいたい想像できると思います。
- Cursor query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
- Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
- Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
リスト1(2)の23、24行目に、queryメソッドのコードの例がありますが、これにwhere条件のパラメータを使って、25才以上のデータを取得する例を、以下に示します。
rawQueryメソッド
rawQueryメソッドは、引数にSQL文と、 SQL文にパラメータ?使われている場合は、パラメーター値を文字列配列で指定します(パラメータが使われていない場合はnull)。
上記のqueryメソッドの例を、rawQueryメソッドを使って書き換えると、以下のようになります。
SQLを使い慣れている方は、こちらの方が使いやすいでしょう。
SQLiteQueryBuilderクラス
SQLiteQueryBuilderクラスを使って、データの検索をおこなう事もできます。
SQLiteQueryBuilderクラスを使った検索の例については、「コンテンツ プロバイダ」を参照して下さい。
カーソル
queryメソッドとrawQueryメソッドは、どちらもCursorオブジェクトを返します。
カーソルは、Javaのイテレータのようなものと考えれば良いでしょう。
カーソルを操作する事で、1行ずつデータを取得します。
リスト1(2)の25行目~30行目のように、moveToFirstメソッドで最初の行に移動して、moveToNextメソッドで次の行に移動します。
moveToFirstメソッドやmoveToNextメソッドなどの行移動メソッドは、戻り値として、移動する行が無い場合はfalseを返します。
カーソルには、moveToPreviousメソッド等、他の行移動メソッドや、 他にもgetStringメソッド等の列データ取得メソッド,getColumnNameメソッド等の列情報取得メソッドなどの、有益なメソッドがあります。
詳しくは、 Cursorクラスのリファレンスを参照して下さい。
カーソルは、使い終わったらcloseメソッドを実行して、クローズするようにして下さい。
リスト1(2)のように、whileループを使って行を走査する事もできますが、以下のようにgetCountメソッドで行数を取得して、forループを使う事もできます。
プリコンパイルステートメントによるスピードアップ
同じSQLを、パラメータのみを変更して、何回も実行する場合、 プリコンパイルステートメントを使うと、処理スピードをアップさせる事ができます。
以下に、リスト1(1)の25,26,27行目の3つの挿入処理を、プリコンパイルステートメントに置き換えた場合の例を示します。
SQLiteStatementオブジェクトには、executeメソッドのほかに、INSERT専用のメソッドexecuteInsertが存在し、 このメソッドを使うと、挿入された行のIDを取得する事ができます。
以下に、その例を示します。
SQLiteのトランザクション
アンドロイドのSQLiteで、トランザクション処理 をおこなうには、以下のコ-ドのコメント部分(3行目)に、SQLの処理を記述します。
beginTransactionでトランザクションを開始し、 エラーが発生しなければsetTransactionSuccessfulでコミットして、 endTransactionでトランザクションを終了します。
エラーが発生した場合は、setTransactionSuccessfulが実行されないまま、 endTransactionでトランザクションが終了してしまいますので、 処理はロールバックされコミットされません。
CursorAdapterを使ってSQLiteの検索結果をListViewに表示する。
SQLiteの検索結果であるCursorオブジェクトより、Adapterオブジェクトを作成して、ListViewのAdapterに設定する事により、 ListViewに簡単に検索結果を表示する事ができます。
以下に、その例を示します。
リスト2-アクティビティクラス(SQLiteSample2.java)
ここでは、ヘルパークラスとして、リスト1(1)のPersonOpenHelperクラスをそのまま利用します。
このプログラムを実行すると、以下のようにCursorオブジェクトの各行がListViewに表示されます。
CursorオブジェクトをAdapterクラスと組み合わせて使うには、Cursorオブジェクトに「_id」列を含んでいなければならないようです。
このため、この例ではCursorオブジェクトを生成するためのSQL文で、SQLiteにより自動的に作成される「rowid」列を利用して、 この列を別名「_id」列として使っています。
rowidについては、ROWID - テーブルの作成 - SQLite入門を参照して下さい。
このような事をしなくても、すなおにテーブルに主キーとして「_id」列を定義して、検索結果列に出力させても良いです。
データベースのバージョンアップのシナリオ
onUpgradeメソッドを使ったサンプルとして、あまり良い例ではありませんが、データベースをアップグレードするプログラムを作ってみました。
リスト2(1)-ヘルパークラス(PersonOpenHelper.java)
リスト2(2)-アクティビティクラス(SQLiteSample1.java)
リスト2(1)の19行目と37行目のように、ヘルパークラスのonCreateメソッドとonUpgradeメソッド に、ログを出力するコードを挿入して、 11行目のDB_VERSION定数を変化させてみると、 onCreateメソッドとonUpgradeメソッドが、どのタイミングで呼ばれるのか確認する事ができます。
そのためには、14行目のsuperメソッドの第2引数に、nullのかわりにデータベースファイルの名前を指定して、 データベースがファイルに保存されるようにする必要があります。
DB_VERSIONが「1」の状態でプログラムを実行すると、最初の1回だけonCreateメソッドが呼ばれるのがわかります。
次に、DB_VERSIONを「2」に変更すると、次の1回だけonUpgradeメソッドが実行されます。
以下に、その時のログの出力を示します。
2行目のログをみると、データベースのバージョンを1から2に変更する事で、 onUpgradeメソッドのoldVersion引数に1が、newVersionに2が渡されるのが 確認できます。
このプログラムでは、データベースのバージョンが1の時は、name列とage列を持つperson_tableテーブルを使用していましたが、 仕様の変更により、 テーブルの列にteam列を追加して、一般の人からスポーツ選手を管理するテーブルに変更をおこなっています。
これにより、テーブル名もperson_tableからplayer_tableに変更しています。
また、テーブルの移行時にエラーが発生した場合に、元に戻るようにトランザクション処理をおこなっています。
SQL文の「ALTER TABLE」を使って、列の追加やテーブル名の変更をおこなってもよいのですが、 ここでは新規にテーブルを作成して、前のテーブルのデータを1行づつコピーしています。
後述する方法で、アンドロイドマシーン内部に作成したデーアベースファイルをWindowsにコピーして、データベースのテーブルの内容をDBViewerで確認してみました。
DBViewerについては「eclipseのDBViewerプラグイン」を参照して下さい。
図1-バージョン1のテーブルの内容
図2-バージョン2のテーブルの内容
意図どおりに、テーブルが変更されている事が確認できます。
WindowsからSQLite
アンドロイドマシーン内部に作成したSQLiteのデータベースを、 Windows・OSにコピーして、Windows上のツールを使って、データベースの内部を参照する事ができます。
Windows・OS上でのSQLiteのツールについては、「JavaとSQLiteデータベース」を参照して下さい。
ヘルパークラスによって作成されたデータベースファイルは、アンドロイドマシーン内の「/data/data/パッケージ名/databases」デレクトリに格納されています。
eclipseを、「DDMS」パースペクティブに表示を切り替えて (eclipseのメニューより「Windows(W)」→「パースペクティブを開く(O)」→「DDMS」を選択)、 「ファイル・エクスプローラー」タブを選択すると、アンドロイド内部のデレクトリを覗くことができます。
ここで、リスト2のプログラムを実行していれば、以下の図のように 「/data/data/gudon.sample.sqlite2/databases」デレクトリに「xxx.db」ファイルが格納されているはずです。
このデータベースファイルを選択して、図xの右上に赤丸で表示されているアイコンをクリックして、コピーしたいデレクトリを選択すると Windows・OS上にファイルをコピーする事ができます。
このデータベースファイルを、SQLiteのツールより指定すれば、データベースファイルの内容を確認できます。
以下の図は、「eclipseのDBViewerプラグイン」 でxxx.db内のデータを表示しているところです。
アンドロイドのShellからSQLite
アンドロイドOSのshellから、SQLiteにアクセスする事もできます。
アンドロイドのエミュレータを起動後に、MSDOSのコマンドプロンプトより「adb shell」と入力すると、 アンドロイドOSのshellが起動します。
アンドロイドOSはLinuxベースのOSですから、 このshellから「ls」等のLinuxコマンドが実行できます。
ただ、日本語のデータを表示させようとすると、文字化けが発生してしまいます。
このために、日本語のデータを表示させるには、ログの表示の時の「日本語のログ表示の文字化け」と同様、コマンドプロンプトの設定を変更する必要があります。
コマンドプロンプトのフォント設定を、MSゴシックに変更します。(最初に1回だけ設定すれば良い。)
コマンドプロンプトを起動し、chcpコマンド「chcp 65001」にてコードページをutf-8に変更します。(コマンドプロンプトを起動するたびに毎回実行する必要があります。)
「adb shell」と入力して、アンドロイドOSのshellを起動します。
「cd」コマンドにて、データベースファイルの存在するデレクトリに移動して、「sqlite3 データベースファイル名」と入力してアンドロイドのSQLiteを起動します。
これで、sqlite3のコマンドラインツールのコマンドが使えるようになります。
終了する場合は、「.exit」コマンドでsqlite3のコマンドラインツールを終了させて、「exit」コマンドでアンドロイドのshellを終了させると、コマンドプロンプトに戻ります。
アンドロイドOSのsqlite3のコマンドラインツールを起動して、person_tableの内容を表示した際の画面を以下に示します。
日本語の表示の右端が表示されない事がありますが、コマンドプロンプトをスクロールさせると表示されます。
参考URL
- Android Developers
- JavaとSQLiteデータベース
- SQLite入門