前言
這篇就來看看插件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
pub fn add_migrations(mut self,db_url: &str, migrations: Vec<Migration>) -> Self
需要傳入db_url和migrations
比如db_url可以設置sqlite:start.db
migrations是個Vec,元素類型是Migration
Migration的定義如下
#[derive(Debug)]
pub struct Migration {pub version: i64,pub description: &'static str,pub sql: &'static str,pub kind: MigrationKind,
}
?MigrationKind是指定遷移的類型
#[derive(Debug)]
pub enum MigrationKind {Up,Down,
}
項目結構如下?
在migration.rs中
use tauri_plugin_sql::{Migration,MigrationKind};
pub fn get_migration()->Vec<Migration>{vec![// Define your migrations hereMigration {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,}]
}
?創建了一張表
注冊
.plugin(tauri_plugin_sql::Builder::default().add_migrations("sqlite:start.db",get_migration()).build())
?想要在后端使用
怎么在后端使用,這確實是個問題,筆者發現好像沒有在后端使用的東西,全都是前端調用
比如說
pub(crate) async fn connect<R: Runtime>(conn_url: &str,_app: &AppHandle<R>,) -> Result<Self, crate::Error>
按道理來說,connect應該可以使用,但是這是私有的,只在crate內部公開,
筆者也沒找到怎么使用,
看看內部的通信函數
#[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)
}
impl DbPool {pub(crate) async fn connectpub(crate) async fn migratepub(crate) async fn close(&self) pub(crate) async fn execute}
可以看出使用了DbInstances和Migrations,兩個State
要先連接?DbPool::connect,但是這個connect沒公開,其他方法也沒有公開
看來這個插件就是寫在前端的。后端無法使用。
要么使用sqlx,要么修改源碼。額,都很麻煩。
筆者不在后端使用這個插件。直接使用sqlx
sqlx
暫時丟掉插件,簡單使用一下這個crate
創建一個數據庫和book表
添加依賴
sqlx = { version = "0.8.5", features = ["sqlite", "runtime-tokio"] }
?
在connect.rs中,定義DbInstances ,然后將其注冊成State
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}}
}
通信函數?
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
既然這個插件是為前端服務的,就在前端簡單使用一下。
添加依賴
pnpm add @tauri-apps/plugin-sql
后端注冊完成后,簡單的代碼如下?
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方法,需要配置權限
"sql:default","sql:allow-execute"
?通信函數load
#[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方法
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?))}
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。
可以寫絕對路徑,比如
const db = await Database.load('sqlite:D:/start/start.db');
當然,需要有表,結果如下
從官網的案例中可以發現
const result = await db.execute(
"UPDATE todos SET title = $1, status = $2 WHERE id = $3",
[todos.title, todos.status, todos.id],
);
?可以在sql語句中使用占位符。基本操作。
預加載
可以在配置文件中提前配置。不重要
"plugins": {"sql": {"preload": ["sqlite:start.db"]}},
總結
這個插件實際上感覺就是對sqlx的封裝。也不是很完全的封裝,也沒有對sql語句的封裝。
等用于為前端提供了數據庫連接和執行的接口。