目的
1、学习Tauri2
2、学习SeaORM
3、完成前后端交互
4、本地打包,Github工作流打包
想法来源
看到这篇文章
Tauri2+Leptos开发桌面应用--Sqlite数据库操作_tauri sqlite-CSDN博客https://blog.csdn.net/weixin_44274609/article/details/144796615这位大佬写的很好,笔者才发现原来还可以连接数据库,既然如此,写一写前后端。
但是笔者学了一下sqlx,感觉像是JDBC,或者PyMySQL。sqlx不像是ORM框架,还要写sql语句,感觉有点麻烦。
因此,笔者搜了搜,选择SeaORM,虽然没学过,无所谓。
SeaORM的官网如下。
Index | SeaORM 🐚 An async & dynamic ORM for Rusthttps://www.sea-ql.org/SeaORM/docs/index/其他参考
【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程-CSDN博客https://blog.csdn.net/qq_35270805/article/details/135946438Rust语言从入门到精通系列 - SeaORM框架实践(基础篇)SeaORM是一个基于Rust语言的ORM(对象关系映射 - 掘金
https://juejin.cn/post/7239502215889879077
正文
建立Tauri2项目
pnpm create tauri-app
结果如下

进入项目,修改package.json文件中的script内容
javascript
"scripts": {
"dev": "vite",
"build": "vite build",
"tauri:build": "tauri build",
"tauri:dev": "tauri dev",
"android": "tauri android dev"
}
启动命令
pnpm run tauri
经过一段时间的编译,结果如下

修改前端项目------配置路由
安装react-router
rust
pnpm add react-router-dom
路由如下
rust
import Root from "../pages/Root.jsx";
import Home from "../pages/Home.jsx";
import Book from "../pages/Book.jsx";
const routes=[
{
path:'/',
element:<Root/>,
children:[
{
index:true,
element:<Home/>
},
{
path:'book',
element:<Book/>
}
]
}
]
export default routes;
组件的内容笔者使用Curor生成。
创建表
不搞那么复杂,就创建一张表book。sql语句如下。
sql
-- 创建书
CREATE TABLE books (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(100) NOT NULL,
author VARCHAR(100) NOT NULL,
price DECIMAL(10, 2) NOT NULL
);
尝试单独使用SeaORM
使用RustRover或者Cargo new创建一个测试项目。
配置依赖
在Cargo.toml文件中,引入依赖
sql
[package]
name = "rust-sql" # 项目名称
version = "0.1.0" # 版本号
edition = "2024" # Rust 2024
[dependencies] # 依赖项
# sea-orm是一个Rust的ORM库,支持多种数据库,features参数指定了使用的mysql数据库和tokio的runtime
sea-orm={version = "1.1.7",features = ["runtime-tokio-rustls","sqlx-mysql","rust_decimal"]}
# dotenv是一个Rust的库,用于读取.env文件
dotenv = "0.15.0"
# rust_decimal是一个Rust的库,用于处理十进制数
rust_decimal = "1.36.0"
# tokio是一个Rust的异步运行时库
tokio = {version = "1.44.1",features = ["full"]}
在Rust里面,crate 是指包或库,features中选择使用那些**crate,**没有features,则使用默认
在配置sea-orm时,使用了runtime-tokio-rustls、sqlx-mysql、rust_decimal三个crate。
安装sea-orm-cli
sql
cargo install sea-orm-cli
sea-orm-cli是个命令行工具,就像Django的命令一样。
进行数据迁移
命令如下
sql
sea-orm-cli generate entity -u mysql://root:[email protected]:3306/store -o src/entity
这个命令实现sql表转换为Rust的实体,就相当Django的model。
在src/entity目录下就会生成实体代码。当然,也可以自己写。
查看Rust实体代码
目录文件如下

数据库里面有多张表,关注book.rs
mod.rs相当于Python的__init__.py文件。
进入book.rs中
sql
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.7
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "books")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub title: String,
pub author: String,
#[sea_orm(column_type = "Decimal(Some((10, 2)))")]
pub price: Decimal,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
use------简单来说,导包,类似于import
#[derive()] 是属性宏,简单来说,增加新的属性。Clone------能否复制的属性,Debug能够调试的属性,其他属性类似,可以问问AI
pub------public
impl ActiveModelBehavior for ActiveModel {}------ActiveModelBehavior 是一个trait,而triat是可以共享的,简单的说,ActiveModel是结构体,实现了ActiveModelBehavior,很明显,通过{}可以猜测ActiveModelBehavior
中定义了默认方法,直接使用默认方法。
写一个方法------获得所有的书
rust
use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, DbErr, EntityTrait,ActiveValue};
use dotenv::dotenv;
use std::env;
use rust_decimal::Decimal;
pub mod entity;
use entity::books::Entity as BooksDao;
use entity::books::Model as BooksModel;
use entity::books::ActiveModel as BooksActiveModel;
#[tokio::main]
async fn main() {
// 加载环境变量
dotenv().ok();
// 从环境变量中获取数据库连接字符串
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
// 连接数据库
let db = Database::connect(database_url).await.unwrap();
// 调用 get_books 函数获取书籍数据
match get_books(&db).await {
Ok(books) => {
println!("Books: {:?}", books);
}
Err(err) => {
eprintln!("Error fetching books: {:?}", err);
}
}
}
// 异步函数,用于获取所有书籍
async fn get_books(db: &DatabaseConnection) -> Result<Vec<BooksModel>, DbErr> {
BooksDao::find().all(db).await
}
在根目录下新建一个.env文件。里面写连接数据库的URL
&DatabaseConnection:对DatabaseConnection的不可变引用
Result<Vec<BooksModel>, DbErr>:成功Vec<BooksModel>,失败返回DbErr
运行,结果如下。

