4.5.1 示例代码
4.5.1.1 创建/操作数据库
在 Android 应用中处理数据库时,可以通过使用SQLiteOpenHelper [10] 来实现数据库文件的适当安排和访问权限设置(拒绝其他应用访问的设置)。 下面是一个简单的应用示例,它在启动时创建数据库,并通过 UI 执行搜索/添加/更改/删除数据。 示例代码完成了 SQL 注入的防范,来避免来自外部的输入执行不正确的 SQL。
[10] 对于文件存储,可以将绝对文件路径指定为
SQLiteOpenHelper构造函数的第二个参数(名称)。 因此,如果指定了 SD 卡路径,则需要注意,存储的文件可以被其他应用读取和写入。

SQLiteOpenHelper应该用于创建数据库。使用占位符。
根据应用要求验证输入值。
SampleDbOpenHelper.java
package org.jssec.android.sqlite;import android.content.Context;import android.database.SQLException;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import android.util.Log;import android.widget.Toast;public class SampleDbOpenHelper extends SQLiteOpenHelper {private SQLiteDatabase mSampleDb; //Database to store the data to be handledpublic static SampleDbOpenHelper newHelper(Context context) {//*** POINT 1 *** SQLiteOpenHelper should be used for database creation.return new SampleDbOpenHelper(context);}public SQLiteDatabase getDb() {return mSampleDb;}//Open DB by Writable modepublic void openDatabaseWithHelper() {try {if (mSampleDb != null && mSampleDb.isOpen()) {if (!mSampleDb.isReadOnly())// Already opened by writable modereturn;mSampleDb.close();}mSampleDb = getWritableDatabase(); //It's opened here.} catch (SQLException e) {//In case fail to construct database, output to logLog.e(mContext.getClass().toString(), mContext.getString(R.string.DATABASE_OPEN_ERROR_MESSAGE));Toast.makeText(mContext, R.string.DATABASE_OPEN_ERROR_MESSAGE, Toast.LENGTH_LONG).show();}}//Open DB by ReadOnly mode.public void openDatabaseReadOnly() {try {if (mSampleDb != null && mSampleDb.isOpen()) {if (mSampleDb.isReadOnly())// Already opened by ReadOnly.return;mSampleDb.close();}SQLiteDatabase.openDatabase(mContext.getDatabasePath(CommonData.DBFILE_NAME).getPath(), null, SQLiteDatabase.OPEN_READONLY);} catch (SQLException e) {//In case failed to construct database, output to logLog.e(mContext.getClass().toString(), mContext.getString(R.string.DATABASE_OPEN_ERROR_MESSAGE));Toast.makeText(mContext, R.string.DATABASE_OPEN_ERROR_MESSAGE, Toast.LENGTH_LONG).show();}}//Database Closepublic void closeDatabase() {try {if (mSampleDb != null && mSampleDb.isOpen()) {mSampleDb.close();}} catch (SQLException e) {//In case failed to construct database, output to logLog.e(mContext.getClass().toString(), mContext.getString(R.string.DATABASE_CLOSE_ERROR_MESSAGE));Toast.makeText(mContext, R.string.DATABASE_CLOSE_ERROR_MESSAGE, Toast.LENGTH_LONG).show();}}//Remember Contextprivate Context mContext;//Table creation commandprivate static final String CREATE_TABLE_COMMANDS= "CREATE TABLE " + CommonData.TABLE_NAME + " ("+ "_id INTEGER PRIMARY KEY AUTOINCREMENT, "+ "idno INTEGER UNIQUE, "+ "name VARCHAR(" + CommonData.TEXT_DATA_LENGTH_MAX + ") NOT NULL, "+ "info VARCHAR(" + CommonData.TEXT_DATA_LENGTH_MAX + ")"+ ");";public SampleDbOpenHelper(Context context) {super(context, CommonData.DBFILE_NAME, null, CommonData.DB_VERSION);mContext = context;}@Overridepublic void onCreate(SQLiteDatabase db) {try {db.execSQL(CREATE_TABLE_COMMANDS); //Execute DB construction command} catch (SQLException e) {//In case failed to construct database, output to logLog.e(this.getClass().toString(), mContext.getString(R.string.DATABASE_CREATE_ERROR_MESSAGE));}}@Overridepublic void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {// It's to be executed when database version up. Write processes like data transition.}}
DataSearchTask.java(SQLite 数据库项目)
package org.jssec.android.sqlite.task;import org.jssec.android.sqlite.CommonData;import org.jssec.android.sqlite.DataValidator;import org.jssec.android.sqlite.MainActivity;import org.jssec.android.sqlite.R;import android.database.Cursor;import android.database.SQLException;import android.database.sqlite.SQLiteDatabase;import android.os.AsyncTask;import android.util.Log;//Data search taskpublic class DataSearchTask extends AsyncTask<String, Void, Cursor> {private MainActivity mActivity;private SQLiteDatabase mSampleDB;public DataSearchTask(SQLiteDatabase db, MainActivity activity) {mSampleDB = db;mActivity = activity;}@Overrideprotected Cursor doInBackground(String... params) {String idno = params[0];String name = params[1];String info = params[2];String cols[] = {"_id", "idno","name","info"};Cursor cur;//*** POINT 3 *** Validate the input value according the application requirements.if (!DataValidator.validateData(idno, name, info)){return null;}//When all parameters are null, execute all searchif ((idno == null || idno.length() == 0) &&(name == null || name.length() == 0) &&(info == null || info.length() == 0) ) {try {cur = mSampleDB.query(CommonData.TABLE_NAME, cols, null, null, null, null, null);} catch (SQLException e) {Log.e(DataSearchTask.class.toString(), mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));return null;}return cur;}//When No is specified, execute searching by Noif (idno != null && idno.length() > 0) {String selectionArgs[] = {idno};try {//*** POINT 2 *** Use place holder.cur = mSampleDB.query(CommonData.TABLE_NAME, cols, "idno = ?", selectionArgs, null, null, null);} catch (SQLException e) {Log.e(DataSearchTask.class.toString(), mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));return null;}return cur;}//When Name is specified, execute perfect match search by Nameif (name != null && name.length() > 0) {String selectionArgs[] = {name};try {//*** POINT 2 *** Use place holder.cur = mSampleDB.query(CommonData.TABLE_NAME, cols, "name = ?", selectionArgs, null, null, null);} catch (SQLException e) {Log.e(DataSearchTask.class.toString(), mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));return null;}return cur;}//Other than above, execute partly match searching with the condition of info.String argString = info.replaceAll("@", "@@"); //Escape $ in info which was received as input.argString = argString.replaceAll("%", "@%"); //Escape % in info which was received as input.argString = argString.replaceAll("_", "@_"); //Escape _ in info which was received as input.String selectionArgs[] = {argString};try {//*** POINT 2 *** Use place holder.cur = mSampleDB.query(CommonData.TABLE_NAME, cols, "info LIKE '%' || ? || '%' ESCAPE '@'", selectionArgs, null, null, null);} catch (SQLException e) {Log.e(DataSearchTask.class.toString(), mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));return null;}return cur;}@Overrideprotected void onPostExecute(Cursor resultCur) {mActivity.updateCursor(resultCur);}}
DataValidator.java
package org.jssec.android.sqlite;public class DataValidator {//Validate the Input value//validate numeric characterspublic static boolean validateNo(String idno) {//null and blank are OKif (idno == null || idno.length() == 0) {return true;}//Validate that it's numeric character.try {if (!idno.matches("[1-9][0-9]*")) {//Error if it's not numeric valuereturn false;}} catch (NullPointerException e) {//Detected an errorreturn false;}return true;}// Validate the length of a character stringpublic static boolean validateLength(String str, int max_length) {//null and blank are OKif (str == null || str.length() == 0) {return true;}//Validate the length of a character string is less than MAXtry {if (str.length() > max_length) {//When it's longer than MAX, errorreturn false;}} catch (NullPointerException e) {//Bugreturn false;}return true;}// Validate the Input valuepublic static boolean validateData(String idno, String name, String info) {if (!validateNo(idno)) {return false;}if (!validateLength(name, CommonData.TEXT_DATA_LENGTH_MAX)) {return false;}else if(!validateLength(info, CommonData.TEXT_DATA_LENGTH_MAX)) {return false;}return true;}}
