mucchinのAndroid戦記

Androidアプリのデータ保存方法の一つ「SQLite」の使い方 SQLiteOpenHelper編

スポンサーリンク

Androidのデータベース「SQLite」の使い方は?

今回は、AndroidアプリからSQLiteを使用する際の、コードレベルでの使い方をみていきましょう。
長くなりそうなので、3記事に分けたいと思います。
今回は、SQLiteOpenHelperというクラスの使い方について触れたいと思います。

今回は、データベースのオープン処理の説明になります。
行の追加・更新・削除編は下記のページをご覧下さい。
Androidアプリのデータ保存方法の一つ「SQLite」の使い方 行の追加・更新・削除編
レコードの検索編は下記のページをご覧下さい。
Androidアプリのデータ保存方法の一つ「SQLite」の使い方 レコード検索編


まず、Androidでは、データの保存形式として、今回のSQLiteの他にも、「プリファレンス」「ローカルファイル」があります。以前にこれらの記事を書いています。
Androidアプリのデータ保存方法の一つ「プリファレンス」の使い方
Androidアプリのデータ保存方法の一つ「ローカルファイル」の使い方

まずは、この中から何を使ってデータを保持させるかを検討しましょう。
保存するデータ量が明らかに少なくて、プリファレンスで事足りるのであれば、プリファレンスで充分だと思います。
SQLiteは、同じような組み合わせのデータを複数保持させるような、例えば電話帳やブックマーク等のような感じでデータを保存させる場合に使うといいでしょう。


少し話しがそれました。それではSQLiteの使い方を見ていきます。

まず手始めに行う作業は、以下です。

SQLiteOpenHelperのサブクラスの実装

必須ではありませんが、SQLiteOpenHelperのサブクラスの実装を行います。
このクラスは、文字通り、データベースオープン時に使えば便利なクラスです。
HTTP通信や入出力ストリームと同じく、データベースを使用するときは、最初にオープン⇒読み取りや書込み⇒最後にクローズ、という手順を踏みます。

このクラスを使うと発生する利点は、以下のような点です。
・DBオープン時、もしテーブルが存在していないときはテーブル生成を行う事ができる。
・テーブルのバージョンアップが感知できる。

言葉で見てもよくわかりませんので、コードを見てみましょう。
例えば、以下のようなクラスを実装します。


class SubOpenHelper extends SQLiteOpenHelper{

  public SubOpenHelper(Context c,String dbname,int version){
    super(c,dbname,null,version);
  }

  public void onCreate(SQLiteDatabase db){
    db.execSQL(“create table -省略-”);
  }

  public void onUpgrade(
      SQLiteDatabase db,int oldVersion,int newVersion){
    実装例
    //現在のレコードを取得して、一旦メモリへ退避。
    //テーブルの削除
    //新しくテーブルを作り直して、
    //メモリへ退避させたレコードを挿入する etc
  }
}


スポンサーリンク


コンストラクタ

まずはコンストラクタを見ましょう。
スーパークラスのコンストラクタを呼び出してます。
第一引数は、Context型インスタンスです。
自身のアプリを示す場合は、getApplicationContext()を使って生成します。
第二引数は、DBファイル名です。
第四引数は、DBのバージョンで、作成するときに指定できます。
例えば、テーブルの仕様を変えた後であれば、この数値を上げてやる、という使い方が出来ます。
第3引数にはCursorFactory型インスタンスを渡すのですが、とりあえず普通に使う分には使わないのでnullでいいです。(私も良く知らないので、もし余力があれば調べて、別記事で紹介します。)

onCreateメソッド

このインスタンスを使ってデータベースをオープンする時に、もし指定されたデータベースが無い場合に、自動的に実行されるメソッドです。
ここでは、このデータベースで使用するテーブルのCreate文の発行を行えばよいでしょう。
その他、デフォルトで入れておきたいレコードがあれば挿入してやるなどの処理も行えます。

既にデータベースが存在しているときに、このインスタンスが生成されたとしても実行されません。

onUpgradeメソッド

データベースのバージョンが上がったときに自動的に実行されるメソッドです。
このインスタンスを使ってデータベースをオープンするときに、コンストラクタで渡したバージョンと、実際に存在しているデータベースのバージョンが違うときに呼び出され、古いバージョンの値と、新しいバージョンの値は引数としても渡されてきます。
サンプルコードでは、処理の実体を書いていませんが、例えば、テーブルの仕様を変えたという想定ですと、一旦データを取り出しておいてメモリに持たせておき、今のテーブルを消して、新しくテーブルを作り直して、取り出しておいたレコードをちゃんと挿入しなおす、という処理を行う事ができます。
不必要なら処理を書かなくてもいいですが、定義をしておかないとコンパイルが通りません。