SeaORM和Tauri2联合使用
其实笔者刚开始不知道怎么使用,但是看到github上的大佬写的模板。
总体使用流程
1、实体模型在一个项目,比如entity项目
2、需要使用的方法,在另一个项目中service中,
3、entity和service作为tauri项目的依赖。
entity项目

每个Cargo项目都有Cargo.toml文件,
src/lib.rs:整个库的入口点,可以定义哪些模块、函数、结构体等是公开的。
因此,内容如下
rust
pub mod books;
entity的Cargo.toml的内容如下。
rust
[package]
name = "entity"
version = "0.1.0"
edition = "2024"
publish = false
[lib]
name = "entity"
path = "src/lib.rs"
[dependencies]
# serde 是一个Rust的序列化和反序列化库
serde = { version = "1", features = ["derive"] }
sea-orm = "1.1.7"
需要book实现序列化和反序列化。
需要在属性宏上加上Serialize、Deserialize。即

实现序列化这一点非常关键。
service项目与展示逻辑的实现

主要业务的逻辑代码,暂时先写获得所有书的逻辑
其中Cargo.toml文件的内容如下
rust
[package]
name = "service"
version = "0.1.0"
edition = "2024"
[dependencies]
# 导入entity模块
entity = { path = "../entity" }
[dependencies.sea-orm]
version = "1.1.7" #
features = [
"sqlx-mysql",
"runtime-tokio-native-tls",
]
[dev-dependencies]
tokio = { version = "1.44.1", features = ["full"] }
在service/src/book_query.rc的内容,与获得所有书的逻辑相似
rust
use entity::books::Model as BookModel;
use entity::books::Entity as BookDao;
use sea_orm::{DatabaseConnection, EntityTrait,DbErr};
// 声明一个结构体
pub struct BookQuery;
// 为结构体实现方法
impl BookQuery{
pub async fn get_list(db:&DatabaseConnection)->Result<Vec<BookModel>,DbErr>{
BookDao::find().all(db).await
}
}
tauri项目------启动数据库,准备通信
Cargo.toml文件的内容
rust
[package]
name = "info"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
edition = "2024"
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tokio = {version = "1.44.1", features = ["full"] }
tauri = { version = "2", features = [] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri-plugin-opener = "2"
service= { path = "./service" } # 导入service模块
entity= { path ="./entity" } # 导入entity模块
[workspace]
# 项目的成员
members = [".", "service", "entity"]
整体项目结构

经过前面这么多布局,可以写最关键的启动文件了
rust
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::State;
use entity::books::Model as BooksModel;
use service::BookQuery;
use dotenv::dotenv;
use std::env;
use service::{
sea_orm::{DatabaseConnection,Database}
};
// 定义一个异步函数,用于获取书籍数据
#[tauri::command]
async fn get_books(state: State<'_,AppState>)->Result<Vec<BooksModel>,String>{
BookQuery::get_list(&state.connect)
.await
.map_err(|e| e.to_string())
}
// 定义一个结构体,用于存储应用程序的状态
#[derive(Clone)]
struct AppState{
connect:DatabaseConnection,
}
// 启动程序
#[tokio::main]
async fn main() {
dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("需要设置DATABASE_URL ");
// 连接数据库
let connect=Database::connect(database_url)
.await
.expect("连接失败");
let state=AppState{
connect
};
tauri::Builder::default()
.manage(state)// 全局状态管理
.invoke_handler(tauri::generate_handler![
get_books
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
这里有个细节。
来源与deepseek的回答
Result<T, E>
的序列化要求
T
:成功时的返回值类型,必须实现Serialize
。
E
:错误时的返回值类型,必须实现Serialize
和std::fmt::Debug
需要实现序列化,前面在entity中实现了。
前端发送信号------get_books
关键代码
rust
import {useEffect, useState} from 'react';
import {invoke} from '@tauri-apps/api/core'
const [books, setBooks] = useState([])
function get_books(){
invoke('get_books').then((books) => {
setBooks(books);
});
}
useEffect(() => {
get_books()
}, []);
invoke作为交互的关键函数,第一个参数是拥有**#[tauri::command]**的需要使用函数名,异步。
运行
rust
pnpm run tauri
结果如下

项目终于建立完成,成功。哈哈哈哈哈。
数据和页面都是AI生成的。
本来想把CRUD全部写出来的,都是重复操作,懒得写。
本地打包
完成CURD之后。
打包后,双击没有运行,后来发现是没有.env文件。
修改rust的代码,或者把.env文件放到安装目录下。可以运行
结果如下

github工作流打包
.github/workflows/build.yaml
name: 打包tauri
on:
push:
tags:
- "v*.*.*"
jobs:
build:
runs-on: windows-latest
steps:
- name: 读取仓库
uses: actions/checkout@v4
- name: 设置node
uses: actions/setup-node@v4
with:
node-version: '23'
- name: 安装pnpm
run: npm i -g pnpm
- name: 安装依赖
run: pnpm install
- name: 安装 Rust
uses: dtolnay/rust-toolchain@stable
- name: 缓存 Rust
uses: swatinem/rust-cache@v2
with:
workspaces: './src-tauri -> target'
- name: 打包项目
uses: tauri-apps/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tagName: ${{ github.ref_name }} # This only works if your workflow triggers on new tags.
releaseName: 'TSR v__VERSION__' # tauri-action replaces \_\_VERSION\_\_ with the app version.
releaseBody: 'See the assets to download and install this version'
releaseDraft: false
prerelease: false
publish: true
地址
qe-present/tauri-seaorm-reacthttps://github.com/qe-present/tauri-seaorm-react