保存到数据库

    对于重复或者结构化的数据(如联系人信息)等保存到DB是个不错的主意。本课假定读者已经熟悉SQL数据库的常用操作。在Android上可能会使用到的APIs,可以从android.database.sqlite包中找到。

    SQL中一个重要的概念是schema:一种DB结构的正式声明,用于表示database的组成结构。schema是从创建DB的SQL语句中生成的。我们会发现创建一个伴随类(companion class)是很有益的,这个类称为合约类(contract class),它用一种系统化并且自动生成文档的方式,显示指定了schema样式。

    Contract Clsss是一些常量的容器。它定义了例如URIs,表名,列名等。这个contract类允许在同一个包下与其他类使用同样的常量。 它让我们只需要在一个地方修改列名,然后这个列名就可以自动传递给整个code。

    组织contract类的一个好方法是在类的根层级定义一些全局变量,然后为每一个table来创建内部类。

    例如,下面的例子定义了表名与该表的列名:

    使用SQL Helper创建DB

    定义好了的DB的结构之后,就应该实现那些创建与维护db和table的方法。下面是一些典型的创建与删除table的语句。

    1. private static final String COMMA_SEP = ",";
    2. private static final String SQL_CREATE_ENTRIES =
    3. "CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" +
    4. FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
    5. FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
    6. FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    7. ... // Any other options for the CREATE command
    8. " )";
    9. private static final String SQL_DELETE_ENTRIES =
    10. "DROP TABLE IF EXISTS " + TABLE_NAME_ENTRIES;

    SQLiteOpenHelper类中有一些很有用的APIs。当使用这个类来做一些与db有关的操作时,系统会对那些有可能比较耗时的操作(例如创建与更新等)在真正需要的时候才去执行,而不是在app刚启动的时候就去做那些动作。我们所需要做的仅仅是执行或者getReadableDatabase().

    为了使用 SQLiteOpenHelper, 需要创建一个子类并重写, onUpgrade()与等callback方法。也许还需要实现onDowngrade(), 但这并不是必需的。

    例如,下面是一个实现了SQLiteOpenHelper 类的例子:

    1. public class FeedReaderDbHelper extends SQLiteOpenHelper {
    2. // If you change the database schema, you must increment the database version.
    3. public static final int DATABASE_VERSION = 1;
    4. public static final String DATABASE_NAME = "FeedReader.db";
    5. public FeedReaderDbHelper(Context context) {
    6. super(context, DATABASE_NAME, null, DATABASE_VERSION);
    7. }
    8. public void onCreate(SQLiteDatabase db) {
    9. db.execSQL(SQL_CREATE_ENTRIES);
    10. }
    11. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    12. // This database is only a cache for online data, so its upgrade policy is
    13. // to simply to discard the data and start over
    14. onCreate(db);
    15. }
    16. public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    17. onUpgrade(db, oldVersion, newVersion);
    18. }
    19. }

    为了访问我们的db,需要实例化 SQLiteOpenHelper的子类:

    通过传递一个 对象到insert()方法:

    1. SQLiteDatabase db = mDbHelper.getWritableDatabase();
    2. // Create a new map of values, where column names are the keys
    3. ContentValues values = new ContentValues();
    4. values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, id);
    5. values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
    6. values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_CONTENT, content);
    7. // Insert the new row, returning the primary key value of the new row
    8. long newRowId;
    9. newRowId = db.insert(
    10. FeedReaderContract.FeedEntry.TABLE_NAME,
    11. FeedReaderContract.FeedEntry.COLUMN_NAME_NULLABLE,
    12. values);

    insert()方法的第一个参数是table名,第二个参数会使得系统自动对那些ContentValues 没有提供数据的列填充数据为null,如果第二个参数传递的是null,那么系统则不会对那些没有提供数据的列进行填充。

    从DB中读取信息

    1. SQLiteDatabase db = mDbHelper.getReadableDatabase();
    2. // Define a projection that specifies which columns from the database
    3. // you will actually use after this query.
    4. String[] projection = {
    5. FeedReaderContract.FeedEntry._ID,
    6. FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE,
    7. FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED,
    8. ...
    9. };
    10. String sortOrder =
    11. FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED + " DESC";
    12. FeedReaderContract.FeedEntry.TABLE_NAME, // The table to query
    13. projection, // The columns to return
    14. selection, // The columns for the WHERE clause
    15. selectionArgs, // The values for the WHERE clause
    16. null, // don't group the rows
    17. null, // don't filter by row groups
    18. sortOrder // The sort order
    19. );

    要查询在cursor中的行,使用cursor的其中一个move方法,但必须在读取值之前调用。一般来说应该先调用moveToFirst()函数,将读取位置置于结果集最开始的位置。对每一行,我们可以使用cursor的其中一个get方法如getString()getLong()获取列的值。对于每一个get方法必须传递想要获取的列的索引位置(index position),索引位置可以通过调用getColumnIndex()getColumnIndexOrThrow()获得。

    下面演示如何从course对象中读取数据信息:

    和查询信息一样,删除数据同样需要提供一些删除标准。DB的API提供了一个防止SQL注入的机制来创建查询与删除标准。

    该机制把查询语句划分为选项条件与选项参数两部分。条件定义了查询的列的特征,参数用于测试是否符合前面的条款。由于处理的结果不同于通常的SQL语句,这样可以避免SQL注入问题。

    1. // Define 'where' part of query.
    2. String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
    3. // Specify arguments in placeholder order.
    4. String[] selelectionArgs = { String.valueOf(rowId) };
    5. // Issue SQL statement.
    6. db.delete(table_name, mySelection, selectionArgs);

    更新数据

    当需要修改DB中的某些数据时,使用 update() 方法。

    update结合了插入与删除的语法。

    1. SQLiteDatabase db = mDbHelper.getReadableDatabase();
    2. // New value for one column
    3. ContentValues values = new ContentValues();
    4. values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
    5. // Which row to update, based on the ID
    6. String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
    7. String[] selectionArgs = { String.valueOf(rowId) };
    8. int count = db.update(
    9. FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    10. values,