3、SQLiteDatabase核心API:execSQL与rawQuery、insert/update/delete/query方法、事务处理beginTransaction、编译优化compileStatement
说实话,Android开发里跟SQLite打交道,绕不开的就是SQLiteDatabase这个类。它就像是你跟数据库之间的桥梁,所有增删改查都得通过它来干。今天我就把这几个核心API掰开揉碎了讲清楚,都是我在项目里实打实踩过的坑。
3.1 execSQL与rawQuery:最原始的SQL执行方式
先说说execSQL和rawQuery。这俩是最底层的API,说白了就是直接往数据库里扔SQL语句。
execSQL:只执行,不返回
execSQL用来执行那些不需要返回结果集的SQL,比如CREATE TABLE、INSERT、UPDATE、DELETE。它不返回任何数据,只告诉你执行成功还是失败。
// 创建表
db.execSQL("CREATE TABLE IF NOT EXISTS user (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT NOT NULL, " +
"age INTEGER)");
// 插入数据
db.execSQL("INSERT INTO user(name, age) VALUES('张三', 25)");
// 更新数据
db.execSQL("UPDATE user SET age = 26 WHERE name = '张三'");
rawQuery:查询并返回Cursor
rawQuery专门用来执行SELECT语句,返回一个Cursor对象。Cursor就像是一个游标,指向查询结果集的某一行。
Cursor cursor = db.rawQuery("SELECT * FROM user WHERE age > ?", new String[]{"20"});
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
// 处理数据...
}
cursor.close(); // 别忘了关闭!
3.2 insert/update/delete/query:封装好的便捷方法
说实话,execSQL和rawQuery虽然灵活,但写起来有点啰嗦。Google也想到了这点,所以提供了insert、update、delete、query这四个封装好的方法。我个人更推荐用这些,代码更清晰,也不容易出错。
insert:插入数据
ContentValues values = new ContentValues();
values.put("name", "李四");
values.put("age", 30);
long rowId = db.insert("user", null, values);
if (rowId != -1) {
// 插入成功,rowId是新行的ID
}
这里有个小细节:第二个参数nullColumnHack。当values为空时,它会指定一个允许为NULL的列名,避免插入空行。不过我个人习惯是values不为空,所以直接传null。
update:更新数据
ContentValues values = new ContentValues();
values.put("age", 31);
int rowsAffected = db.update("user", values, "name = ?", new String[]{"李四"});
// rowsAffected表示受影响的行数
delete:删除数据
int rowsDeleted = db.delete("user", "age < ?", new String[]{"18"});
// 删除所有age小于18的用户
query:查询数据
Cursor cursor = db.query("user", // 表名
new String[]{"id", "name", "age"}, // 要查询的列
"age > ?", // WHERE条件
new String[]{"20"}, // WHERE参数
null, // GROUP BY
null, // HAVING
"age DESC"); // ORDER BY
3.3 事务处理:beginTransaction与endTransaction
事务处理这块,我得好好说说。你在项目里肯定会遇到需要同时执行多条SQL的场景,比如转账、批量导入数据。这时候事务就派上用场了。
为什么需要事务?
想象一下:你要给用户A扣100元,给用户B加100元。如果扣完A的钱后程序崩溃了,B没收到钱,那账就对不上了。事务能保证:要么全部成功,要么全部回滚。
基本用法
db.beginTransaction();
try {
// 执行多条SQL
db.execSQL("UPDATE account SET balance = balance - 100 WHERE name = 'A'");
db.execSQL("UPDATE account SET balance = balance + 100 WHERE name = 'B'");
db.setTransactionSuccessful(); // 标记事务成功
} catch (Exception e) {
// 异常时自动回滚
} finally {
db.endTransaction(); // 结束事务
}
批量插入的性能优化
如果你要插入1000条数据,逐条插入会非常慢。用事务包裹起来,速度能提升几十倍。
db.beginTransaction();
try {
for (int i = 0; i < 1000; i++) {
ContentValues values = new ContentValues();
values.put("name", "用户" + i);
values.put("age", i % 100);
db.insert("user", null, values);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
3.4 编译优化:compileStatement
最后说说compileStatement。这玩意儿你可能用得不多,但在高频写入场景下,它简直是性能利器。
什么是compileStatement?
简单说,它就是预编译SQL语句。你把SQL模板编译一次,后面只需要绑定参数就能执行,省去了每次解析SQL的开销。
使用示例
String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
SQLiteStatement statement = db.compileStatement(sql);
db.beginTransaction();
try {
for (int i = 0; i < 10000; i++) {
statement.clearBindings();
statement.bindString(1, "用户" + i); // 绑定第1个参数
statement.bindLong(2, i % 100); // 绑定第2个参数
statement.executeInsert(); // 执行插入
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
statement.close(); // 别忘了释放
}
compileStatement的常用方法
| 方法 | 说明 |
|---|---|
| bindNull(int index) | 绑定NULL值 |
| bindLong(int index, long value) | 绑定整型值 |
| bindDouble(int index, double value) | 绑定浮点值 |
| bindString(int index, String value) | 绑定字符串值 |
| executeInsert() | 执行INSERT,返回新行ID |
| executeUpdateDelete() | 执行UPDATE或DELETE,返回受影响行数 |
| simpleQueryForLong() | 执行查询,返回long值 |
| simpleQueryForString() | 执行查询,返回String值 |
知识体系总览
下面这张图把今天讲的核心API串起来了,你可以对照着回顾一下。
嗯,以上就是SQLiteDatabase最核心的几个API。从最原始的execSQL/rawQuery,到封装好的insert/update/delete/query,再到事务处理和编译优化,每个都有自己最适合的场景。我个人建议:日常开发优先用封装方法,批量操作上事务,高频写入用compileStatement。这样写出来的代码既清晰又高效。
公众号:蓝海资料掘金营,微信deep3321