安卓训练-开始-保存数据-保存数据到数据库

保存数据到数据库

把重复的或结构化的数据(比如联系人信息)保存到数据库是非常理想的。这节课假设你大体上已经熟悉 SQL 数据库,帮助你开始使用安卓上的 SQLite 数据库。你将使用的安卓上的数据库的 API 可以在android.database.sqlite 包中找到。

定义模式(Scheme)和协议(Contract)


SQL 数据库的一个主要原则是模式:数据库怎样组织的一个正式声明。你用于创建你的数据库的 SQL 语句反映了模式。你会发现定义一个伙伴类(称为协议类)很有用,它以一种系统的自文档的方式显式地指定你的模式的布局。

一个协议类是一个定义URI、表和列的名称常量的容器。协议类允许你在相同包中的其他类中使用相同的常量。这能让你在一个地方修改列名而影响扩散到你的所有代码。

协议类的一个好的组织方式是在类的根级别放置你的整个数据库全局定义,然后为每个表创建一个枚举所有列的内部类。

注意:通过实现 BaseColumns 接口,你的内部类可以继承一个称为_ID 的主键字段,一些安卓类,比如游标适配器(cursor adaptor),会期望有这个字段。它不是必需的,但是这能帮助你的数据库与安卓框架协调地工作。

例如,下面的代码段为一个简单的表定义表名和列名:

public final class FeedReaderContract {
    // 为了防止某些人意外地实例化这个协议类,
    // 定义一个空构造方法。
    public FeedReaderContract() {}

    /* 定义表内容的内部类 */
    public static abstract class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_ENTRY_ID = "entryid";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
        ...
    }
}

使用 SQL Helper 创建数据库


一旦你已经定义了你的数据库的样子,你需要实现创建和维护数据库和表的方法。这里是一些创建和删除一个表的典型的语句:

private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    ... // CREATE 命令的其他选项
    " )";

private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

就像你在设备的内部存储上保存的文件,安卓在与应用关联的私有磁盘空间中存储你的数据库。你的数据是安全的,因为默认情况下其他应用不能访问这个区域。

SQLiteOpenHelper 类中有一系列有用的 API。当你使用这个类获取你的数据的引用时,系统只有在需要时才执行创建和更新数据库的可能的长运行(long-running)操作,而不是在应用启动时。你需要做的就是调用getWritableDatabase()getReadableDatabase()

注意:因为它们可能是长运行的,请确保在后台线程中调用 getWritableDatabase()getReadableDatabase(),比如使用AsyncTaskIntentService

为了使用 SQLiteOpenHelper,创建一个子类覆盖onCreate(),onUpgrade()onOpen() 回调方法。你可能还想实现onDowngrade(),但这不是必须的。

例如,这里是 SQLiteOpenHelper 的一个实现,使用了一些前面展示的命令:

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // 如果你修改了数据库模式,你必须增加数据库版本。
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 这个数据库仅仅是在线数据的缓存,所以它的更新策略是
        // 简单地丢弃数据,然后重新开始
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

为了访问你的数据库,实例化你的 SQLiteOpenHelper 的子类:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

向数据库中存入信息


传一个 ContentValues 对象给insert() 方法向数据库中插入数据:

// 以写模式取得数据仓库
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// 创建一个新的值映射,列名是键
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_CONTENT, content);

// 插入新行,返回它的主键的值
long newRowId;
newRowId = db.insert(
         FeedEntry.TABLE_NAME,
         FeedEntry.COLUMN_NAME_NULLABLE,
         values);

insert() 的第一个参数就是表名。第二个参数提供当ContentValues 为空时框架可以插入 NULL 的列名(如果你把它设置为"null",那么当没有值时框架不会插入行)。

读取数据库中的信息


为了读取数据库中的信息,使用 query() 方法,传入你的选择条件和需要的列。这个方法结合了insert()update() 的元素,除了定义你想获取数据的列列表,而不是要插入的数据。查询结果以一个Cursor 对象返回给你。

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// 定义一个投影,指定在这个查询后你实际需要使用的数据库的列
String[] projection = {
    FeedEntry._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };

// 你希望结果游标中记录怎样排序 
String sortOrder =
    FeedEntry.COLUMN_NAME_UPDATED + " DESC";

Cursor c = db.query(
    FeedEntry.TABLE_NAME,  // 查询的表
    projection,            // 返回的列
    selection,             // WHERE 子句的列
    selectionArgs,         // WHERE 子句的值
    null,                  // 行不进行分组
    null,                  // 行分组不进行过滤
    sortOrder              // 排序
    );

为了查看游标中一行,使用 Cursor 的移动方法,你必须在读取值前调用这些方法。通常,你需要通过调用moveToFirst() 开始,它把“读位置”放到结果集的第一个入口。对每一行,你可以调用Cursor 的 get 方法读取一列的值,比如getString()getLong()。对每个 get 方法,你必须传你想要的列的索引位置,你可以调用getColumnIndex()getColumnIndexOrThrow() 得到索引位置。例如:

cursor.moveToFirst();
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedEntry._ID)
);

删除数据库中的信息


为了删除表中的行,你需要提供识别行的选择条件。数据库 API 提供了一种创建能防止 SQL 注入的选择条件的机制。这个机制把选择规格分成选择子句和选择参数。选择子句定义查看的列,允许你组合列测试。参数是被绑定到选择子句用于测试列的值。因为结果不像常规的 SQL 语句那样处理,它对于 SQL 注入是免疫的。

// 定义查询的 ‘where‘ 部分。
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// 按占位符顺序指定参数。
String[] selectionArgs = { String.valueOf(rowId) };
// 发出 SQL 语句。
db.delete(table_name, selection, selectionArgs);

更新数据库


当你需要修改你的数据库值的一个子集,使用 update() 方法。

更新表结合了 insert() 的内容值语法和delete()where 语法。

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// 列的新值
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// 根据 ID 指定需要更新哪一行
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };

int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);
 
 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。