[翻译]Android教程-保存数据-在SQL数据库中保存数据

定义一个 Schema 和 Contract


SQL数据库的主要原则是模式(schema): 一种数据库被如何组织的正式声明. 模式被反映在你用来创建你的数据库的SQL语句中. 你可能会发现创建一个同伴类很有用,它被称作(contract)协议类, 它用一种系统和自文档化的方式明确指定了你的模式的布局.

协议类是定义的URI、表格和列名称常量的容器. 协议类让你可以在同一个包的所有其它类那里使用相同的常量. 这让你可以在一个地方对列名称的改变传播到你所有的代码.

组织一个协议类最好的方式是将对你的整个数据库全局可用的定义放置到类的根一级别 . 然后为每一个表创建一个内部类,并枚举出它们的列.

注意: 通过实现 BaseColumns 接口, 你的内部类可以继承到一个称作 _ID 的主键域,一些诸如游标适配器的Android类将希望有这个东西 . 它也不是一定要有的,但它有助于你的数据库同Android框架更加的协调 .

例如,这个小代码块为一个表定义了表名和列名 :

public final class FeedReaderContract { 
    // To prevent someone from accidentally instantiating the contract class,     
    // give it an empty constructor. 
    public FeedReaderContract() {} 
    /* Inner class that defines the table contents */ 
    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 + 
    ... // Any other options for the CREATE command 
    " )"; 
private static final String SQL_DELETE_ENTRIES = 
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

就像是你保存在设备的 内部存储 中的文件, Android 将你的数据库保存在同应用程序相关联的私有磁盘空间上. 你的数据时受到保护的,因为这一区域默认不能被其它应用程序所访问 .

SQLiteOpenHelper 类中有一堆实用的API . 当你想要使用这个类来获取你的数据库的引用时,系统只会在需要时并且不会是应用启动时,执行创建和更新数据库的可耗时较长的操作. 你总共需要做的就是调用 getWritableDatabase() 和 getReadableDatabase().

注意: 以为它们可以是耗时较长的,请确保你是在一个后台线程中调用的 getWritableDatabase() 和 getReadableDatabase(), 使用诸如 AsyncTask 或者 IntentService.

为了使用 SQLiteOpenHelper, 创建一个重载了 onCreate()onUpgrade() 和 onOpen() 回调函数的子类. 你可能还想到要实现 onDowngrade(), 但它不是必须的.

例如,这里是一个使用如下所示的一些命令的 SQLiteOpenHelper 实现 :

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // If you change the database schema, you must increment the database version.
    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) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        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() 方法,来将数据插入一个数据库:

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
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);

// Insert the new row, returning the primary key value of the new row
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();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedEntry._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };

// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_UPDATED + " DESC";

Cursor c = db.query(
    FeedEntry.TABLE_NAME,  // The table to query
    projection,                               // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                            // The values for the WHERE clause
    null,                                     // don't group the rows
    null,                                     // don't filter by row groups
    sortOrder                                 // The sort order
    );

要在游标对象中浏览一行数据,就使用 Cursor 中的某一个移动方法 , 它们总是必须在你开始读取值时被调用. 一般而言,一开始你应该调用 moveToFirst(), 它会将“读取位置”放在结果的第一条的位置 . 对于每一行,你可以通过调用 Cursor 中的摸一个获取方法来读取一列的值 , 比如 getString() 或者 getLong(). 对于每一个获取方法,你必须传入你想要获取的列的索引位置 , 它可以通过调用 getColumnIndex() 或者 getColumnIndexOrThrow() 获取到. 例如:

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

从数据库中删除信息


要从数据库中删除行,你需要提供识别这些行的选择条件. 数据库API提供了创建能够防止数据库注入的选择条件的机制. 该机制将选择指标分成选择语句和选择参数 . 语句部分定义了要查找的列,同时也允许你结合列测试 . 参数部分是绑定到语句中的测试用的值 . 因为结果不是处理同一个常规的SQL语句,它也就不会受到SQL注入的侵害.

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

更新数据库


当你想要修改你的数据库值的子集,就是用 update() 方法.

更新数据表将 insert() 内含的values语法内容同delete()的where语法结合了起来.

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// New value for one column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the 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);

来源:

http://developer.android.com/training/basics/data-storage/databases.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java 技术分享

WEB 小案例 -- 网上书城(一)

3905
来自专栏不想当开发的产品不是好测试

mybatis 使用tips - 使用多个参数

执行如下命令: mvn -Dmybatis.generator.overwrite=true mybatis-generator:generate 可以使用my...

1909
来自专栏坚毅的PHP

mysql数据迁移hbase问题

无法直接dump,写了java多线程程序做迁移 问题1:Operation not allowed after ResultSet closed 裸jdbc语句...

3845
来自专栏黑白安全

sqlmap的tamper详解

sqlmap 是一款注入神器广为人知,里面的 tamper 常常用来绕过 WAF ,很实用的模块,但是却常常被新手忽略(比如我),今天就整理总结一下 tampe...

2473
来自专栏吴柯的运维笔记

mysql数据备份-导入与导出数据

数据导入: 把系统文件的内容存储到数据库服务器的表里。 命令格式: LOAD DATA INFILE “目录名/文件名” INTO TABLE 库.表名 ...

3757
来自专栏kevindroid

room的使用-以demo为例

3713
来自专栏PingCAP的专栏

TiDB 源码阅读系列文章(七)基于规则的优化

本篇将主要关注逻辑优化。先介绍 TiDB 中的逻辑算子,然后介绍 TiDB 的逻辑优化规则,包括列裁剪、最大最小消除、投影消除、谓词下推、TopN 下推等等。

5.6K15
来自专栏牛肉圆粉不加葱

Spark SQL Limit 介绍及优化

全局限制,最多返回 limitExpr 对应条 records。总是通过 IntegerLiteral#unapply(limitExpr: Expressio...

3052
来自专栏用户2442861的专栏

MyBatis学习总结(四)——解决字段名与实体类属性名不相同的冲突

  在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定都是完全相同的,下面来演示一下这种情况下的如何解决字段名与实体类属性名不相同的冲突。

761
来自专栏杨建荣的学习笔记

pl/sql中的参数模式(r4笔记第54天)

在平时的工作中,可能通过pl/sql传入参数来做一些特定的操作,参数模式一般有In,out.in out这几种 比如dbms_sqltune下的PREPARE_...

3124

扫码关注云+社区