This document written: 2013-07-30 .. 2014-05-28

Android プログラミング - SQLDroid でハッピーに Android/SQLite ライフ

日本語情報で SQLDroid を JDBC ドライバーとして利用している具体例が見つからず、否定的な情報(使えない等)しか見かけなかったので、少々不安ではあったが、実際にやってみたらちゃんと使えた。


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends Activity {
	private static final String dbFile = "Question.db";
	private static final int dbFileId = R.raw.question;
	private File dbPath;
	private InputStream inFile;
	private OutputStream outFile;
	private byte[] buffer = new byte[1024];
	private String url = "jdbc:sqldroid:";
	private static final String sqldroidDriver = "org.sqldroid.SQLDroidDriver";
	private Statement statement;
	private ResultSet result;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		/*
		 * 予め用意した SQLite データベースファイルを res/raw 以下からコピーする。
		 * res/raw に配置するファイルには拡張子(やサブフォルダーも?)は使えないようだ。
		 * 
		 * Android 用の SQLite データベースファイルを PC の SQLite3 で作成するにあたって注意すべき点は、
		 * _id (Integer)をプライマリーキーとする点(これは必須ではなく、特定の Android の SQLite API で
		 * プライマリーキー= _id と決め付けている問題児が存在するという話。SQLDroid を使う場合どうでもいいと思う)
		 * android_metadata (locale TEXT) という名前のテーブルを作成し、
		 * そこに 'ja_JP' というエントリーを含めておくことである(存在しなければ、自動的に作成されるようだ)。
		 */
		dbPath = getDatabasePath(dbFile);
		try {
			//inFile = getAssets().open("databases/" + dbFile);
			inFile = getResources().openRawResource(dbFileId);
			if (!dbPath.getParentFile().exists()) {
				dbPath.getParentFile().mkdir();
			}
			outFile = new FileOutputStream(dbPath);
			int length;
			while ((length = inFile.read(buffer)) > 0) {
				outFile.write(buffer, 0, length);
			}
		} catch (IOException e) {
			Log.e("File", e.getMessage());
		} finally {
			try {
				outFile.flush();
				outFile.close();
				inFile.close();
			} catch (IOException e) {
				Log.e("File", e.getMessage());
			}
		}
		
		/*
		 * SQLDroid を JDBC ドライバーとして利用する。
		 * 不思議なことに、現時点で SQLDroid を使っているという日本語の情報が存在せず、
		 * Android の独自 API を直接利用しているサンプル情報しか見付からないので、
		 * 少々不安になったが、普通に使えるようになっている。
		 * もちろん、Java Build Path で Library として SQLDroid の jar を含めることが必要なのは
		 * 言うまでもない。(jar はプロジェクトフォルダーの libs 辺りに置いておいておけばよし)
		 */
		url += dbPath;
		try {
			Class.forName(sqldroidDriver).newInstance();
			statement = DriverManager.getConnection(url).createStatement();
			result = statement.executeQuery("select question from Question");
			while (result.next()) {
				Log.d("DB", "Q: " + result.getString("question"));
			}
		} catch (InstantiationException e) {
			Log.e("DB", e.getMessage());
		} catch (IllegalAccessException e) {
			Log.e("DB", e.getMessage());
		} catch (ClassNotFoundException e) {
			Log.e("DB", e.getMessage());
		} catch (SQLException e) {
			Log.e("DB", e.getMessage());
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

このサンプルでは、Question という表を持つ SQLite データベースファイルから question という列の値を検索して、LogCat に表示している。

最初ハマったのは、そもそも res や assets からのファイルのコピーが正しくできず、それが原因で「データベースが破損しているので、空のデータベースファイルを作成し直した」というエラーが発生し、データベースの操作のあたりに問題があるのかと思って延々悩んでいたら、実は、「単にファイルコピー自体に失敗していて、空ファイルをコピーしていた」というオチ。.read(buffer) を .read() としていたのが原因だった。

俄 Android 開発者なため、DDMS の使い方(存在)すら知らなかったので、この原因を突き止めるために、DDMS というものが使える(エミュレーター上のファイルなどを色々と覗いたりできる)こと自体を知ったのは実に(!)、大きな収穫だった。


Android