SQLite,是一款轻型的数据库,是遵照ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp创建的公有领域项目。它的设计目标是嵌入式的,并且目前已经在不少嵌入式产品中使用了它,它占用资源很是的低,在嵌入式设备中,可能只须要几百K的内存就够了。它可以支持Windows/Linux/Unix等等主流的操做系统,同时可以跟不少程序语言相结合,好比 Tcl、C#、PHP、Java等,还有ODBC接口,一样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来说,它的处理速度比他们都快。(摘自百度百科)html
鉴于 SQLite
具有如此优秀的业务处理能力,Android 平台天然使用它做为内置的数据库存储模块。存储在 SD 卡上,只是一个文件的形式,能够方便的备份、移植。有兴趣的小伙伴,能够至SQLite官网下载 C 源码研究研究。android
另外 Android 平台提供了一整套的 SQLite 操做 API。能够方便快速的建立表、更新表、对表数据进行增删改查。git
建立数据库方法,分四类。github
上述四个方式,均可以指定 CursorFactory
、Flags
、DatabaseErrorHandler
(API 11)、OpenParams
(API 27) 四个基本参数。最终都 执行到 openDatabase
方法。算法
SQLite 对数据库文件添加四种访问权限,作 flags 参数:sql
android.database.sqlite.SQLiteReadOnlyDatabaseException
。// 执行 query 方法时,使用该对象,用于生成 cursor
SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
@Override
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return new SQLiteCursor(masterQuery, editTable, query);
}else
return new SQLiteCursor(db,masterQuery, editTable, query);
}
};
// 数据库发生异常时,执行该对象。
DatabaseErrorHandler errorHandler = new DatabaseErrorHandler() {
@Override
public void onCorruption(SQLiteDatabase dbObj) {
// 作关闭数据库的操做
dbObj.close();
}
};
// 打开数据库的参数,API 27 才支持。
SQLiteDatabase.OpenParams openParams = new SQLiteDatabase.OpenParams.Builder()
.setCursorFactory(factory) // 指定 CursorFactory
.setErrorHandler(errorHandler)// 指定 DatabaseErrorHandler
.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY) // 指定打开权限。
.build();
复制代码
直接在内存中建立数据库,create/createInMemory 两个方法,后者在 API 27 才支持,建议使用 create。数据库
/** * 在内存中建立 SQLite 数据库,当数据库关闭时,即销毁。 * 适合临时保存数据。 * * @param factory 可选参数,建议传入 NULL 或者构建 factory 实例。 * @return 返回数据库对象 * @throws 数据库建立失败时,抛出 SQLiteException */
@NonNull
public static SQLiteDatabase create(@Nullable CursorFactory factory) {
// This is a magic string with special meaning for SQLite.
return openDatabase(SQLiteDatabaseConfiguration.MEMORY_DB_PATH,
factory, CREATE_IF_NECESSARY);
}
/** * 在内存中建立 SQLite 数据库,当数据库关闭时,即销毁。 * 适合临时保存数据。 * * @param openParams 配置打开数据库的参数 * @return 返回数据库对象 * @throws 数据库建立失败时,抛出 SQLiteException */
@NonNull
public static SQLiteDatabase createInMemory(@NonNull OpenParams openParams) {
return openDatabase(SQLiteDatabaseConfiguration.MEMORY_DB_PATH,
openParams.toBuilder().addOpenFlags(CREATE_IF_NECESSARY).build());
}
复制代码
都执行到 openDatabase 方法,Flags = CREATE_IF_NECESSARY。缓存
/** * 根据 flags 打开数据库 * * @param path 建立或打开数据库的文件路径。可使用相对地址或绝对地址。 * 相对地址存在应用缓存 database 目录,绝对地址能够存在 sd 卡目录下。 * @param factory 可选参数,建议传入 NULL 或者构建 factory 实例。 * @param flags 控制数据的方式方式, * @return 返回打开新打开的数据库 * @throws 数据库建立失败时,抛出 SQLiteException */
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory, @DatabaseOpenFlags int flags) {
return openDatabase(path, factory, flags, null);
}
/** * 根据 flags 打开数据库 * * @param path 打开或建立数据库的文件 * @param factory 可选参数,建议传入 NULL 或者构建 factory 实例。 * @param flags 控制数据库文件的访问方式 * @param errorHandler 当 SQLite 报出数据库出错时,使用 DatabaseErrorHandler 处理错误。如关闭数据库。 * @return 返回打开新打开的数据库 * @throws 数据库建立失败时,抛出 SQLiteException */
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory, @DatabaseOpenFlags int flags, @Nullable DatabaseErrorHandler errorHandler) {
SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1, -1, null,
null);
db.open();
return db;
}
/** * 根据指定的参数,打开数据库 * * @param path 建立或打开数据库的文件路径。 * 使用绝对路径,或者经过context#getDatabasePath(String) * @param openParams 配置打开数据库的参数 * @return 返回打开新打开的数据库 * @throws 数据库建立失败时,抛出 SQLiteException */
public static SQLiteDatabase openDatabase(@NonNull File path, @NonNull OpenParams openParams) {
return openDatabase(path.getPath(), openParams);
}
private static SQLiteDatabase openDatabase(@NonNull String path, @NonNull OpenParams openParams) {
Preconditions.checkArgument(openParams != null, "OpenParams cannot be null");
SQLiteDatabase db = new SQLiteDatabase(path, openParams.mOpenFlags,
openParams.mCursorFactory, openParams.mErrorHandler,
openParams.mLookasideSlotSize, openParams.mLookasideSlotCount,
openParams.mIdleConnectionTimeout, openParams.mJournalMode, openParams.mSyncMode);
db.open();
return db;
}
复制代码
四个方法中,能够分为两类。app
/** * 至关于 openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY). */
public static SQLiteDatabase openOrCreateDatabase(@NonNull File file, @Nullable CursorFactory factory) {
return openOrCreateDatabase(file.getPath(), factory);
}
/** * 至关于 openDatabase(path, factory, CREATE_IF_NECESSARY). */
public static SQLiteDatabase openOrCreateDatabase(@NonNull String path, @Nullable CursorFactory factory) {
return openDatabase(path, factory, CREATE_IF_NECESSARY, null);
}
/** * 至关于 openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler). */
public static SQLiteDatabase openOrCreateDatabase(@NonNull String path, @Nullable CursorFactory factory, @Nullable DatabaseErrorHandler errorHandler) {
return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
}
复制代码
建立并打开数据库,三个方法最后都会执行到 SQLiteDatabase openDatabase(String ,CursorFactory,int,DatabaseErrorHandler)
方法。ide
SQLiteOpenHelper 是 Android 封装的最人性化的工具。方便开发者自行管理数据库表目录和结构。
/** * 管理数据库建立和版本 */
public class DBOpenHelper extends SQLiteOpenHelper {
// 至关于 SQLiteDatabase openDatabase(String, CursorFactory)
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
// 至关于 SQLiteDatabase openDatabase(String, CursorFactory, DatabaseErrorHandler)
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) {
super(context, name, factory, version, errorHandler);
}
// 至关于 SQLiteDatabase openDatabase(String , OpenParams);
@TargetApi(Build.VERSION_CODES.P)
public DBOpenHelper(Context context, String name, int version, SQLiteDatabase.OpenParams openParams) {
super(context, name, version, openParams);
}
// 建立数据文件时调用,此时适合建立新表
@Override
public void onCreate(SQLiteDatabase db) {
}
// 更新数据库版本时调用,适合更新表结构或建立新表
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
// 生成 helper 对象,能够打开数据库文件。文件名能够是相对路径或绝对路径
DBOpenHelper dbHelper = new DBOpenHelper(this, "test.db", null, 1);
// 用读写的方式打开数据库文件
SQLiteDatabase database = dbHelper.getWritableDatabase();
复制代码
openDatabase 适合打开已经存在的数据库文件。而且表目录和结构固定,通常只有对数据库表的常规操做。如,已有一个全国城市信息数据库文件,须要在项目中打开。
SQLiteOpenHelper 须要本身从新建立数据库文件,根据后期的需求可能须要对数据库的表目录及结构作修改。升级数据版本,在onUpgrade
方法中处理便可。openDatabase 也能够实现这个需求,只是稍微麻烦些。
插入操做方法总结为两类,insert 和 replace ,最后都执行 insertWithOnConflict 方法。
/** * 向数据库插入一行数据 * * @param table 指定表名,插入一行数据 * @param nullColumnHack 可选参数,建议是 null * 若是设置 null,将不容许向表中插入空数据,即 values = null 时没法正确执行插入操做。 * 若是不设置 null,那么须要设置表中能够为空的属性列的名称。 * 当 values = null 时,能够向表中插入空数据。 * 而其实是插入一行数据,只有属性列名 nullColumnHack 的值是 null。 * @param values map 集合,包含须要插入的一行数据。至少须要包含一个属性的 key 和 value。 * key 是属性列名称,value 是属性值。 * @return 返回新插入的行序号, 发生错误时,返回 -1 */
public long insert(String table, String nullColumnHack, ContentValues values) {
try {
return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
} catch (SQLException e) {
Log.e(TAG, "Error inserting " + values, e);
return -1;
}
}
/** * 向数据库插入一行数据。只是在插入操做异常时,须要手动捕获异常。 * 参数都同上 * @param table * @param nullColumnHack * @param values * @throws SQLException * @return */
public long insertOrThrow(String table, String nullColumnHack, ContentValues values) throws SQLException {
return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
}
复制代码
/** * 根据主键替换表中的一行数据,若是替换的主键在表中不存在,则插入一行数据。 * 参数都同上 * @param table * @param nullColumnHack * @param initialValues * @return 返回新插入的行序号, 发生错误时,返回 -1 */
public long replace(String table, String nullColumnHack, ContentValues initialValues) {
try {
return insertWithOnConflict(table, nullColumnHack, initialValues,
CONFLICT_REPLACE);
} catch (SQLException e) {
Log.e(TAG, "Error inserting " + initialValues, e);
return -1;
}
}
/** * 根据主键替换表中的一行数据,若是替换的主键在表中不存在,则插入一行数据。 * 参数都同上 * @param table * @param nullColumnHack * @param initialValues * @throws SQLException * @return */
public long replaceOrThrow(String table, String nullColumnHack, ContentValues initialValues) throws SQLException {
return insertWithOnConflict(table, nullColumnHack, initialValues,
CONFLICT_REPLACE);
}
复制代码
/** * 向数据库中插入一行数据的通用方法。 * 参数同上。 * @param table * @param nullColumnHack * @param initialValues * @param conflictAlgorithm 解决冲突的处理算法 * @return 返回插入数据的行序号。当 conflictAlgorithm 错误或者发生插入异常时,返回-1。 */
public long insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm) {
acquireReference();
try {
StringBuilder sql = new StringBuilder();
sql.append("INSERT");
// 冲突处理算法,插入时只用第一个和最后一个。
// {"", "OR ROLLBACK", "OR ABORT", "OR FAIL", "OR IGNORE", "OR REPLACE"};
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(" INTO ");
sql.append(table);
sql.append('(');
Object[] bindArgs = null;
int size = (initialValues != null && !initialValues.isEmpty())
? initialValues.size() : 0;
// 拼接 sql 语句
if (size > 0) {
bindArgs = new Object[size];
int i = 0;
for (String colName : initialValues.keySet()) {
sql.append((i > 0) ? "," : "");
// 拼接列名称
sql.append(colName);
// 保存属性值参数
bindArgs[i++] = initialValues.get(colName);
}
sql.append(')');
sql.append(" VALUES (");
// 将全部的值用 ? 作占位符
for (i = 0; i < size; i++) {
sql.append((i > 0) ? ",?" : "?");
}
} else {
// 插入的数据集合是空时,至少须要有一列数据,列名称是 nullColumnHack,值是 NULL
sql.append(nullColumnHack + ") VALUES (NULL");
}
sql.append(')');
// 执行插入操做
SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
try {
return statement.executeInsert();
} finally {
statement.close();
}
} finally {
releaseReference();
}
}
复制代码
演示代码,不掩饰抛出异常的方法:
ContentValues values = new ContentValues();
values.put("id", 1);
values.put("name", "flueky");
values.put("age", 27);
// 插入完整的数据,id name age
database.insert("user", null, values);
// 插入空数据,将 name 设置为 null
database.insert("user", "name", null);
// 替换 id = 1 的数据
values.put("id", 1);
values.put("name", "xiaofei");
values.put("age", 27);
database.replace("user", null, values);
复制代码
replace
方法相似于 update
操做。
删除数据库表数据,只有一个方法。支持指定删除条件
/** * 删除数据库中一行的方法 * * @param table 须要删除的表名 * @param whereClause 传入 null 时表示删除表中所有数据。 * 或者指定删除条件,只会删除知足条件的行。 * @param whereArgs 指定删除条件的值,按照顺序替换在删除条件中的 ? 。 * @return 删除知足条件的行时,返回删除的行数。找不到知足条件删除的时候,返回 0 。 */
public int delete(String table, String whereClause, String[] whereArgs) {
acquireReference();
try {
SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
(!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
try {
return statement.executeUpdateDelete();
} finally {
statement.close();
}
} finally {
releaseReference();
}
}
复制代码
更新表数据方法有两个,建议只用第一个,conflictAlgorithm
参数默认 CONFLICT_NONE
。
/** * 更新数据库表中的一行数据 * * @param table 须要更新的表名 * @param values 包含属性名和新属性值的 map 集合。 * @param whereClause 可选的 WHERE 条件决定须要更新的行。 * 若是是空,则更新全部的行。 * @param whereArgs 替换在 where 条件中包含的 ? 。 * @return 返回更新的行数 */
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
}
/** * 更新数据库表中的一行数据 * 参数同上 * * @param table * @param values * @param whereClause * @param whereArgs * @param conflictAlgorithm 决定更新冲突的算法。 * @return */
public int updateWithOnConflict(String table, ContentValues values, String whereClause, String[] whereArgs, int conflictAlgorithm) {
// 官方源码中说,null is valid,此处却抛出异常,有点费解
if (values == null || values.isEmpty()) {
throw new IllegalArgumentException("Empty values");
}
acquireReference();
try {
// 拼接更新的 sql 语句
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(table);
sql.append(" SET ");
// 将 values 和 whereArgs 值拼接在一块儿
int setValuesSize = values.size();
int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
Object[] bindArgs = new Object[bindArgsSize];
int i = 0;
// 拼接须要更新的列属性
for (String colName : values.keySet()) {
sql.append((i > 0) ? "," : "");
sql.append(colName);
// 保存属性值
bindArgs[i++] = values.get(colName);
sql.append("=?");
}
// 添加 where 条件的值
if (whereArgs != null) {
for (i = setValuesSize; i < bindArgsSize; i++) {
bindArgs[i] = whereArgs[i - setValuesSize];
}
}
if (!TextUtils.isEmpty(whereClause)) {
sql.append(" WHERE ");
sql.append(whereClause);
}
SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
try {
// 执行 sql 语句
return statement.executeUpdateDelete();
} finally {
statement.close();
}
} finally {
releaseReference();
}
}
复制代码
Android 给数据库的表查询操做提供了丰富的 API,一共10个方法,去除重载方法,共四个。下表中列出,四个方法和使用的参数。
参数 | query | queryWithFactory | rawQuery | rawQueryWithFactory |
---|---|---|---|---|
String table | ● | ● | - | - |
String[] columns | ● | ● | - | - |
String selection | ● | ● | - | - |
String[] selectionArgs | ● | ● | ● | ● |
String groupBy | ● | ● | - | - |
String having | ● | ● | - | - |
String orderBy | ● | ● | - | - |
String limit | ○ | ● | - | - |
boolean distinct | ○ | ● | - | - |
CancellationSignal cancellationSignal | ○ | ○ | ○ | ○ |
CursorFactory cursorFactory | - | ● | - | ● |
String sql | - | - | ● | ● |
String editTable | - | - | - | ● |
参数说明:
null
将返回全部的列。不建议这样作,防止从不用的存储中读取数据。where
条件子句格式同样(不包含 where
自身)。传入 null
将返回表中全部的数据。selection
中使用的 ?
或 sql
中使用的 ?
。GROUP BY
子句同样(不包含 GROUP BY
)传入 null
将不分组。cursor
中。若是使用了分组条件将按照 HAVING
子句(不包含 HAVING
)去格式化分组。传入 null
会将全部的分组都包含在 cursor
中。且不使用分组时,必须用 null
。ORDER BY
的条件语句同样(不包含 ORDER BY
)。传入 null
将不排序。LIMIT
子句。传入 null
不限定查询条数。true
,不然用 false
。null
。若是操做被取消会抛出 OperationCanceledException
。Cursor
对象,若是是 null
,则使用打开数据库时使用的 cursorFactory
。若是打开数据库时也使用 null
,那么自动生成 Cursor
。query
方法,最后都会合并出 sql
执行 rawquery
方法。/** * 最简单也最经常使用的 query 方法。最后拼接好 sql 语句,去执行 rawQuery 方法。 */
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
return query(false, table, columns, selection, selectionArgs, groupBy,
having, orderBy, null /* limit */);
}
/** * 参数最全的 query 方法,全部的参数能够手动指定,或使用 null。除 table。 */
public Cursor queryWithFactory(CursorFactory cursorFactory, boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
acquireReference();
try {
String sql = SQLiteQueryBuilder.buildQueryString(
distinct, table, columns, selection, groupBy, having, orderBy, limit);
return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
findEditTable(table), cancellationSignal);
} finally {
releaseReference();
}
}
/** * 最简单也最经常使用的 rawQuery 方法。能够自定义好查询的 sql 语句后,自主查询。适用于复杂的关系表关联查询。 */
public Cursor rawQuery(String sql, String[] selectionArgs) {
return rawQueryWithFactory(null, sql, selectionArgs, null, null);
}
/** * 参数最全的 rawQuery 方法,使用场景很少。 */
public Cursor rawQueryWithFactory( CursorFactory cursorFactory, String sql, String[] selectionArgs, String editTable, CancellationSignal cancellationSignal) {
acquireReference();
try {
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
cancellationSignal);
return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
selectionArgs);
} finally {
releaseReference();
}
}
复制代码
事务(Transaction
)是一个对数据库执行工做单元。事务(Transaction
)是以逻辑顺序完成的工做单位或序列,能够是由用户手动操做完成,也能够是由某种数据库程序自动完成。
Android
中,SQLite
事务主要提供了三个 API。在结束事务前,如想将事务提交到数据库,须要设置事务完成标记。不然,在事务开启时候作的数据库操做将不会保留。
database.beginTransaction();
/* 此处执行数据库操做 */
database.setTransactionSuccessful();
database.endTransaction();
复制代码
本文到此,已经介绍了 SQLite
经常使用的操做。如想自定义 sql
语句实现更丰富的数据库操做,使用 database.execSQL
方法。使用自定义 sql
查询数据库请使用 database.rawQuery
方法。
为避免篇幅过长, 暂时介绍到这里。关于 SQLite
更多的操做语句和用法,请见后续文章。
以为有用?那打赏一个呗。[去打赏]({{ site.url }}/donate/)
此处是广告:Flueky的技术小站