DataBase in Android
这篇文章是我在学习 Udacity 上的 Android 课程时, 写的知识总结.
可能会有一些内容来自 Udacity, 也有一些例子来自 Android 的开发文档.
总结好了想给大家分享, 如果我学到了新的东西, 我也会后续更新
我们知道, 如果我们的数据仅仅是存储在变量中的, 那么其生命周期可能只是和 Activity 一样长, 在我们推出 APP 之后, 我们的数据就会丢失. 我们需要数据持久化(data persistence). 我们可以将数据存储在文件或者数据库中. 这里我们讨论的是数据库的方式.
计算机存储
计算机有两大主要的存储设备, 一类是内存, 一类是外存. 内存(Memory), 是一种临时存储器, 读写速度快, 断电数据丢失, 所以不能长期保存数据. 外存, 包括硬盘, 软盘, 光盘等等, 读写速度慢, 但是可以长期保存数据, 断电不丢失. 我们平时所称的安装 APP, 是安装在外存中, 只有当程序运行的时候, 才会被加载到内存中(也必须被加载到内存中才能运行). 我们在程序中使用的变量, 他们的生命周期是随着程序的结束而结束的, 在程序启动时被创建和使用, 在程序结束时被系统销毁, 所以不能作为长久保存数据的工具.
Android 中的几种不同的数据存储选择
我们这里所讨论的内容, 是将数据存储到我们的 Android 设备中, 不包括存储到云端等方式.
- Files, 文件
- Shared Preferences
- SQLite Databases, SQLite 数据库
Files, 保存到文件中
将保存为文件, 常见的有图片, 音乐, 视频的存储等.
Shared Preferences(首选项)
数据以键值对的形式来保存, (Key – Unique string, Value – Primitive types and Strings), 即包括一个唯一的字符串 key 和其对应的值, 值可以是各种原始数据类型或者是 String.
Shared Preferences 不适合存储大量的用户数据, 适合存储少量的关键的用户信息.
SQLite Databases
SQLLite 是一种数据库, 数据库简单来讲, 是一种有组织的数据结构.
SQLite 的基本组成简单来讲, 可以理解为表, SQLite 数据库由一张张的表来组成.
数据库可以让我们轻松的分享大量相关的结构化数据, 同时, 这些数据可能随着使用而不断增长. 比如我们的通讯录, 里面存储的每一个联系人的各种信息, 可以理解为一张表, 记录各种的属性, 比如姓名, 手机, 邮箱等等. 这些联系人之间, 属性和属性之间是相关的, 他们都是大致相同的格式化数据. 属性之间是相关的, “张三” 的手机号是 “130XXXXXXXX” 而不是其他. 同时, 通讯录还会因为我们的联系人的增多而添加内容, 还可以修改和删除内容. 对于这些数据, 我们可以轻松的检索到我们需要的信息.
SQLite
安装 SQLite
最新版的 MacOS 中已经预装了 SQLite, 打开终端输入 sqlite3
, 可以检查是否有 SQLite.
SQL
当我们输入 sqlite3
命令进入 SQLite 之后会出现一下界面.
dcrdeMacBook-Pro:~ Dcr$ sqlite3
SQLite version 3.16.0 2016-11-04 19:09:39
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite>
这里我们可以使用 SQL(Structured query language, 结构化查询语言).
SQLite 中的类型
SQLite 中的 Storage Class:
- NULL
- INTEGER: 有符号整数
- REAL: 浮点数
- TEXT: 字符串
- BLOB: 原始数据, 比如图片或者二进制数据.
对于 booleans, 我们可以使用 INTEGER 来代替:
- 0 – false
- 1 – true
语法
下面介绍 SQL 的基本语法.
CRUD
CRUD 是 Create, Read, Update, Delete 的缩写. 是数据库中的四项重要操作.
CREATE
Create a database
在 SQLite 中, 创建一个新的数据库, 可以在命令行中使用 sqlite3 <database_name>.db
来创建. 或者在进入 sqlite 之后, 使用 .open <database_name>.db
来创建.
Create a table
创建一张新的表需要指定表名和列名和对应的数据类型. 语法如下:
CREATE TABLE <table_name>(
<column_name_1> <data_type_1>,
<column_name_2> <data_type_2>,
...
);
上面是创建表的基本语法, 我们创建时, 还可以给每列对应的参数添加属性, 基本的属性有:
-
PRIMARY KEY
: 主键, 每张表只能有一个主键. -
AUTOINCREMENT
: 添加行时, 该属性自动加一, 可以用于指定每行的 ID. -
NOT NULL
: 非空, 表示添加行时, 该元素必须有值. -
DEFAULT <value>
: 默认值, 如果创建时没有提供值, 那么以默认值填充.
INSERT data in a table
向表中添加行:
INSERT INTO <table_name> (
<column_name_1>,
<column_name_2>,
...) VALUES (
<values_1>,
<values_2>,
...
);
READ
读操作使用的是 SELECT
关键字, 基本操作如下:
SELECT <columns>
FROM <table_name>;
SELECT
后跟的列项可以用 *
表示所有列.
执行上面的操作, 是查询一个表的所有行, 我们要查询我们所需要的行时, 可以使用 WHERE
关键字.
SELECT <columns>
FROM <table_name>
WHERE <condition>;
我们还可以指定结果的排序方式, 通过 ORDER BY
关键字.
SELECT <columns>
FROM <table_name>
WHERE <condition>
ORDER BY <ASC|DESC>
-
ASC
: 升序 -
DESC
: 降序
UPDATE
UPDATE 用于更新表中的数据.
UPDATE <table_name>
SET <column_name> = <value>
WHERE <condition>;
同样, 使用 WHERE
指定所要修改的行.
DELETE
删除行
删除满足 WHERE
条件的行.
注意: 使用 DELETE 一定要非常小心, 比如, DELETE FROM table_name
就会删除所有行.
DELETE FROM <table_name>
WHERE <condition>;
删除表
删除表使用 DROP TABLE
关键字:
DROP TABLE <table_name>;
SQLite 中的命令
-
.shema <table_name>
: 显示创建表的命令. -
PRAGMA TABLE_INFO(<table_name>);
: 显示表的列信息
SQLite in Android
[Why is a database always represented with a cylinder?](https://stackoverflow.com/que…
)
确定数据库的架构
- 确定表的名称
- 每一列数据的名称和数据类型
TABLE_NAME = “pets”
Column name | Data types | Attributes |
---|---|---|
_id | INTEGER | PRIMARY KEY AUTOINCREEMENT |
name | TEXT | NOT NULL |
breed | TEXT | |
gender | INTEGER | NOT NULL DEFAULT 0 |
weight | INTEGER | NOT NULL DEFAULT 0 |
Contract
我们在使用数据库时, 会用到很多的变量, 为了方便和不出错, 我们可以将这些变量单独拿出来, 这就是 Contract
类.
Contract
类中, 定义了我们将用到的许多常量, 比如, URI, 表名, 列名以及一些有特殊意义的整数值等等. 下面是一个简单的例子: 例子中, 重写了私有的构造函数, 因为我们并不需要该类的对象.
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";
}
}
有了 Contract 类之后, 我们就可以写出我们常用的 SQL 语句了. 比如, 下面就是我们常用的 CREATE TABLE
和 DROP TABLE
语句.
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;
通过 SQLHelper 创建数据库
在 Android 中, 数据库默认存储在应用的私有区域的, 这一区域默认情况下其他应用是无法访问的, 所以是安全的.
我们可以使用 SQLiteOpenHelper
类来获取数据库的引用, 此时, 系统会在需要的时候才会执行 creating 和 update 数据库这些费时的操作, 而不是在应用启动的时候, 我们需要做的就是去调用 getWritableDatabase()
或者 getReadableDatabase()
方法.
注意: 因为这样的操作可能是长时间操作, 所以, 务必请在后台线程调用
getWritableDatabase()
或者getReadableDatabase()
方法.
使用时, 我们可以继承 SQLiteOpenHelper
类重写 onCreate()
, onUpgrade()
, 和 onOpen()
回调方法, 在需要时, 也可以重写 onDowngrade()
方法. 比如:
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);
}
}
我们在需要数据库的时候, 使用下面的语句.
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
在数据库中存入数据
我们在将数据存入数据库中时, 可以使用 ContentValues
对象来进行.
ContentValues
对象就是键值对组, 操作起来也比较简单. 操作步骤如下:
// 1. 获取 SQLiteDatabase 对象
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// 2. 创建值的 map, keys 是列名
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);
// 3. 加入到数据库中去
long newRowId = db.insert(FeedEntry.Table_name, null, values);
从数据库中读取数据
method query()
首先我们可以先了解一下 query()
方法.
SQLiteDatabase.query()
:
Cursor query (String table,
String[] columns,
String selection,
String[] selectionArgs,
String groupBy,
String having,
String orderBy,
String limit)
Parameters | |
---|---|
table | String: 要查询的表 |
column | String[]: 要查询的列的数组。 |
selection | String: 对应 SQL 中的 WHERE 语句部分(不包括 WHERE 本身). |
selectionArgs | String[], 在 selection 参数中, 可能包含 ? 作为参数的占位符, 在这个参数中给出. |
groupBy | String: 对应 SQL 语句中的 GROUP BY 语句(不包括 GROUP BY 本身). |
having | String: 对应 HAVING 语句(不包括 HAVING 本身). |
orderBy | String: SQL ORDER BY 语句(不包括 ORDER BY 本身) |
limit | String: 返回的行数, LIMIT 分句的格式 |
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_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 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
我们的数据库查询语句返回一个 Cursor
对象, 那么什么是 Cursor 呢? Cursor 是查询结果中行的集合.
首先, 顾名思义, Cursor 有一个 position 的指针, 指向的就是当前的行. 行的编号是从 0 开始的, 初始 position = -1.
其工作模式就是, 通过一系列 move 方法, 来选中行, 再通过不同的 get 方法来获取我们需要的数据.
在确定列的时候, 我们需要知道列名, 然后通过 getColumnIndex()
或者 getColumnIndexOrThrow()
来获取 column index. 有了 column index, 我们就可以通过 getInt()
getFloat()
等一系列方法来获取具体的数据.
-
getCount()
: 返回 cursor 的行数. -
getType(int columnIndex)
: 返回给定的列的类型 -
getDouble(int columnIndex)
getFloat(int columnIndex)
getInt(int columnIndex)
getLong(int columnIndex)
getString(int columnIndex)
getShort(int columnIndex)
: 返回对应类型的值.
所有的 move 方法返回一个 boolean 值, 表示能否移动到该行.
-
moveToFirst()
: 移动到第一行 -
moveToLast()
: 移动到最后一行 -
moveToNext()
: 移动到下一行 -
moveToPosition(int position)
: 移动到指定位置 -
moveToPrevious()
: 移动到前一次移动的行