4.5.1 示例代码

4.5.1.1 创建/操作数据库

在 Android 应用中处理数据库时,可以通过使用SQLiteOpenHelper [10] 来实现数据库文件的适当安排和访问权限设置(拒绝其他应用访问的设置)。 下面是一个简单的应用示例,它在启动时创建数据库,并通过 UI 执行搜索/添加/更改/删除数据。 示例代码完成了 SQL 注入的防范,来避免来自外部的输入执行不正确的 SQL。

[10] 对于文件存储,可以将绝对文件路径指定为SQLiteOpenHelper构造函数的第二个参数(名称)。 因此,如果指定了 SD 卡路径,则需要注意,存储的文件可以被其他应用读取和写入。

4.5.1.md - 图1

  1. SQLiteOpenHelper应该用于创建数据库。

  2. 使用占位符。

  3. 根据应用要求验证输入值。

SampleDbOpenHelper.java

  1. package org.jssec.android.sqlite;
  2. import android.content.Context;
  3. import android.database.SQLException;
  4. import android.database.sqlite.SQLiteDatabase;
  5. import android.database.sqlite.SQLiteOpenHelper;
  6. import android.util.Log;
  7. import android.widget.Toast;
  8. public class SampleDbOpenHelper extends SQLiteOpenHelper {
  9. private SQLiteDatabase mSampleDb; //Database to store the data to be handled
  10. public static SampleDbOpenHelper newHelper(Context context) {
  11. //*** POINT 1 *** SQLiteOpenHelper should be used for database creation.
  12. return new SampleDbOpenHelper(context);
  13. }
  14. public SQLiteDatabase getDb() {
  15. return mSampleDb;
  16. }
  17. //Open DB by Writable mode
  18. public void openDatabaseWithHelper() {
  19. try {
  20. if (mSampleDb != null && mSampleDb.isOpen()) {
  21. if (!mSampleDb.isReadOnly())// Already opened by writable mode
  22. return;
  23. mSampleDb.close();
  24. }
  25. mSampleDb = getWritableDatabase(); //It's opened here.
  26. } catch (SQLException e) {
  27. //In case fail to construct database, output to log
  28. Log.e(mContext.getClass().toString(), mContext.getString(R.string.DATABASE_OPEN_ERROR_MESSAGE));
  29. Toast.makeText(mContext, R.string.DATABASE_OPEN_ERROR_MESSAGE, Toast.LENGTH_LONG).show();
  30. }
  31. }
  32. //Open DB by ReadOnly mode.
  33. public void openDatabaseReadOnly() {
  34. try {
  35. if (mSampleDb != null && mSampleDb.isOpen()) {
  36. if (mSampleDb.isReadOnly())// Already opened by ReadOnly.
  37. return;
  38. mSampleDb.close();
  39. }
  40. SQLiteDatabase.openDatabase(mContext.getDatabasePath(CommonData.DBFILE_NAME).getPath(), null, SQLiteDatabase.OPEN_READONLY);
  41. } catch (SQLException e) {
  42. //In case failed to construct database, output to log
  43. Log.e(mContext.getClass().toString(), mContext.getString(R.string.DATABASE_OPEN_ERROR_MESSAGE));
  44. Toast.makeText(mContext, R.string.DATABASE_OPEN_ERROR_MESSAGE, Toast.LENGTH_LONG).show();
  45. }
  46. }
  47. //Database Close
  48. public void closeDatabase() {
  49. try {
  50. if (mSampleDb != null && mSampleDb.isOpen()) {
  51. mSampleDb.close();
  52. }
  53. } catch (SQLException e) {
  54. //In case failed to construct database, output to log
  55. Log.e(mContext.getClass().toString(), mContext.getString(R.string.DATABASE_CLOSE_ERROR_MESSAGE));
  56. Toast.makeText(mContext, R.string.DATABASE_CLOSE_ERROR_MESSAGE, Toast.LENGTH_LONG).show();
  57. }
  58. }
  59. //Remember Context
  60. private Context mContext;
  61. //Table creation command
  62. private static final String CREATE_TABLE_COMMANDS
  63. = "CREATE TABLE " + CommonData.TABLE_NAME + " ("
  64. + "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
  65. + "idno INTEGER UNIQUE, "
  66. + "name VARCHAR(" + CommonData.TEXT_DATA_LENGTH_MAX + ") NOT NULL, "
  67. + "info VARCHAR(" + CommonData.TEXT_DATA_LENGTH_MAX + ")"
  68. + ");";
  69. public SampleDbOpenHelper(Context context) {
  70. super(context, CommonData.DBFILE_NAME, null, CommonData.DB_VERSION);
  71. mContext = context;
  72. }
  73. @Override
  74. public void onCreate(SQLiteDatabase db) {
  75. try {
  76. db.execSQL(CREATE_TABLE_COMMANDS); //Execute DB construction command
  77. } catch (SQLException e) {
  78. //In case failed to construct database, output to log
  79. Log.e(this.getClass().toString(), mContext.getString(R.string.DATABASE_CREATE_ERROR_MESSAGE));
  80. }
  81. }
  82. @Override
  83. public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
  84. // It's to be executed when database version up. Write processes like data transition.
  85. }
  86. }

