SQLite 基础
问题
Android SQLite 的核心概念是什么?数据库版本升级如何处理?
答案
SQLiteOpenHelper
Android 通过 SQLiteOpenHelper 管理数据库的创建和升级:
class AppDatabase(context: Context) : SQLiteOpenHelper(
context, "app.db", null, 3 // version = 3
) {
override fun onCreate(db: SQLiteDatabase) {
// 首次安装,创建所有表
db.execSQL("""
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE,
age INTEGER DEFAULT 0
)
""")
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
// 逐版本迁移
if (oldVersion < 2) {
db.execSQL("ALTER TABLE users ADD COLUMN age INTEGER DEFAULT 0")
}
if (oldVersion < 3) {
db.execSQL("ALTER TABLE users ADD COLUMN avatar TEXT")
}
}
}
事务操作
fun transferMoney(fromId: Long, toId: Long, amount: Double) {
val db = helper.writableDatabase
db.beginTransaction()
try {
db.execSQL("UPDATE accounts SET balance = balance - ? WHERE id = ?",
arrayOf(amount, fromId))
db.execSQL("UPDATE accounts SET balance = balance + ? WHERE id = ?",
arrayOf(amount, toId))
db.setTransactionSuccessful()
} finally {
db.endTransaction()
}
}
SQL 注入防御
永远使用参数化查询,不要拼接 SQL 字符串:
// ❌ SQL 注入风险
db.rawQuery("SELECT * FROM users WHERE name = '$input'", null)
// ✅ 参数化查询
db.rawQuery("SELECT * FROM users WHERE name = ?", arrayOf(input))
为什么推荐 Room
直接使用 SQLite API 存在以下问题:
- 手写 SQL 无编译期检查
- Cursor 操作繁琐,容易遗漏
close - 无法直接返回 Flow 进行数据观察
- 线程安全需要手动管理
Room 作为 SQLite 的抽象层解决了这些问题。
常见面试问题
Q1: SQLite 的 WAL 模式是什么?
答案:
WAL(Write-Ahead Logging)模式下,写操作追加到 WAL 文件而不是直接修改数据库文件,读操作仍然读取原文件。好处是读写可以并发(普通模式读写互斥)。Android 默认为 Room 开启 WAL 模式。
Q2: onUpgrade 中如何安全地修改表结构?
答案:
SQLite 的 ALTER TABLE 功能有限(只能 ADD COLUMN、RENAME),复杂修改需要 4 步:
- 创建临时表(新结构)
- 从旧表复制数据
- 删除旧表
- 重命名临时表
db.execSQL("CREATE TABLE users_new (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
db.execSQL("INSERT INTO users_new SELECT id, name FROM users")
db.execSQL("DROP TABLE users")
db.execSQL("ALTER TABLE users_new RENAME TO users")