SQLite 设计
灵动桌面框架使用 SQLite 作为本地数据库,通过 rusqlite 库操作(Cargo.toml 中为 rusqlite = { version = "0.31", features = ["bundled"] },bundled 会把 SQLite 一起编译进应用,用户机器上无需另装 SQLite)。
为什么选择 SQLite
- 零配置 — 无需安装数据库服务
- 文件级存储 — 单个
.db文件,首次启动时在应用数据目录自动创建 - 性能优秀 — 单用户桌面应用场景下性能极佳
- 可靠性 — ACID 事务保证数据完整性
数据库初始化
// src-tauri/src/database/mod.rs
pub mod schema;
use std::sync::Mutex;
use rusqlite::Connection;
use crate::error::AppError;
/// 数据库封装,线程安全
pub struct Database {
conn: Mutex<Connection>,
}
impl Database {
/// 初始化数据库(创建或打开 + 自动迁移)
pub fn init(db_path: &str) -> Result<Self, AppError> {
let conn = Connection::open(db_path)?;
// 启用 WAL 模式提升并发性能
conn.pragma_update(None, "journal_mode", "WAL")?;
// 设置忙等待超时,防止并发写入死锁
conn.busy_timeout(std::time::Duration::from_secs(5))?;
// 执行 Schema 迁移
schema::migrate(&conn)?;
Ok(Self {
conn: Mutex::new(conn),
})
}
}
- 整个应用只持有一个
Connection,用Mutex包起来保证线程安全 - 迁移在打开连接后、交给业务使用前完成,详见 Schema 迁移
全局状态注册
// src-tauri/src/lib.rs(.setup 闭包内)
let data_dir = app.path().app_data_dir()?;
std::fs::create_dir_all(&data_dir)?;
// 开发与生产用不同文件名隔离数据,避免开发环境污染生产数据
let db_filename = if cfg!(debug_assertions) {
"dev-app.db"
} else {
"app.db"
};
let db_path = data_dir.join(db_filename);
let db_path_str = db_path.to_string_lossy().to_string();
let db = database::Database::init(&db_path_str)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// 注册全局状态
app.manage(AppState::new(db));
// src-tauri/src/state.rs
pub struct AppState {
pub db: Database,
}
Command 中通过 state: tauri::State<'_, AppState> 拿到 state.db。
数据库文件位置
数据库文件放在 Tauri 的 app_data_dir() 下,目录名取自 tauri.conf.json 的 identifier:
| 运行方式 | identifier | 文件名 | Windows 路径示例 |
|---|---|---|---|
pnpm tauri build 打出的正式包 | com.agilefr.tauri | app.db | %APPDATA%\com.agilefr.tauri\app.db |
pnpm tauri:dev(叠加 tauri.conf.dev.json) | com.agilefr.tauri.dev | dev-app.db | %APPDATA%\com.agilefr.tauri.dev\dev-app.db |
pnpm tauri:dev:prod-id(不叠加 dev 配置) | com.agilefr.tauri | dev-app.db | %APPDATA%\com.agilefr.tauri\dev-app.db |
TIP
衍生项目改了 identifier 后,数据目录随之变化。实际路径可通过 get_system_info Command 返回的 data_dir 字段确认;启动日志里也会打印 数据库初始化完成: <完整路径>。
表设计规范
- 业务表主键使用
INTEGER PRIMARY KEY AUTOINCREMENT;键值型表(如内置的app_config)可直接用key TEXT PRIMARY KEY - 时间字段使用
TEXT,默认值datetime('now', 'localtime'),存储格式为本地时间YYYY-MM-DD HH:MM:SS - 每张表包含
created_at和updated_at - 需要可恢复删除的表加
deleted_at TEXT DEFAULT NULL软删除字段,查询统一带WHERE deleted_at IS NULL
框架内置的 app_config 表(v0→v1 建表,v1→v2 加软删除字段)最终结构:
CREATE TABLE IF NOT EXISTS app_config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')),
updated_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime'))
);
-- v1 -> v2
ALTER TABLE app_config ADD COLUMN deleted_at TEXT DEFAULT NULL;
建表时会写入三条默认配置:theme = light、language = zh-CN、sidebar_collapsed = false。
新增业务表可参考以下模板:
CREATE TABLE IF NOT EXISTS {table_name} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
-- 业务字段
name TEXT NOT NULL,
status INTEGER DEFAULT 1, -- 0: 禁用, 1: 正常
-- 审计字段
created_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')),
updated_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')),
deleted_at TEXT DEFAULT NULL -- 软删除标记(非 NULL 表示已删除)
);