【Tauri2】035——sql和sqlx

前言

这篇就来看看插件sql

SQL | Taurihttps://tauri.app/plugin/sql/

正文

准备

添加依赖

复制代码
tauri-plugin-sql = {version = "2.2.0",features = ["sqlite"]}

features可以是mysql、sqlite、postsql

进去features看看

复制代码
sqlite = [
    "sqlx/sqlite",
    "sqlx/runtime-tokio",
]

可以发现本质使用的是sqlx

sqlx - Rusthttps://docs.rs/sqlx/latest/sqlx/注册插件

复制代码
        .plugin(tauri_plugin_sql::Builder::default().build())

并不像其他插件一样,有init方法,因为需要一些配置,比如连接数据库。

配置

要想配置数据库,如果数据库里面有东西,比如表,需要用到如下函数add_migrations

rust 复制代码
    pub fn add_migrations(mut self,
                          db_url: &str, 
                          migrations: Vec<Migration>) -> Self 

需要传入db_urlmigrations

比如db_url可以设置sqlite:start.db

migrations是个Vec,元素类型是Migration

Migration的定义如下

rust 复制代码
#[derive(Debug)]
pub struct Migration {
    pub version: i64,
    pub description: &'static str,
    pub sql: &'static str,
    pub kind: MigrationKind,
}

MigrationKind是指定迁移的类型

rust 复制代码
#[derive(Debug)]
pub enum MigrationKind {
    Up,
    Down,
}

项目结构如下

在migration.rs中

rust 复制代码
use tauri_plugin_sql::{Migration,MigrationKind};
pub fn get_migration()->Vec<Migration>{
    vec![
        // Define your migrations here
        Migration {
            version: 1,
            description: "Create book table",
            sql: r"
                     CREATE TABLE book (
                        id INTEGER PRIMARY KEY,
                        author TEXT,
                        title TEXT,
                        published_date date
                            );
                ",
            kind: MigrationKind::Up,
        }
    ] 
}

创建了一张表

注册

rust 复制代码
       .plugin(tauri_plugin_sql::Builder::default()
            .add_migrations("sqlite:start.db",get_migration())
            .build())

想要在后端使用

怎么在后端使用,这确实是个问题,笔者发现好像没有在后端使用的东西,全都是前端调用

比如说

rust 复制代码
    pub(crate) async fn connect<R: Runtime>(
        conn_url: &str,
        _app: &AppHandle<R>,
    ) -> Result<Self, crate::Error> 

按道理来说,connect应该可以使用,但是这是私有的,只在crate内部公开,

笔者也没找到怎么使用,

看看内部的通信函数

rust 复制代码
#[command]
pub(crate) async fn load<R: Runtime>(
    app: AppHandle<R>,
    db_instances: State<'_, DbInstances>,
    migrations: State<'_, Migrations>,
    db: String,
) -> Result<String, crate::Error> {
    let pool = DbPool::connect(&db, &app).await?;

    if let Some(migrations) = migrations.0.lock().await.remove(&db) {
        let migrator = Migrator::new(migrations).await?;
        pool.migrate(&migrator).await?;
    }

    db_instances.0.write().await.insert(db.clone(), pool);

    Ok(db)
}
rust 复制代码
impl DbPool {
    pub(crate) async fn connect
    pub(crate) async fn migrate

    pub(crate) async fn close(&self) 
    pub(crate) async fn execute
    }

可以看出使用了DbInstances和Migrations,两个State

要先连接DbPool::connect,但是这个connect没公开,其他方法也没有公开

看来这个插件就是写在前端的。后端无法使用。

要么使用sqlx,要么修改源码。额,都很麻烦。

笔者不在后端使用这个插件。直接使用sqlx

sqlx

暂时丢掉插件,简单使用一下这个crate

创建一个数据库和book表

添加依赖

rust 复制代码
sqlx = { version = "0.8.5", features = ["sqlite", "runtime-tokio"] }

在connect.rs中,定义DbInstances ,然后将其注册成State

rust 复制代码
use sqlx::sqlite::SqlitePoolOptions;
use sqlx::Sqlite;
use sqlx::Pool;
use tauri::{command, AppHandle, State};
async fn connect()-> Pool<Sqlite> {
    SqlitePoolOptions::new()
        .connect("sqlite:start.db").await.unwrap()

}
pub struct DbInstances {
    pub db: Pool<Sqlite>
}
impl DbInstances {
    pub async fn new() -> Self {
        Self {
            db: connect().await
        }
    }
}

通信函数

rust 复制代码
use sqlx::Executor;
use crate::sql::connect::DbInstances;
use tauri::{command, AppHandle, State};
use tauri::Result;

