软软AI中增加sqlite数据库,Tauri中使用sqlite

1,755 阅读3分钟

软软AI中增加sqlite数据库,Tauri中使用sqlite

集成sqlite的需求

作为桌面端,或者单机版软件,数据存储是需要的,集成一个关系型数据库对应一些功能的开发是十分方便的。作为文件型数据库sqlite应用比较广泛,因此将sqlite集成到tauri的项目中。

选取rust中的sqlite第三方库

笔者选取的是rusqlite docs.rs/rusqlite/la…

tauri中的集成方式

在tauri中可以使用command进行扩展、可以使用plugin的方式、也可以采用自定义协议的方式。为了提高复用性,笔者将sqlite作为plugin的方式进行接入。

plugin工程

工程创建

详细可以参见tauri的文档:tauri.app/zh-cn/v1/gu…


pnpm tauri plugin init --name awesome

插件的详细代码可以参见github: github.com/rrkeji/rrai…

封装sqlite数据库的操作接口

  • 首先是打开的数据库连接管理

可以使用tauri中提供的state方式进行管理

pub struct SqliteMap(Mutex<HashMap<String, Connection>>);

#[command]
pub async fn open(state: State<'_, SqliteMap>, path: String) -> Result<bool> {
}

可以使用文件的路径作为key。

也可以直接在内存中使用rust的静态变量维护一个全局的map

lazy_static! {
    pub(crate) static ref CONNECTIONS: RwLock<HashMap<String, Arc<Mutex<Connection>>>> =
        RwLock::new(HashMap::new());
}


  • 查询接口封装
  • 更新接口封装
  • 关闭接口封装

#[command]
pub async fn open(path: String) -> Result<bool> {
    rrai_desktop_sdk_common::sqlite::open(&path).await?;
    Ok(true)
}

#[command]
pub async fn open_with_flags(path: String, iflags: i32) -> Result<bool> {
    let flags = OpenFlags::default();

    rrai_desktop_sdk_common::sqlite::open_with_flags(&path, flags).await?;
    Ok(true)
}

#[command]
pub async fn close(path: String) -> Result<bool> {
    rrai_desktop_sdk_common::sqlite::close(&path).await?;
    Ok(true)
}

#[command]
pub async fn execute_sql(path: String, sql: String) -> Result<usize> {
    let res = rrai_desktop_sdk_common::sqlite::execute_sql(&path, &sql).await?;
    Ok(res)
}

#[command]
pub async fn execute_batch(path: String, sql: String) -> Result<bool> {
    let res = rrai_desktop_sdk_common::sqlite::execute_batch(&path, &sql).await?;
    Ok(res)
}

#[command]
pub async fn execute(path: String, sql: String, args: JsonValue) -> Result<usize> {
    let res = rrai_desktop_sdk_common::sqlite::execute(&path, &sql, &args).await?;
    Ok(res)
}

#[command]
pub async fn query_with_args(
    path: String,
    sql: String,
    args: JsonValue,
) -> Result<Vec<HashMap<String, JsonValue>>> {
    let res = rrai_desktop_sdk_common::sqlite::query_with_args(&path, &sql, &args).await?;
    Ok(res)
}



/// Initializes the plugin.
pub fn init<R: Runtime>() -> TauriPlugin<R> {
    Builder::new("rrai-sqlite")
        .invoke_handler(tauri::generate_handler![
            open,
            open_with_flags,
            query_with_args,
            close,
            execute_sql,
            execute_batch,
            execute
        ])
        .setup(|app| {
            // app.manage(SqliteMap::default());
            Ok(())
        })
        .build()
}


如果使用tauri的state时,在init的setup中进行state的初始化。

详细的代码参见git github.com/rrkeji/rrai…

软软AI项目中引入plugin

tauri::Builder::default()
        .setup(move |app| {
            // let window = app.get_window("main").unwrap();
            // set_shadow(&window, true).expect("Unsupported platform!");
            #[cfg(debug_assertions)]
            {
                let window = app.get_window("main").unwrap();
                window.open_devtools();
            }
            Ok(())
        })
        .plugin(rrai_desktop_sdk::plugins::storage::init())
        .plugin(rrai_desktop_sdk::plugins::appbox::init())
        .plugin(rrai_desktop_sdk::plugins::idns::init())
        .plugin(rrai_desktop_sdk::plugins::sqlite::init())
        .plugin(rrai_desktop_sdk::plugins::substrate::init())
        .invoke_handler(tauri::generate_handler![
            commands::system::cmds_system_process_exec
        ])
        .register_uri_scheme_protocol("rrapp", rrai_desktop_sdk::plugins::appbox::rrapp_protocol)
        .run(tauri::generate_context!())
        .expect("error while running tauri application");

在软软AI的tauri主项目中,引用插件,主要是.plugin(rrai_desktop_sdk::plugins::sqlite::init()) 这句。

封装前端接口

import { invoke } from '@tauri-apps/api/tauri'

export class SQLite {
    path: string

    constructor(path: string) {
        this.path = path
    }

    static async open(path: string): Promise<SQLite> {
        let res = await invoke<string>('plugin:rrai-sqlite|open', { path });
        console.log(res);
        return new SQLite(path);
    }

    async close(): Promise<boolean> {
        return await invoke('plugin:rrai-sqlite|close', { path: this.path })
    }

    async execute(sql: string, values?: Record<string, any>): Promise<boolean> {
        return values ? await invoke('plugin:rrai-sqlite|execute', { path: this.path, sql, args: values }) : await invoke('plugin:sqlite|execute_sql', { path: this.path, sql })
    }

    async queryWithArgs<T>(sql: string, values?: Record<string, any>): Promise<T> {
        return await invoke('plugin:rrai-sqlite|query_with_args', { path: this.path, sql, args: values ?? {} })
    }
}

export default SQLite;

在ts中对应的插件中的方法进行封装。

调用示例

 const dbversion = await SQLite.open(dbName);

    //查询是否有该表
    const rows = await dbversion.queryWithArgs<Array<{ count: number }>>("SELECT count(1) count FROM sqlite_master WHERE type='table' and name = 'databases_version' ");

    console.log(rows);
    if (!!rows && rows.length > 0 && rows[0].count > 0) {

    } else {
        //创建表
        await dbversion.execute(`CREATE TABLE databases_version (name TEXT, version INTEGER, unique(name));`)
    }

这样前端就可以不依赖后端工程师,直接操作数据库了。 关于数据库的版本问题,下一篇实现一个简单的数据库脚本版本控制的代码。

github:github.com/rrkeji/rrai…