跳到正文
灵动桌面框架
Esc
本栏目菜单

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.tauriapp.db%APPDATA%\com.agilefr.tauri\app.db
pnpm tauri:dev(叠加 tauri.conf.dev.json)com.agilefr.tauri.devdev-app.db%APPDATA%\com.agilefr.tauri.dev\dev-app.db
pnpm tauri:dev:prod-id(不叠加 dev 配置)com.agilefr.tauridev-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 表示已删除)
);