Android 使用SQLite保存数据
- 简介
- 定义架构和协定
- 使用 SQL 创建数据库
- 将信息添加到数据库
-
- `insert()`函数介绍
- 从数据库中读取信息
-
- `query()`函数介绍
- 从数据库中删除信息
-
- `delete()`函数介绍
- 更新数据库
-
- `update()`函数介绍
- 保留数据库连接
- 调试数据库
简介
对于重复数据或结构化数据(例如联系信息),将数据保存到数据库是理想选择。android.database.sqlite
软件包中提供了在 Android 上使用数据库所需的 API。本篇文章介绍在 Android 上使用 SQLite
数据库。
定义架构和协定
SQL 数据库的主要原则之一是架构,即数据库组织方式的正式声明。架构反映在你用于创建数据库的 SQL 语句中。您可能会发现创建伴随类(称为协定类)很有用,该类以系统化、自记录的方式明确指定了架构的布局。
协定类是定义 URI、表和列名称的常量的容器。通过协定类,您可以在同一软件包的所有其他类中使用相同的常量。这样一来,您就可以在一个位置更改列名称并将其传播到整个代码中。
组织协定类的一种良好方法是将对整个数据库而言具有全局性的定义放入类的根级别。然后,为每个表创建一个内部类。每个内部类都枚举相应表的列。
注意:通过实现
BaseColumns
接口,您的内部类可以继承名为_ID
的主键字段。
例如,以下协定定义了表示 RSS Feed 的单个表的表名称和列名称:
public final class FeedReaderContract {
// To prevent someone from accidentally instantiating the contract class,
// make the constructor private.
private FeedReaderContract() {}
/* Inner class that defines the table contents */
public static class FeedEntry implements BaseColumns {
// 表名
public static final String TABLE_NAME = "entry";
// 列名
public static final String COLUMN_NAME_TITLE = "title";
// 列名
public static final String COLUMN_NAME_SUBTITLE = "subtitle";
}
}
使用 SQL 创建数据库
定义了数据库的结构后,应实现用于创建和维护数据库和表的方法。以下是用于创建和删除表的一些语句:
// 创建数据库表
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
FeedEntry._ID + " INTEGER PRIMARY KEY," +
FeedEntry.COLUMN_NAME_TITLE + " TEXT," +
FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";
// 删除数据库表
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
就像您在设备的内部存储空间中保存文件一样,Android 会将您的数据库存储在您应用的私有文件夹中。您的数据很安全,因为在默认情况下,其他应用或用户无法访问此区域。
SQLiteOpenHelper
类包含一组用于管理数据库的实用 API。当您使用此类获取对数据库的引用时,系统仅在需要时才执行可能需要长时间运行的数据库创建和更新操作,而不是在应用启动期间执行。您仅需调用 getWritableDatabase()
或 getReadableDatabase()
即可。
注意:由于这些操作可能会长时间运行,因此请务必在后台线程中调用
getWritableDatabase()
或getReadableDatabase()
。
如需使用 SQLiteOpenHelper
,请创建一个用于替换 onCreate()
和 onUpgrade()
回调方法的子类。您可能还需要实现 onDowngrade()
或 onOpen()
方法,但这些方法并非必需。
例如,下面展示了使用上述一些命令的 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";
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
FeedEntry._ID + " INTEGER PRIMARY KEY," +
FeedEntry.COLUMN_NAME_TITLE + " TEXT," +
FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
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 dbHelper = new FeedReaderDbHelper(getContext());
将信息添加到数据库
通过将 ContentValues
对象传递给 insert()
方法,将数据插入到数据库中:
// Gets the data repository in write mode
SQLiteDatabase db = dbHelper.getWritableDatabase();
// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);
// Insert the new row, returning the primary key value of the new row
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
insert()
函数介绍
public long insert (String table,
String nullColumnHack,
ContentValues values)
Parameters | Details |
---|---|
table | String: 要插入行的表名,该值不能为空 |
nullColumnHack | String: 可选;可为null。SQL 不允许在未命名至少一个列名的情况下插入完全为空的记录。如果提供的值为空,则不知道列名,无法插入空行。如果不设置为空,nullColumnHack 参数会提供可空的列名,以便在值为空的情况下明确插入 NULL。 |
values | ContentValues: 该映射包含该行的初始列值。键应该是列名,值应该是列值。该值可以为空。 |
Returns | Details |
---|---|
long | 新插入行的行 ID,如果发生错误则为 -1 |
返回新创建行的 ID;如果在插入数据时出错,会返回 -1。如果您的数据与数据库中已有的数据之间存在冲突,就会出现这种情况。
从数据库中读取信息
如需从数据库中读取信息,请使用 query()
方法,向其传递您的选择条件和所需的列。该方法合并了 insert()
和 update()
元素,不过列表定义了要提取的数据(“预测值”),而不是要插入的数据。查询结果会包含在 Cursor
对象中返回给您。
SQLiteDatabase db = dbHelper.getReadableDatabase();
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
BaseColumns._ID,
FeedEntry.COLUMN_NAME_TITLE,
FeedEntry.COLUMN_NAME_SUBTITLE
};
// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };
// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";
Cursor cursor = db.query(
FeedEntry.TABLE_NAME, // The table to query
projection, // The array of columns to return (pass null to get all)
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
);
query()
函数介绍
public Cursor query (String table, // 要查询的表名,不能为空
String[] columns, // 要返回的列的列表。传递 null 将返回所有列
String selection, // 声明要返回哪些行的过滤器,传递 null 将返回给定表的所有行
String[] selectionArgs, // 可以在选择中包含 ?,它将被选择参数中的值替换,按照它们在选择中出现的顺序
String groupBy, // 分组过滤器,传递 null 将导致行不被分组
String having, // 如果使用行分组,则过滤器声明游标中包含哪些行组,传递 null 将导致包含所有行组
String orderBy) // 对行进行排序,传递 null 将使用默认排序顺序,该顺序可能是无序的
第三个参数和第四个参数(selection
和 selectionArgs
)会合并起来创建一个 WHERE
子句。由于这两个参数是与选择查询分开提供的,因此它们在合并之前会进行转义。这样一来,您的选择语句就不受 SQL 注入的影响。
如需查看光标中的某一行,请使用 Cursor
move 方法之一,您必须始终在开始读取值之前调用该方法。由于光标从位置 -1 开始,因此调用 moveToNext()
会将“读取位置”置于结果的第一个条目上,并返回光标是否已经过结果集中的最后一个条目。对于每一行,您都可以通过调用 Cursor
get 方法之一(例如 getString()
或 getLong()
)读取列的值。对于每个 get 方法,您必须传递所需列的索引位置,您可以通过调用 getColumnIndex()
或 getColumnIndexOrThrow()
获取该位置。遍历结果之后,请对光标调用 close()
以释放其资源。例如,以下代码展示了如何获取存储在光标中的所有项目 ID 并将其添加到列表中:
List itemIds = new ArrayList>();
while(cursor.moveToNext()) {
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedEntry._ID));
itemIds.add(itemId);
}
cursor.close();
从数据库中删除信息
如需从表中删除行,您需要提供选择条件,以标识 delete()
方法的目标行。该机制与 query()
方法的目标选择参数的工作方式相同。它将选择规范划分为选择子句和选择参数。子句定义要查看的列,并允许您合并列测试。参数是要测试的值,这服务器托管些值绑定到子句中。由于结果的处理方式与常规 SQL 语句的处理方式不同,因此不受 SQL 注入的影响。
// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { "MyTitle" };
// Issue SQL statement.
int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);
delete()
方法的返回值表示从数据库中删除的行数。
delete()
函数介绍
// return: 如果传入 whereClause,则影响的行数,否则为 0。要删除所有行并获取计数,请传递“1”作为 whereClause。
public int delete (String table, // 要操作的表名,不能为空
String whereClause, // 删除时应用的可选 WHERE 子句。传递 null 将删除所有行。
String[] whereArgs) // 可以在 where 子句中包含 ?s,它将被 whereArgs 中的值替换。这些值将绑定为字符串。如果 whereClause 为 null 或不包含 ?s,则 whereArgs 可能为 null。
更新数据库
如果您需要修改数据库值的子集,请使用 update()
方法。
SQLiteDatabase db = dbHelper.getWritableDatabase();
// New value for one column
String title = "MyNewTitle";
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
// Which row to update, based o服务器托管n the title
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
String[] selectionArgs = { "MyOldTitle" };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);
update()
方法的返回值是数据库中受影响的行数。
update()
函数介绍
public int update (String table, // 要更新的表名,不能为null
ContentValues values, // 从列名到新列值的映射。 null 是一个有效值,将被转换为 NULL。
String whereClause, // 更新时应用的可选 WHERE 子句。传递 null 将更新所有行。
String[] whereArgs) // 可以在 where 子句中包含 ?s,它将被 whereArgs 中的值替换。这些值将绑定为字符串。如果 whereClause 为 null 或不包含 ?s,则 whereArgs 可能为 null。
保留数据库连接
由于在数据库关闭时,调用 getWritableDatabase()
和 getReadableDatabase()
的成本比较高,因此只要您有可能需要访问数据库,就应保持数据库连接处于打开状态。通常情况下,最好在发出调用的 Activity 的 onDestroy()
中关闭数据库。
@Override
protected void onDestroy() {
dbHelper.close();
super.onDestroy();
}
调试数据库
Android SDK 提供一个 sqlite3
shell 工具,您可以使用该工具浏览表内容、运行 SQL 命令以及在 SQLite 数据库中执行其他实用操作。
sqlite3
可启动用于检查 SQLite
数据库的 sqlite
命令行程序。它包含用于输出表格内容的 .dump
以及用于输出现有表格的 SQL CREATE
语句的 .schema
等命令。您也可以从命令行执行 SQLite
命令,如下所示:
$ adb -s emulator-5554 shell
$ sqlite3 /data/data/com.example.app/databases/rssitems.db
SQLite version 3.3.12
Enter ".help" for instructions
注意:您必须拥有对文件系统的 root 权限(例如在模拟器上),才能访问 SQLite 数据库。
如需了解详情,请参阅 sqlite3
命令行文档。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net