SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。(摘自百度百科)
鉴于 SQLite
具备如此优秀的业务处理能力,Android 平台自然使用它作为内置的数据库存储模块。存储在 SD 卡上,只是一个文件的形式,可以方便的备份、移植。有兴趣的小伙伴,可以至SQLite官网下载 C 源码研究研究。
另外 Android 平台提供了一整套的 SQLite 操作 API。可以方便快速的创建表、更新表、对表数据进行增删改查。
1 创建数据库
创建数据库方法,分四类。
- create/createInMemory 在内存中创建数据库,当数据库关闭时即销毁。
- openDatabase 打开数据库,可以指定打开方式。
- openOrCreateDatabase 创建并打开数据库。
- getWritableDatabase/getReadableDatabase 打开读写,或只读数据库。
上述四个方式,都可以指定 CursorFactory
、Flags
、DatabaseErrorHandler
(API 11)、OpenParams
(API 27) 四个基本参数。最终都 执行到 openDatabase
方法。
SQLite 对数据库文件添加四种访问权限,做 flags 参数:
- OPEN_READWRITE:读写方式打开数据库文件,可以进行增删改查等操作。
- OPEN_READONLY:只读方式打开数据库文件,只能执行查询操作,插入数据会抛出异常
android.database.sqlite.SQLiteReadOnlyDatabaseException
。
- CREATE_IF_NECESSARY:打开数据库时,如没有数据库文件,便会自动创建,并允许增删改查。
- NO_LOCALIZED_COLLATORS: 使 setLocale 方法不生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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(); } };
SQLiteDatabase.OpenParams openParams = new SQLiteDatabase.OpenParams.Builder() .setCursorFactory(factory) .setErrorHandler(errorHandler) .addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY) .build();
|
1.1 create
直接在内存中创建数据库,create/createInMemory 两个方法,后者在 API 27 才支持,建议使用 create。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
@NonNull public static SQLiteDatabase create(@Nullable CursorFactory factory) { return openDatabase(SQLiteDatabaseConfiguration.MEMORY_DB_PATH, factory, CREATE_IF_NECESSARY); }
@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。
1.2 openDatabase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory, @DatabaseOpenFlags int flags) { return openDatabase(path, factory, flags, null); }
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; }
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; }
|
四个方法中,可以分为两类。
- 前两个分别指定 Flags、CursorFactory 和 DatabaseErrorHandler(API 11)支持。
- 后两个统一由 OpenParams 指定打开数据库的参数。包含但不限于上面三个属性。
1.3 openOrCreateDatabase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public static SQLiteDatabase openOrCreateDatabase(@NonNull File file, @Nullable CursorFactory factory) { return openOrCreateDatabase(file.getPath(), factory); }
public static SQLiteDatabase openOrCreateDatabase(@NonNull String path, @Nullable CursorFactory factory) { return openDatabase(path, factory, CREATE_IF_NECESSARY, null); }
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)
方法。
1.4 SQLiteOpenHelper
SQLiteOpenHelper 是 Android 封装的最人性化的工具。方便开发者自行管理数据库表目录和结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
public class DBOpenHelper extends SQLiteOpenHelper {
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); }
@TargetApi(Build.VERSION_CODES.HONEYCOMB) public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) { super(context, name, factory, version, errorHandler); }
@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) { } }
DBOpenHelper dbHelper = new DBOpenHelper(this, "test.db", null, 1);
SQLiteDatabase database = dbHelper.getWritableDatabase();
|
1.5 使用场景
openDatabase 适合打开已经存在的数据库文件。并且表目录和结构固定,一般只有对数据库表的常规操作。如,已有一个全国城市信息数据库文件,需要在项目中打开。
SQLiteOpenHelper 需要自己重新创建数据库文件,根据后期的需求可能需要对数据库的表目录及结构做修改。升级数据版本,在onUpgrade
方法中处理即可。openDatabase 也可以实现这个需求,只是稍微麻烦些。
2 插入数据
插入操作方法总结为两类,insert 和 replace ,最后都执行 insertWithOnConflict 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
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; } }
public long insertOrThrow(String table, String nullColumnHack, ContentValues values) throws SQLException { return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
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; } }
public long replaceOrThrow(String table, String nullColumnHack, ContentValues initialValues) throws SQLException { return insertWithOnConflict(table, nullColumnHack, initialValues, CONFLICT_REPLACE); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
public long insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm) { acquireReference(); try { StringBuilder sql = new StringBuilder(); sql.append("INSERT"); 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; 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 { sql.append(nullColumnHack + ") VALUES (NULL"); } sql.append(')'); SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs); try { return statement.executeInsert(); } finally { statement.close(); } } finally { releaseReference(); } }
|
演示代码,不掩饰抛出异常的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| ContentValues values = new ContentValues(); values.put("id", 1); values.put("name", "flueky"); values.put("age", 27);
database.insert("user", null, values);
database.insert("user", "name", null);
values.put("id", 1); values.put("name", "xiaofei"); values.put("age", 27); database.replace("user", null, values);
|
replace
方法类似于 update
操作。
3 删除数据
删除数据库表数据,只有一个方法。支持指定删除条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
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(); } }
|
4 更新数据
更新表数据方法有两个,建议只用第一个,conflictAlgorithm
参数默认 CONFLICT_NONE
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) { return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE); }
public int updateWithOnConflict(String table, ContentValues values, String whereClause, String[] whereArgs, int conflictAlgorithm) { if (values == null || values.isEmpty()) { throw new IllegalArgumentException("Empty values"); }
acquireReference(); try { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); sql.append(CONFLICT_VALUES[conflictAlgorithm]); sql.append(table); sql.append(" SET ");
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("=?"); } 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 { return statement.executeUpdateDelete(); } finally { statement.close(); } } finally { releaseReference(); } }
|
5 查询数据
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 |
- |
- |
- |
● |
参数说明:
- table 需要查询的表名。
- columns 列出需要查询的列。传递
null
将返回所有的列。不建议这样做,防止从不用的存储中读取数据。
- selection 声明需要返回表数据的过滤器,同
where
条件子句格式一样(不包含 where
自身)。传入 null
将返回表中所有的数据。
- selectionArgs 替换在
selection
中使用的 ?
或 sql
中使用的 ?
。
- groupBy 申明返回的表数据的分组规则,同
GROUP BY
子句一样(不包含 GROUP BY
)传入 null
将不分组。
- having 决定哪些分组会包含在
cursor
中。如果使用了分组条件将按照 HAVING
子句(不包含 HAVING
)去格式化分组。传入 null
会将所有的分组都包含在 cursor
中。且不使用分组时,必须用 null
。
- orderBy 对表数据进行排序。同
ORDER BY
的条件语句一样(不包含 ORDER BY
)。传入 null
将不排序。
- limit 限定查询数据的条数。同
LIMIT
子句。传入 null
不限定查询条数。
- distinct 如果想每行数据不一样,用
true
,否则用 false
。
- cancellationSignal 取消查询进程中的操作信号。如果不需要手动取消,使用
null
。如果操作被取消会抛出 OperationCanceledException
。
- cursorFactory 生成
Cursor
对象,如果是 null
,则使用打开数据库时使用的 cursorFactory
。如果打开数据库时也使用 null
,那么自动生成 Cursor
。
- sql 所有的
query
方法,最后都会合并出 sql
执行 rawquery
方法。
- editTable 指定需要编辑的表名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
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 ); }
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(); } }
public Cursor rawQuery(String sql, String[] selectionArgs) { return rawQueryWithFactory(null, sql, selectionArgs, null, null); }
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(); } }
|
6 事务
事务(Transaction
)是一个对数据库执行工作单元。事务(Transaction
)是以逻辑顺序完成的工作单位或序列,可以是由用户手动操作完成,也可以是由某种数据库程序自动完成。
Android
中,SQLite
事务主要提供了三个 API。在结束事务前,如想将事务提交到数据库,需要设置事务完成标记。否则,在事务开启时候做的数据库操作将不会保留。
1 2 3 4
| database.beginTransaction();
database.setTransactionSuccessful(); database.endTransaction();
|
7 结束语
本文到此,已经介绍了 SQLite
常用的操作。如想自定义 sql
语句实现更丰富的数据库操作,使用 database.execSQL
方法。使用自定义 sql
查询数据库请使用 database.rawQuery
方法。
示例代码
为避免篇幅过长, 暂时介绍到这里。关于 SQLite
更多的操作语句和用法,请见后续文章。
觉得有用?那打赏一个呗。[去打赏](/donate/)