本篇目标
理解 SQLite 的基本概念与使用场景,掌握原生 SQLite 的建库、建表、增删改查,了解常用第三方库的选型与基本用法。
一、SQLite 是什么
SQLite 是一个轻量级的嵌入式关系型数据库,iOS 系统内置支持。
核心特点:
无需服务器,单文件存储
支持标准 SQL 语法
事务安全
跨平台
系统自带,无额外依赖
与 Core Data 的区别:
维度 | SQLite | Core Data |
|---|---|---|
定位 | 数据库 | 对象图管理框架 |
操作方式 | SQL 语句 | 面向对象 |
学习曲线 | 需懂 SQL | 需理解框架概念 |
性能 | 高,可控 | 高,自动优化 |
灵活度 | 高 | 中 |
适用场景 | 复杂查询、跨平台 | iOS 原生应用 |
选择建议:
iOS 原生项目、SwiftUI 深度集成:优先 Core Data。
需要精确控制 SQL、跨平台、已有数据库:考虑 SQLite。
第三方封装库(如 GRDB)可以兼顾易用性和性能。
二、原生 SQLite 使用
导入:
import SQLite3打开数据库:
var db: OpaquePointer?
let fileURL = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first!
.appendingPathComponent("app.db")
if sqlite3_open(fileURL.path, &db) == SQLITE_OK {
print("打开成功")
} else {
print("打开失败")
}关闭数据库:
sqlite3_close(db)创建表:
let createSQL = """
CREATE TABLE IF NOT EXISTS notes (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
content TEXT,
created_at REAL
);
"""
var statement: OpaquePointer?
if sqlite3_prepare_v2(db, createSQL, -1, &statement, nil) == SQLITE_OK {
if sqlite3_step(statement) == SQLITE_DONE {
print("建表成功")
}
}
sqlite3_finalize(statement)插入数据:
let insertSQL = "INSERT INTO notes (id, title, content, created_at) VALUES (?, ?, ?, ?);"
if sqlite3_prepare_v2(db, insertSQL, -1, &statement, nil) == SQLITE_OK {
sqlite3_bind_text(statement, 1, (UUID().uuidString as NSString).utf8String, -1, nil)
sqlite3_bind_text(statement, 2, ("标题" as NSString).utf8String, -1, nil)
sqlite3_bind_text(statement, 3, ("内容" as NSString).utf8String, -1, nil)
sqlite3_bind_double(statement, 4, Date().timeIntervalSince1970)
if sqlite3_step(statement) == SQLITE_DONE {
print("插入成功")
}
}
sqlite3_finalize(statement)查询数据:
let querySQL = "SELECT id, title, content FROM notes ORDER BY created_at DESC;"
if sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK {
while sqlite3_step(statement) == SQLITE_ROW {
let id = String(cString: sqlite3_column_text(statement, 0))
let title = String(cString: sqlite3_column_text(statement, 1))
let content = String(cString: sqlite3_column_text(statement, 2))
print(id, title, content)
}
}
sqlite3_finalize(statement)更新与删除:
let updateSQL = "UPDATE notes SET title = ? WHERE id = ?;"
let deleteSQL = "DELETE FROM notes WHERE id = ?;"使用原生 SQLite 的痛点:
C API,代码冗长。
需要手动管理 statement 生命周期。
类型转换繁琐。
容易出错,不易维护。
三、第三方库选型
常见 SQLite 封装库:
库 | 特点 | 适用场景 |
|---|---|---|
GRDB.swift | 功能强大,类型安全,Swift 原生 | 中大型项目 |
SQLite.swift | 类型安全,链式语法 | 中小型项目 |
FMDB | Objective-C 封装,成熟稳定 | 老项目、OC 项目 |
Realm | 面向对象,非 SQLite | 需要实时同步 |
WCDB | 微信开源,性能好 | 高性能场景 |
选型建议:
新项目、纯 Swift:GRDB.swift。
需要轻量链式 API:SQLite.swift。
维护老 OC 项目:FMDB。
不想写 SQL、需要对象映射:Realm(注意闭源风险)。
极致性能:WCDB。
四、GRDB.swift 入门
GRDB 是 Swift 社区广泛使用的 SQLite 封装库,类型安全、性能好。
安装(Swift Package Manager):
Xcode → File → Add Packages
输入仓库地址:
https://github.com/groue/GRDB.swift选择版本,添加到项目
定义模型:
import GRDB
struct Note: Codable, FetchableRecord, PersistableRecord {
var id: String
var title: String
var content: String
var createdAt: Date
static let databaseTableName = "notes"
}打开数据库:
import GRDB
let dbQueue = try DatabaseQueue(path: fileURL.path)创建表:
try dbQueue.write { db in
try db.create(table: "notes") { t in
t.column("id", .text).primaryKey()
t.column("title", .text).notNull()
t.column("content", .text)
t.column("createdAt", .datetime)
}
}插入数据:
try dbQueue.write { db in
var note = Note(id: UUID().uuidString, title: "标题", content: "内容", createdAt: Date())
try note.insert(db)
}查询数据:
let notes = try dbQueue.read { db in
try Note.order(Column("createdAt").desc).fetchAll(db)
}条件查询:
let notes = try dbQueue.read { db in
try Note.filter(Column("title") == "标题").fetchAll(db)
}更新数据:
try dbQueue.write { db in
var note = try Note.fetchOne(db, key: "id")!
note.title = "新标题"
try note.update(db)
}删除数据:
try dbQueue.write { db in
_ = try Note.deleteOne(db, key: "id")
}观察数据变化:
let observation = ValueObservation.tracking { db in
try Note.fetchAll(db)
}
observation.start(in: dbQueue) { notes in
print("数据更新:\(notes.count) 条")
}优点:
Swift 原生语法,类型安全。
支持 Codable。
支持数据观察。
支持事务、迁移。
五、SQLite.swift 入门
SQLite.swift 提供类型安全的链式 API。
安装:
通过 SPM 添加 https://github.com/stephencelis/SQLite.swift
使用:
import SQLite
let db = try Connection("path/to/db.sqlite3")
let notes = Table("notes")
let id = Expression<String>("id")
let title = Expression<String>("title")
let content = Expression<String>("content")
try db.run(notes.create { t in
t.column(id, primaryKey: true)
t.column(title)
t.column(content)
})
try db.run(notes.insert(id <- UUID().uuidString, title <- "标题", content <- "内容"))
for note in try db.prepare(notes) {
print(note[title])
}特点:
链式调用,接近 SQL 语法。
类型安全。
学习成本低。
六、Realm 简介
Realm 是一个面向对象的移动数据库,不是 SQLite 封装。
特点:
直接操作 Swift 对象。
实时同步(Realm Sync)。
性能好。
跨平台。
示例:
import RealmSwift
class Note: Object {
@Persisted(primaryKey: true) var id: String
@Persisted var title: String
@Persisted var content: String
@Persisted var createdAt: Date
}
let realm = try! Realm()
try! realm.write {
let note = Note()
note.id = UUID().uuidString
note.title = "标题"
note.content = "内容"
note.createdAt = Date()
realm.add(note)
}
let notes = realm.objects(Note.self).sorted(byKeyPath: "createdAt", ascending: false)注意:
Realm 需要额外依赖。
部分功能闭源(Realm Sync)。
与 SwiftUI 集成需要额外处理。
七、选型对比
方案 | 学习成本 | 性能 | 灵活度 | 推荐场景 |
|---|---|---|---|---|
原生 SQLite | 高 | 高 | 高 | 学习底层 |
GRDB | 中 | 高 | 高 | 新 Swift 项目 |
SQLite.swift | 低 | 高 | 中 | 中小项目 |
Core Data | 中 | 高 | 中 | iOS 原生 |
Realm | 低 | 高 | 中 | 快速开发 |
建议:
项目已经在用 Core Data,继续用。
新项目需要复杂查询,选 GRDB。
想快速上手,用 SQLite.swift。
尽量不用原生 SQLite,维护成本高。
八、数据库设计原则
表设计:
主键:UUID 或自增 ID。
字段类型:TEXT、INTEGER、REAL、BLOB。
时间字段:存储为 REAL 或 TEXT。
索引:
CREATE INDEX idx_notes_created_at ON notes(created_at);索引使用建议:
经常查询的字段加索引。
索引过多会影响写入性能。
复合索引注意字段顺序。
事务:
try dbQueue.write { db in
try db.inTransaction {
try note1.insert(db)
try note2.insert(db)
return .commit
}
}事务的好处:
保证数据一致性。
提升批量写入性能。
📌 进阶内容:数据迁移(版本升级、结构变更)属进阶内容,已放到第 6 章《数据迁移与备份》。
📌 进阶内容:性能优化(批量写入、事务、索引、分页、避免 N+1)已放到第 8 章《本地存储性能》。
九、避坑指南
原生 SQLite 不建议直接使用:封装库更安全。
SQL 注入:使用参数绑定,不要拼接字符串。
线程安全:数据库操作要在合适队列,不要跨线程共享连接。
事务不要嵌套太深:容易死锁。
索引不是越多越好:写入性能会下降。
时间字段统一格式:存储和读取保持一致。
迁移要测试:升级前测试旧数据兼容性。
十、检查清单
[ ] 理解 SQLite 的定位与场景
[ ] 了解原生 SQLite 的基本 API
[ ] 掌握常用第三方库选型
[ ] 能使用 GRDB 完成增删改查
[ ] 了解 SQLite.swift 与 Realm
[ ] 理解数据库设计原则
[ ] 会使用事务和索引
[ ] 了解数据迁移与性能优化