#[command]
pub async fn insert_one(state: State<'_, DbInstances>)->Result<()> {
    let db=state.db.clone();
    db.execute(r"
        INSERT INTO book (id, author, title, published_date) VALUES (2, 'gg', 'good', '2024-04-20');
    "
    ).await.unwrap();
    Ok(())

}

注册State通信函数之后。

执行后

成功。简单地使用了一下sqlx

前端使用插件sql

既然这个插件是为前端服务的,就在前端简单使用一下。

添加依赖

rust 复制代码
pnpm add @tauri-apps/plugin-sql

后端注册完成后,简单的代码如下

TypeScript 复制代码
import Database,{QueryResult}  from '@tauri-apps/plugin-sql';
export async function useSql(){
    const db = await Database.load('sqlite:start.db');
    let a=await db.execute("INSERT INTO book (id, author, title, published_date) VALUES (22, 'gg', 'good', '2024-04-20');");
    console.log(a)
    let b=await db.select("select * from book")
    console.log(b)
    db.close()
}

所有的通信函数都在这里。

使用load方法和execuct方法,需要配置权限

rust 复制代码
    "sql:default",
    "sql:allow-execute"

通信函数load

rust 复制代码
#[command]
pub(crate) async fn load<R: Runtime>(
    app: AppHandle<R>,
    db_instances: State<'_, DbInstances>,
    migrations: State<'_, Migrations>,
    db: String,
) -> Result<String, crate::Error> {
    let pool = DbPool::connect(&db, &app).await?;

    if let Some(migrations) = migrations.0.lock().await.remove(&db) {
        let migrator = Migrator::new(migrations).await?;
        pool.migrate(&migrator).await?;
    }

    db_instances.0.write().await.insert(db.clone(), pool);

    Ok(db)
}

先连接,看看DbPool::connect方法

rust 复制代码
 match conn_url
            .split_once(':')
            .ok_or_else(|| crate::Error::InvalidDbUrl(conn_url.to_string()))?
            .0
        {
            #[cfg(feature = "sqlite")]
            "sqlite" => {
                let app_path = _app
                    .path()
                    .app_config_dir()
                    .expect("No App config path was found!");

                create_dir_all(&app_path).expect("Couldn't create app config dir");

                let conn_url = &path_mapper(app_path, conn_url);

                if !Sqlite::database_exists(conn_url).await.unwrap_or(false) {
                    Sqlite::create_database(conn_url).await?;
                }
                Ok(Self::Sqlite(Pool::connect(conn_url).await?))
            }
rust 复制代码
fn path_mapper(mut app_path: std::path::PathBuf, connection_string: &str) -> String {
    app_path.push(
        connection_string
            .split_once(':')
            .expect("Couldn't parse the connection string for DB!")
            .1,
    );

对于sqlite:start.db 可以发现位置其实是在app_config_dir+start.db

笔者是Window系统,位置如下

写相当路径会相对 app_config_dir。

可以写绝对路径,比如

rust 复制代码
const db = await Database.load('sqlite:D:/start/start.db');

当然,需要有表,结果如下

从官网的案例中可以发现

rust 复制代码
const result = await db.execute(
"UPDATE todos SET title = $1, status = $2 WHERE id = $3",
[todos.title, todos.status, todos.id],
);

可以在sql语句中使用占位符。基本操作。

预加载

可以在配置文件中提前配置。不重要

TypeScript 复制代码
  "plugins": {
    "sql": {
      "preload": ["sqlite:start.db"]
    }
  },

总结

这个插件实际上感觉就是对sqlx的封装。也不是很完全的封装,也没有对sql语句的封装。

等用于为前端提供了数据库连接和执行的接口。

相关推荐
rufeii37 分钟前
[极客大挑战 2019]FinalSQL--布尔盲注
sql
技术卷5 小时前
详解力扣高频SQL50题之1084. 销售分析 III【简单】
sql·leetcode·oracle
NPE~8 小时前
基于MySQL实现基础图数据库
数据库·sql·mysql·教程·图数据库·图结构
技术卷8 小时前
详解力扣高频SQL50题之550. 游戏玩法分析 IV【中等】
sql·mysql·leetcode·oracle
样子20188 小时前
Sql注入 之sqlmap使用教程
数据库·sql
技术卷13 小时前
详解力扣高频 SQL 50 题之584. 寻找用户推荐人【入门】
sql·leetcode·oracle
ALLSectorSorft20 小时前
教务管理系统学排课教务系统模块设计
数据库·sql·oracle
笑衬人心。1 天前
后端项目中大量 SQL 执行的性能优化
sql·spring·性能优化
旧时光巷1 天前
SQL基础⑭ | 变量、流程控制与游标篇
数据库·sql·学习·mysql·变量·游标·流程控制
尘土哥1 天前
Mysql 索引下推(Index Condition Pushdown, ICP)详解
sql·mysql