AndroidでSQLiteをつかう

            Androidのデータ保存方法のひとつにSQLiteがある。

wikipediaより引用 SQLiteMySQLPostgreSQLと同じデータベース管理システムであるが、サーバとしてではなくアプリケーションに組み込んで利用される軽量のデータベースである。

SQLが使えるのでそれなりに複雑なことができるし、電源を落としたからデータが消えるとかいうこともない。 消えてほしくないデータをごにょごにょするにはSQLiteがつかえる。 ちなみにiOSでも使える。

SQLiteを操作するライブラリはAndroid SDKで提供されているのでそれを使うことになる。 http://developer.android.com/reference/android/database/package-summary.html http://developer.android.com/reference/android/database/sqlite/package-summary.html

ということで、テスト的に書いてみた。

SQLitePlugin.java

[java] package tnnsst35.me.SQLiteTest;

import java.util.regex.Matcher; import java.util.regex.Pattern;

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

public class SQLitePlugin { public void execute(String sql, Context ctx) { String result = ""; // Results of SQL

    // SQLがなければ終了
    if (sql.length() == 0) {
        result = "No SQL";
        return;
    }

    SQLiteHelper helper = new SQLiteHelper(ctx, " tnnsst35.db", 1);
    SQLiteDatabase db;

    // Open Database
    try {
        // SQLiteを書き込み可能で開く
        db = helper.getWritableDatabase();
    } catch (SQLiteException e) {
        result = "Failed to execute SQL : " + e.getMessage();
        return;
    }

    // SELECT文以外はexecSQLで実行できる
    // 今回はINSERT, UPDATE, DELETE, CREATE, DROPで始まるSQLだけを許可するようにしている
    // INSERT, UPDATE, DELETE, CREATE, DROP
    if (this.isMatch(sql, "^INSERT|^UPDATE|^DELETE|^CREATE|^DROP")) {
        try {
            db.execSQL(sql);
            result = "Success to execute SQL";
        } catch (SQLiteException e) {
            result = "Failed to execute SQL : " + e.getMessage();
        }
    }

    // SELECT文はrawQueryで実行できる
    // SELECT
    if (this.isMatch(sql, "^SELECT")) {
        try {
            Cursor c = db.rawQuery(sql, null);
            boolean isEof = c.moveToFirst();
            result += "[";
            while (isEof) {
                result += "{";                  
                for (int i = 0;i < c.getColumnCount();i++) {
                    if (i != 0) result += ",";
                    result += "\"" + c.getColumnName(i) +  "\":\"" + c.getString(i) + "\""; 
                }
                result += "}";
                isEof = c.moveToNext();
                if (isEof) result += ",";
            }
            result += "]";
            c.close();
        } catch (SQLiteException e) {
            result = "Failed to execute SQL : " + e.getMessage();
        }
    }

    // Close Database
    db.close();

    System.out.println(result);
}

private boolean isMatch(String str, String regex) {
    Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher(str);
    return m.find();
}

public class SQLiteHelper extends SQLiteOpenHelper {
    public SQLiteHelper(Context c, String dbName, int version) {
        super(c, dbName, null, version);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }
}

} [/java]

SQLiteTestActivity .java

[java] package tnnsst35.me.SQLiteTest;

import android.app.Activity; import android.os.Bundle;

import tnnsst35.me.SQLiteTest.SQLitePlugin;

public class SQLiteTestActivity extends Activity { /* Called when the activity is first created. / @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

    SQLitePlugin sqlite = new SQLitePlugin();

    sqlite.execute(
            "CREATE TABLE IF NOT EXISTS tests (" +
            "str text," +
            "str2 text" +
            ")",
            getApplicationContext()
            );

    sqlite.execute(
            "INSERT INTO tests VALUES ('hoge', 'hoge');",
            getApplicationContext()
            );

    sqlite.execute(
            "INSERT INTO tests VALUES ('fuga', 'fuga');",
            getApplicationContext()
            );

    sqlite.execute(
            "UPDATE tests SET str2 = 'foo' WHERE str2 = 'fuga';",
            getApplicationContext()
            );

    sqlite.execute(
            "SELECT * FROM tests;",
            getApplicationContext()
            );

    sqlite.execute(
            "DROP TABLE tests;",
            getApplicationContext()
            );
}

@Override
protected void onDestroy() {
    super.onDestroy();
}

} [/java]

そんなにきちんと書いていないが、まぁこんなもんだと思えばよいと思う。

いちおう補足。

SELECT文の結果はJSONっぽい文字列に整形するようにしている。 例えば、今回のコードを実行すると [{"str":"hoge","str2":"hoge"},{"str":"fuga","str2":"foo"}] が標準出力される。

SQLiteDatabaseには「insert」「update」「delete」「query」などの関数が用意されていて、それを使うとSQL分を書かずにデータを操作できる。 僕は日頃からSQLを書いていて、上記の関数を使う方が面倒なので「execSQL」「rawQuery」を使うようにした。

またお気づきだとは思うが、このコードだとプレースホルダを全く使ってないのでSQLインジェクションという脆弱性を抱えている。 これについてはSQLiteStatement、SQLiteQueryBuilderとかいうそれっぽいのがあるので調べてみるとよいかと。 僕も調べます。

記事を書いといてなんだが、mucchinのAndroid戦記 - Androidアプリのデータ保存方法の一つ「SQLite」の使い方 がわかりやすいので詳しくはこちらを参照するとよいと思う。

おわり。