アンドロイドのデータベースについて
SQlite
データベースからデータの読み込み
Cursorクラス
//例えばこんなデータベースがあるとして(SQLiteOpenHelperクラスを継承したもの) Database dataBase = new Database(context);
//データベースからとってくるカラム final String[] PROJECTION = { Database.ID , Database.NAME }; //データベースからデータをCursorで取ってくる Cursorでデータをとってきます。 //注意 定数でDatabaseクラスの中にあるとします public static final String ID = "__id";
//カラムは何番目かをとってくる
int idxID = c.getColumnIndex(Database.ID);
int idxName = c.getColumnIndex(Database.NAME); //これを忘れると、カーソル-1番目から始まってエラー c.moveToFirst(); if ( c.getCount() > 0 ) //カーソルのデータ数取得 { do { int id = c.getInt(idxID);
String name = c.getString(idxName); //なにかの処理 } while ( c.moveToNext() ); } //クローズしないとリーク! c.close(); CursorをmoveToFirst()を最初にやって、 moveToNext()で次のデータへ進めます。 Cursorはイテレータです。
//データベースからとってくるカラム final String[] PROJECTION = { Database.ID , Database.NAME }; SQLiteDatabase.query( テーブル名 , カラムの名前が入ったString配列 (上のPROJECTION) , クエリー , SelectionArgs(String配列) , GroupBy , Having , ソート順 ); >クエリの発行 ( SQLiteDatabaseクラス ) SelectionArgsはクエリーのところで、“ID = ? or price = ?”として String[] selectionArgs = { “10” , “100” }; を設定すると、? のとこが左から順に置き換わる。 ID = 10 or Price = 100 となる この順番でCursorに値がはいる
>Cursorについて カーソルのインデックス番号0番に戻す moveToFirst() カーソルのインデックス番号を次へ進める moveToNext() カーソルのインデックス番号をNum番まで進める MoveToPosition( Num ) カーソルのRowの数を得る(データの数) getCount() カラム番号の値をそれぞれの型で受け取る getInt( カラム番号 ) getString( カラム番号 ) getFloat( カラム番号 ) getBoolean( カラム番号 ) カーソルのカラム名から、何番目にアクセスすればいいかを得る getColumnIndex( カラムの名前(String) ) カーソルを閉じる close()
Cursorをクローズしなければ、DDMSで表示されます。
緑文字(info)なので、メモリリークにはなってないかもしれません。 (OS1.6では赤字(error)でleak found!と表示されていた)
Cursorからデータを取ってくるとき、インデックスの番号で取得 することになります。2通りの方法があります。
①カラムの番号を直接いれる
String name = Cursor.getString(1);
②最初に一度、カラムの番号をカラム名で問い合わせておく (Database.Nameはstatic final String Name = “_name”;) Int idxName = Cursor.getColumnIndex( Database.Name ); String name = Cursor.getString( idxName );
結果は同じですが・・・
①はあとでソースを見たときにさっぱりわからない。 ②がお勧めです。
Cursorの中身をデバッグなどで簡単に表示したい場合 Logクラスを使用すると、ログ出力できます。DDMSなどで確認できます。 しかし、自前でログ出力を作りたくない・・・・そんなとき! ログ出力の際に、Cursorの中身をログ出力してくれる便利なクラス android.database.DatabaseUtils.dumpCursor(cursor); ログ出力の 中身
Cursorを自分で作りたい場合
final String[] projection = { "id","color" };
MatrixCursor c = new MatrixCursor(projection); c.addRow( new Object[]{ 1,"blue" });
c.addRow( new Object[]{ 2,"red" }); c.addRow( new Object[]{ 3,"green" }); c.addRow( new Object[]{ 4,"pink" }); c.addRow( new Object[]{ 5,"white" });
MatrixCursorを使えば作れますが・・・・ なかなか用途は思いつきません(汗
データベースクラスの作り方
データベースを操作する前段階として、面倒な問題があります。 データベースのファイルはすでに存在するか ない場合は、ファイル作成、テーブル作成 データベースのバージョンは同じか 同じでないなら、アップグレード(テーブル、カラムの追加等) Cursorでデータの読み出し
SQLiteOpenHelperクラスはそれをやってくれます。 public class Database extends SQLiteOpenHelper {
public Database(Context context, String name, CursorFactory factory,int version) { super(context, name, factory, version);
// name は、ファイル名(ファイル名のみ!ファイルパス付は不可) // factory は不明(汗
// version はデータベースバージョン(自分で決める) }
@Override
public void onCreate(SQLiteDatabase db) { // ファイルがない場合に呼ばれる
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // バージョンが違うときに呼ばれる
データベースのファイルはすでに存在するか ない場合は、ファイル作成、テーブル作成 データベースのバージョンは同じか 同じでないなら、アップグレード(テーブル、カラムの追加等) Cursorでデータの読み出し onCreate() OnUpgrade() SQLiteOpenHelperを実装したとき
端末内のデータベースファイルからの読み出しか、 メモリ上のデータベースしか操作できない、
data/data/ アプリ / databaseにあるdata.dbを使う New Database( context , “data.db” , null , 1 ); メモリ上だけで使う
New Database( context , null , null , 1 );
OnUpgrade()でアップグレード処理を書いても、 インストールされたときはonCreate()しか呼ばれない onCreate()に最終的なバージョンのテーブル作成 などの処理を書かないと、インストールした人はみ んな開いた瞬間エラー! 注意点
public class Database extends SQLiteOpenHelper { protected SQLiteDatabase mWritable = null; protected SQLiteDatabase mReadable = null;
public Database(Context context, String name, CursorFactory factory,int version) { super(context, name, factory, version);
mWritable = getWritableDatabase(); mReadable = getReadableDatabase(); }
//以下はSQLiteHelperクラスには用意されていない 自作です
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
Cursor c = mReadable.query(table, columns, selection, selectionArgs, groupBy, having, orderBy); c.moveToFirst();
return c; }
public void update(String table, ContentValues values, String whereClause) {
mWritable.update(table, values, whereClause, null); }
public void insert(String table, ContentValues values) {
mWritable.insert(table, null , values); }
public void delete(String table, String whereClause) {
public Database(Context context, String name, CursorFactory factory,int version) { super(context, name, factory, version);
mWritable = getWritableDatabase(); mReadable = getReadableDatabase(); }
@Override
public synchronized void close() { super.close();
mWritable.close(); mReadable.close(); }
定期的にこれしないと、deleteされたデータなんかが残って、ファイルサイズが でかくなります
public void vacuum() {
mWritable.execSQL("vacuum"); }
高速にデータを取り出して、加工して、挿入! db.beginTransaction();
try {
SQLiteStatement stmt = db.compileStatement("insert into table values (?);");
for (String s : array) { stmt.bindString(1, s); stmt.executeInsert(); } //これを呼びださなければ、ロールバックされる db.setTransactionSuccessful(); } finally { db.endTransaction(); }
ContentsProvider
Cursorクラス
public class main extends Activity { @Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //SDカードの曲を取得 Cursor c = this.getContentResolver().query( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
コンテンツプロバイダを使えば、1行で簡単取得! URIで、データを取得できます。
マニュフェストファイルに、URIを書きこむ!
<provider android:authorities="jp.test.testDatabase" android:name=".DataProvider" />
ContentProviderを継承して、Query , insert , update などのメソッドを書くだけで使え ます!
public class DataProvider extends ContentProvider { @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Query , Insert , update , delete 以外にもなにかやりたければ、 URIにごりごり追加して、URI解析してやります。
URIMATCHER = new UriMatcher(UriMatcher.NO_MATCH); URIMATCHER.addURI(AUTHORITY, "ほかの処理1", 1); URIMATCHER.addURI(AUTHORITY, "ほかの処理2", 2); int type = URIMATCHER.match(uri);
switch ( type ) { Case 1: ほかの処理1 break; Case 2: ほかの処理2 break; }