SQLite 数据库文件存储在设备上的
/data/data/package_name/databases
文件夹中,所有的数据库都市私有的,只能被创建者访问。
使用数据库时最好的做法是将底层数据库封装起来,只公开与该数据库进行交互时必须使用的公有方法和常量,这一般会用到所谓的辅助类。
ContentValue 和 Cursor
Content Value 用来向表中插入新的行数据,每个 ContentValues 对象都将一个行记录对应。
数据库查询结果作为 Cursor 对象返回。Cursor 是底层数据中的结果集的指针,它没有提取和返回结果值的副本。
Cursor 常用的几个方法:
- moveToFirst
- moveToNext
- moveToPrevious
- moveToPosition
- getCount
- getColumnIndexOrThrow
- getColumnName
- getColumnNames
- getPosition
Android 提供了一种方便的机制,可以确保异步执行查询,API Level 11 引入了 CursorLoader 类和相关的 Loader Manager,现在他们已经成为了支持库的一部分,从而允许你在支持早期的 Android 版本的同时使用这些功能。
SQLiteOpenHelper
是一个抽象类,用来实现创建、打开和升级数据库。会缓存数据库实例。通过 onCreate 和 onUpgrade 方法来分别处理创建新数据库和升级新版本数据库。
等到需要数据库时在创建和打开这些数据库是一种很好的做法,SQLiteOpenHelper 会再打开数据库后缓存他的实例,一般情况下无需手动关闭数据库。
数据库操作(特别是打开或创建数据库)需要很长时间才能完成,为了确保不影响用户操作体验,应该使所有数据库事物异步执行。
通常可以使用 SQLiteOpenHelper 的 getWritableDatabase 或者 getReadableDatabase 来分别打开一个可写或只读的实例。如果数据库不存在,就会执行 onCreate 方法,如果数据库版本发生变化则会执行 onUpgrade 方法。
也可以不使用 SQLiteOpenHelper 打开数据库实例,可以通过 Context 对象的 openOrCreateDatabase
方法
public class HoardDBOPenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "myDatabase.db";
private static final String DATABASE_TABLE = "GoldHoards";
private static final int DATABASE_VERSION = 1;
public static final String KEY_ID = "_id";
public static final String KEY_GOLD_HOARD_NAME_COLUMN = "GOLD_HOARD_NAME_COLUMN";
public static final String KEY_GOLD_HOARD_ACCESSIBLE_COLUMN = "OLD_HOARD_ACCESSIBLE_COLUMN";
public static final String KEY_GOLD_HOARDED_COLUMN = "GOLD_HOARDED_COLUMN";
private static final String DATABASE_CREATE = "create table "
+ DATABASE_NAME + "(" + KEY_ID
+ "integer primary key autoincrement, "
+ KEY_GOLD_HOARD_NAME_COLUMN + " text not null, "
+ KEY_GOLD_HOARDED_COLUMN + " float, "
+ KEY_GOLD_HOARD_ACCESSIBLE_COLUMN + " integer);";
public HoardDBOPenHelper(Context context, String name,
CursorFactory factory, int version,
DatabaseErrorHandler errorHandler) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("TaskDBAdapter", "Upgrading from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF IT EXISTS " + DATABASE_CREATE);
onCreate(db);
}
}
数据库设计注意事项
- 每个表都增加一个
_id
作为每一行的索引字段。如果使用 Content Provider 就必须要有一个 唯一的 ID 字段。- 文件通常是不存在数据库中的,应该存放文件的路径地址。
SQLiteDatabase
数据库操作实例,提供了操作数据库的一切动作,包含增删改查、执行 SQL 语句。
数据库查询
每个数据库查询都会作为一个 Cursor 返回,这就使得 Android 可以按需检索和释放行和列的值,从而更加高效的管理资源。
要对一个数据库对象进行查询,需要使用 query 方法,并传入以下的一些参数:
- 一个可选的布尔值,用来指定结果集是否只包含唯一的值。
- 要查询的表的名称。
- 一个字符串数组形式的投影,列出了包含在结果集中的列。
- 一条 where 子句,定义了要返回的行。可以在其中包含“?” 通配符,它将会被通过选择参数传入的值替换。
- 一个选择参数字符串的数组,它将会替换 where 子句中的“?”,通配符。
- 一条 group by 子句,用来定义返回的行的分组方式。
- 一条 having 子句,如果指定了一条 group by 子句,则盖子句会定义要包含那些行组。
- 一个字符串,用来描述返回的行的顺序。
- 一个字符串,用来定义结果集中的最大行数。
public void queryHoard() {
String[] result_cloums = new String[] { KEY_ID,
KEY_GOLD_HOARD_ACCESSIBLE_COLUMN, KEY_GOLD_HOARDED_COLUMN };
// 指定用于限制结果的 where 子句
String where = KEY_GOLD_HOARD_ACCESSIBLE_COLUMN + "=" + 1;
// 根据需要把以下语句替换为有效的SQL语句
String[] whereArgs = null;
String groupBy = null;
String having = null;
String order = null;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.query(DATABASE_NAME, result_cloums, where,
whereArgs, groupBy, having, order);
}
从 Cursor 中提取值
要 从 Cursor 中提取值,首先要使用前面描述过的 moveTo 系列的方法将游标放到结果 Cursor 的正确行中,然后使用类型安全的 get 方法来返回存储在指定列的当前行中的值。为了找到特定列在游标中的索引,需要使用 getColumnIndex 和 getColumnIndexOrThrow 方法。
当你认为列在所有情况下都存在时,使用 getColumnIndexOrThrow 是一种不错的办法,当列有可能不存在游标中时,使用 getColumnIndex 可以检查结果是否为 -1 比捕获异常更加高效。
int columnIndex = cursor.getColumnIndex(KEY_GOLD_HOARDED_COLUMN);
if (columnIndex > -1) {
String columnValue = cursor.getColumnName(columnIndex);
// 存在时
} else {
// 不存在
}
从 Cursor 中获取值:
float totalHoard = 0f;
float averageHoard = 0f;
// 找出所用列的索引
int GOLD_HOARDED_COLUMN_INDEX = cursor.getColumnIndexOrThrow(KEY_GOLD_HOARD_NAME_COLUMN);
// 遍历游标
while (cursor.moveToNext()) {
float hoard = cursor.getFloat(GOLD_HOARDED_COLUMN_INDEX);
totalHoard += hoard;
}
// 计算平均值
float cursorCount = cursor.getCount();
averageHoard = cursorCount > 0 ? (totalHoard / cursorCount) : Float.NaN;
// 完成之后关闭游标
cursor.close();
添加、更新和删除
SQLiteDatabase 提供了 insert、delete、update 方法来封装执行这些操作需要的 SQL 语句。如果你希望手动去执行这些操作,需要用到 execSQL 方法可以对数据库表执行有效的 SQL 语句。
1. 插入行
// 创建一个要插入的行新值
ContentValues contentValues = new ContentValues();
// 为每行赋值
contentValues.put(KEY_GOLD_HOARD_ACCESSIBLE_COLUMN, "");
contentValues.put(KEY_GOLD_HOARD_NAME_COLUMN, 123);
contentValues.put(KEY_GOLD_HOARDED_COLUMN, true);
// 把行插入到表中
SQLiteDatabase sdb = this.getReadableDatabase();
sdb.insert(this.DATABASE_TABLE, null, contentValues);
insert 方法的第二个参数称为 null 列侵入(null column hack),如果想添加一个空行,在传入 ContentValues 还必须传入一个显示的 null。
2. 更新行
// 创建更新行的 ContentValues
ContentValues updatedValues = new ContentValues();
// 为每一行赋值
updatedValues.put(KEY_GOLD_HOARDED_COLUMN, false);
// 指定一个 where 子语句定义那些行应该被更新
String where = KEY_ID + "=" + 1;
String[] whereArgs = null;
// 使用新值更新数据库记录
SQLiteDatabase sdb = getReadableDatabase();
sdb.update(DATABASE_TABLE, updatedValues, where, whereArgs);
3. 删除行
// 指定一个 where 子语句定义那些行应该被删除
String where = KEY_ID + "=" + 1;
String[] whereArgs = null;
// 使用新值更新数据库记录
SQLiteDatabase sdb = getReadableDatabase();
sdb.delete(DATABASE_TABLE, where, whereArgs);