こんな感じで、SQLiteOpenHelperを実装しておきます。

データベースのオープン処理

では、次は、データベースにテーブルを作ったり、レコードを挿入したり検索したりする前に、最初に行うべきデータベースのオープン処理をみてみましょう。
以下のようなコードになります。


SQLiteDatabase sdb;
SubOpenHelper helper =
  new SubOpenHelper(getApplicationContext(),”test.db”,1);
try{
  sdb = helper.getWritableDatabase();
  //もしくは、
  //sdb = helper.getReadableDatabase();
}catch(SQLiteException e){
  //異常終了
  return;  
}


まずは、先ほど実装したSQLiteOpenHelperのサブクラスのインスタンスを生成します。
そして、そのインスタンスの、getWritableDatabase()メソッドを使って、データベースをオープンします。
読み取り専用でいいときは、getReadableDatabase()でも使用できます。
もし、getWritableDatabese()でデータベースをオープンしたとき、ディスクの容量が不足していると、SQLiteExceptionがスローされます。
この場合に、もし読み取り専用でいいから開きたい場合は、catch句の中にgetReadableDatabese()を記述してもいいです。


getWritableDatabase()、もしくはgetReadableDatabase()メソッドの戻り値SQLiteDatabase型インスタンスを使って、レコードの挿入や削除、検索を行っていきます。
今回はここまでとしまして、次回にその方法を紹介します。
レコードの追加、更新、削除に関しての記事は下記です。
Androidアプリのデータ保存方法の一つ「SQLite」の使い方 行の追加・更新・削除編
レコードの検索に関しての記事は下記です。
Androidアプリのデータ保存方法の一つ「SQLite」の使い方 レコード検索編


スポンサーリンク

URL :
TRACKBACK URL :

