前言
这篇就来看看插件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 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,
}
]
}
创建了一张表
注册
.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 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表
添加依赖
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语句的封装。
等用于为前端提供了数据库连接和执行的接口。