DataSearchTask.java(SQLite 数据库项目)

  1. package org.jssec.android.sqlite.task;
  2. import org.jssec.android.sqlite.CommonData;
  3. import org.jssec.android.sqlite.DataValidator;
  4. import org.jssec.android.sqlite.MainActivity;
  5. import org.jssec.android.sqlite.R;
  6. import android.database.Cursor;
  7. import android.database.SQLException;
  8. import android.database.sqlite.SQLiteDatabase;
  9. import android.os.AsyncTask;
  10. import android.util.Log;
  11. //Data search task
  12. public class DataSearchTask extends AsyncTask<String, Void, Cursor> {
  13. private MainActivity mActivity;
  14. private SQLiteDatabase mSampleDB;
  15. public DataSearchTask(SQLiteDatabase db, MainActivity activity) {
  16. mSampleDB = db;
  17. mActivity = activity;
  18. }
  19. @Override
  20. protected Cursor doInBackground(String... params) {
  21. String idno = params[0];
  22. String name = params[1];
  23. String info = params[2];
  24. String cols[] = {"_id", "idno","name","info"};
  25. Cursor cur;
  26. //*** POINT 3 *** Validate the input value according the application requirements.
  27. if (!DataValidator.validateData(idno, name, info)){
  28. return null;
  29. }
  30. //When all parameters are null, execute all search
  31. if ((idno == null || idno.length() == 0) &&
  32. (name == null || name.length() == 0) &&
  33. (info == null || info.length() == 0) ) {
  34. try {
  35. cur = mSampleDB.query(CommonData.TABLE_NAME, cols, null, null, null, null, null);
  36. } catch (SQLException e) {
  37. Log.e(DataSearchTask.class.toString(), mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));
  38. return null;
  39. }
  40. return cur;
  41. }
  42. //When No is specified, execute searching by No
  43. if (idno != null && idno.length() > 0) {
  44. String selectionArgs[] = {idno};
  45. try {
  46. //*** POINT 2 *** Use place holder.
  47. cur = mSampleDB.query(CommonData.TABLE_NAME, cols, "idno = ?", selectionArgs, null, null, null);
  48. } catch (SQLException e) {
  49. Log.e(DataSearchTask.class.toString(), mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));
  50. return null;
  51. }
  52. return cur;
  53. }
  54. //When Name is specified, execute perfect match search by Name
  55. if (name != null && name.length() > 0) {
  56. String selectionArgs[] = {name};
  57. try {
  58. //*** POINT 2 *** Use place holder.
  59. cur = mSampleDB.query(CommonData.TABLE_NAME, cols, "name = ?", selectionArgs, null, null, null);
  60. } catch (SQLException e) {
  61. Log.e(DataSearchTask.class.toString(), mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));
  62. return null;
  63. }
  64. return cur;
  65. }
  66. //Other than above, execute partly match searching with the condition of info.
  67. String argString = info.replaceAll("@", "@@"); //Escape $ in info which was received as input.
  68. argString = argString.replaceAll("%", "@%"); //Escape % in info which was received as input.
  69. argString = argString.replaceAll("_", "@_"); //Escape _ in info which was received as input.
  70. String selectionArgs[] = {argString};
  71. try {
  72. //*** POINT 2 *** Use place holder.
  73. cur = mSampleDB.query(CommonData.TABLE_NAME, cols, "info LIKE '%' || ? || '%' ESCAPE '@'", selectionArgs, null, null, null);
  74. } catch (SQLException e) {
  75. Log.e(DataSearchTask.class.toString(), mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));
  76. return null;
  77. }
  78. return cur;
  79. }
  80. @Override
  81. protected void onPostExecute(Cursor resultCur) {
  82. mActivity.updateCursor(resultCur);
  83. }
  84. }

DataValidator.java

  1. package org.jssec.android.sqlite;
  2. public class DataValidator {
  3. //Validate the Input value
  4. //validate numeric characters
  5. public static boolean validateNo(String idno) {
  6. //null and blank are OK
  7. if (idno == null || idno.length() == 0) {
  8. return true;
  9. }
  10. //Validate that it's numeric character.
  11. try {
  12. if (!idno.matches("[1-9][0-9]*")) {
  13. //Error if it's not numeric value
  14. return false;
  15. }
  16. } catch (NullPointerException e) {
  17. //Detected an error
  18. return false;
  19. }
  20. return true;
  21. }
  22. // Validate the length of a character string
  23. public static boolean validateLength(String str, int max_length) {
  24. //null and blank are OK
  25. if (str == null || str.length() == 0) {
  26. return true;
  27. }
  28. //Validate the length of a character string is less than MAX
  29. try {
  30. if (str.length() > max_length) {
  31. //When it's longer than MAX, error
  32. return false;
  33. }
  34. } catch (NullPointerException e) {
  35. //Bug
  36. return false;
  37. }
  38. return true;
  39. }
  40. // Validate the Input value
  41. public static boolean validateData(String idno, String name, String info) {
  42. if (!validateNo(idno)) {
  43. return false;
  44. }
  45. if (!validateLength(name, CommonData.TEXT_DATA_LENGTH_MAX)) {
  46. return false;
  47. }else if(!validateLength(info, CommonData.TEXT_DATA_LENGTH_MAX)) {
  48. return false;
  49. }
  50. return true;
  51. }
  52. }