Comments & Trackbacks

  • Comments ( 12 )
  • Trackbacks ( 3 )
  1. AndroidもSQLiteも初心者なので、いつもこのサイトで勉強させてもらってます。
    素人質問でものすごく的外れな質問かもしれませんが、
    一つのDBファイルと一つのテーブルは、一対一対応になっているのでしょうか?
    つまり、複数のテーブルを作成するためには、その都度DBファイルを作成する必要があるのでしょうか?

  2. おー、このような質問コメントは初めてかもしれません。
    的外れな質問だとは、全く思いません。
    初心者ですと、「これって聞くのは恥ずかしい事なんだろーか?」と質問する事を躊躇してしまう事ありますよね。
    私もそうでした。

    ご質問の「DBファイルとテーブルは一対一である必要があるか?」というご質問ですが、答えは「No」です。
    DB(データベース)一つに対して、複数のテーブルを作成できます。
    テーブルを作るたびにDBを新しく作る必要はありません。
    逆に言うと、関連するテーブルであれば、同じデータベースに作成する必要があります。

    例えば、以下のSQLを考えてみれば、
    select * from TABLE_A , TABLE_B
    where TABLE_A.NAME = TABLE_B.NAME

    TABLE_Aと、TABLE_Bは、同じDBにある必要があります。
    余計わかりにくくなったらすみません。

  3. mucchinさん

    早速ご教示頂きありがとうございます。

    むしろ同じデータベースに作成する必要があるのですね。
    よくわかりました。

    初コメントですが、Android関連でググると高頻度でmucchinさんのサイトに辿り着きますので、
    いつもコソコソ拝見させてもらってますw

    ありがとうございました。

  4. 連投申し訳ございません。訂正です…

    >むしろ同じデータベースに作成する必要がある

    と書きましたが、これは「関連するDBの場合は」ですね・・・

    私のコメントを見た方に不十分な情報を与えてしまわないように
    補足しておきます。

    失礼しました・・・

  5. ご丁寧に、どうもありがとうございます。

    全然記事に関連しない事でも、「問い合わせ」からメールを下さっても結構です。
    無視は絶対しません!
    ただ、私でわかるかどうかはわかりませんが…。

  6. mucchinさん初めまして。chitaと申します。
    いつも参考にさせていただいてます。
    自分の調べ方が悪いのだとは思いますが、はっきりしない部分があり質問があります。

    Androidのデータ永続化としてプリファレンス・ローカルファイル・SQLiteがあるというのは理解しました。
    その中のプリファレンスで保存した内容が/data/data/パッケージ名/shared_prefs/ファイル名.xmlに保存されると理解していますが、もしAndroidアプリをアップデートした場合でも、このプリファレンスに保存したものは初期化されずに残っているという理解で大丈夫でしょうか?
    そもそも/data/data/パッケージ名/以下にあるファイルはアプリをアップデートをしてもこちら側が何もしなければプリファレンスもローカルファイルもSQLiteも常にデータとして保持され続けるという理解でよいのでしょうか?

  7. chitaさん
    コメントありがとうございます。
    ご質問の件ですが、仰っておられるように、アプリケーションの更新では、プリファレンス等のデータは保持されているはずです。
    何で得た情報かは忘れてしまいましたが・・・。
    ちなみに実際、バージョンの更新があるAndroidアプリを、Android Marketにてアップデートする際にも、「既存のユーザーデータは保存されます。」と確認ダイアログが表示されます。
    開発者側が意図的に初期化処理を入れない限りは、保持されるものと思われます。
    回答になっていますでしょうか。

  8. 早い回答ありがとうございます。
    Android端末を持っているもののまだ日が浅く、アップデートでそのような確認ダイアログがでるというのも初めて知りました。
    とても参考になりました。
    ありがとうございます。

  9. はじめまして。
    アンドロイド開発初心者ですがSQLiteを使って開発を行っているものです。いつも参考にさせていただいています☆
    「ぬより」さんと同じ質問なのですが、
    実装の過程でDBとファイル名は1対1の関係だと思っていました。その理由としましては、AファイルにAテーブルを作成した後に、AファイルにBテーブルをcreateすると失敗しているみたいで、デー検索や追加などの操作が出来なかったので、AファイルではなくBファイルにBテーブルを作成したところ成功したためです。
    私は複数のテーブルを扱う実装を行っていて、その中のいくつかはリレーション関係にあるテーブルもあるので、ファイルを別にして関係性を保つことは可能なのか、と疑っていました。こちらで確認するとやはりリレーション関係のあるテーブルは同じDBファイルに作成する必要がある、とのことでした。が、作成方法がわかりません。
    その方法を教えていただけませんでしょうか。
    宜しくお願い致します。

    長くってしまい申し訳ありませんでした。

  10. コメントありがとうございます。
    同じDBファイルにテーブルBを作成すると失敗しているみたい、との事なのですが、どんなException或いはErrorが発生しているのでしょうか。
    そのときの失敗している箇所を特定して、例外かエラーかの内容を調べれば原因がわかるのではないでしょうか。
    断言できませんが、同じファイルに作成しようとしているからエラーが起きている、とは限らないと思います。
    わかる範囲でもうちょっとブレイクダウンしてもらって、まだ疑問があるようでしたら、Exceptionの内容等、教えて下さい。
    P.S
    「ぬより」さんじゃなくて、「ぬ」さんですね。「より」は、英語でいうところの「From」にあたるものなので。(笑)

  11. アドバイスありがとうございます☆
    見直してみたところ、おっしゃる通りで同じファイルに作成しようとしていることが問題なのではありませんでした。
    onCreateが呼ばれるタイミングと、実際にテーブルを作成するタイミングが合っておらず、onCreateの際に複数のテーブルを作成することで解決いたしました。
    丁寧な対応ありがとうございました。
    p.s お恥ずかしいです><

    上記とは別件なのですが基本的なjavaについて質問をしてもよろしいでしょうか。
    (以下コードです)
    List tableList;
    tableList = new ArrayList();

    このtableListにオブジェクトをaddしたいのですが、
    オブジェクトの型がSQLQueryAndSoクラスのインターフェイスをimplementしているdbRelationManager = new DBPictureRelationManager();
    dbRelationManager となっています。
    tableList.add(dbRelationManager );とすると型が違う、と怒られてしまうのですが、SQLQueryAndSo型指定でオブジェクトをaddすることは不可能なのでしょうか。
    (他にも同じようなクラスがあり、全てSQLQueryAndSoクラスのインターフェイスをimplementしています。それら10個全てをtableListにaddする実装となっています。)
    基本レベルの質問で申し訳ありません。
    宜しくお願い致します。

    ちか☆

  12. 「ジェネリックス」をご存じでしょうか。
    SQLQueryAndSoという型はオリジナルのクラスでしょうか?
    それはよいとして、SQLQueryAndSoという型を格納する為のArrayListなら、以下のような感じで生成すればよいかと思います。
    ArrayList<SQLQueryAndSo> tableList = new ArrayList<SQLQueryAndSo>();

    ジェネリックスを使わない場合は、Object型のインスタンスが入る、という事になるので、get()かadd()かわかりませんが、適宜キャストする必要が出てきます。
    その為、ジェネリックスを使う事で、そのListが何型のオブジェクトを格納する為のListなのかを、宣言時に決めてやるのです。
    一度試してみてください。
    もしダメならまたコメント下さい。

Leave a Reply

*
* (公開されません)
*

Return Top