跳到主要内容

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 步:

  1. 创建临时表(新结构)
  2. 从旧表复制数据
  3. 删除旧表
  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")

相关链接