【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程


文章目录

前言

前两篇文件主要降了axum相关使用,这篇文章来讲讲orm相关框架。目前rust orm相关框架不多,比较主流的是sqlx,本文介绍的框架实在此基础上封装的一层,sql-orm同样也支持rust异步。

一、引入依赖

rust 复制代码
sea-orm = { version = "0.12", features = [ <DATABASE_DRIVER>, <ASYNC_RUNTIME>, "macros" ] }  #DATABASE_DRIVER和ASYNC_RUNTIME参数需要替换

DATABASE_DRIVER参数

  • sqlx-mysql-SQLx的MySQL
  • sqlx-postgres-SQLx
  • PostgreSQL的 sqlx-sqlite

ASYNC_RUNTIME参数

  • runtime-async-std-native-tls
  • runtime-tokio-native-tls
  • runtime-async-std-rustls
  • runtime-tokio-rustls
    这里我们选择引入tokio异步支持的,还要引入tokio
rust 复制代码
[dependencies]
sea-orm = { version = "0.12", features = [ "sqlx-mysql", "runtime-tokio-native-tls", "macros" ] }
tokio = { version = "1.35.1", features = ["full"] }

二、创建数据库连接

简单链接

rust 复制代码
let db: DatabaseConnection = Database::connect("protocol://username:password@host/database").await?;

举例子mysql数据库连接

rust 复制代码
   let db: DatabaseConnection = Database::connect("mysql://root:root@127.0.0.1:3307/test").await.unwrap();

后续查询选相关操作每次调用DatabaseConnection ,都会从池中获取和释放连接。

连接别的数据库可以看官方文档https://www.sea-ql.org/SeaORM/docs/next/install-and-config/connection/

连接选项

复制代码
若要配置连接,请使用 ConnectOptions 接口
rust 复制代码
let mut opt = ConnectOptions::new("mysql://root:root@127.0.0.1:3307/test");
opt.max_connections(100)
    .min_connections(5)
    .connect_timeout(Duration::from_secs(8))
    .acquire_timeout(Duration::from_secs(8))
    .idle_timeout(Duration::from_secs(8))
    .max_lifetime(Duration::from_secs(8))
    .sqlx_logging(true)
    .sqlx_logging_level(log::LevelFilter::Info)
    .set_schema_search_path("my_schema"); // Setting default PostgreSQL schema

let db = Database::connect(opt).await?;

可以看ConnectOptions接口文档https://docs.rs/sea-orm/0.12.12/sea_orm/struct.ConnectOptions.html

开启日志调试

开发阶段需要打印相关日志,可以开启调试模式

features当中多一个["debug-print"]

rust 复制代码
[dependencies]
sea-orm = { version = "0.12", features = [ "sqlx-mysql", "runtime-tokio-native-tls", "macros" ,"debug-print","with-chrono"] }
tokio = { version = "1.35.1", features = ["full"] }
chrono = "0.4.33"
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18",features = [
    "env-filter",
    "time",
    "local-time", ]}

然后需要执行一段初始化tracing-subscriber代码

rust 复制代码
    // 设置全局日志级别为 info
    let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"))
        //单独设置sea_orm
        .add_directive("sea_orm::driver=debug".parse().unwrap())
        //关闭sqlx自带的日志
        .add_directive("sqlx::query=off".parse().unwrap());

三、生成实体

安装sea-orm-cli

运行命令

rust 复制代码
cargo install sea-orm-cli

创建数据库表

sql 复制代码
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` datetime DEFAULT NULL COMMENT '生日',
  `sex` char(1) DEFAULT NULL COMMENT '性别',
  `address` varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8mb3

使用sea-orm-cli命令生成实体文件代码

在项目文件夹下运行命令,-o 是输出文件目录。相关参数配置可看文档https://www.sea-ql.org/SeaORM/docs/next/generate-entity/sea-orm-cli/

sql 复制代码
sea-orm-cli generate entity -u mysql://root:root@127.0.0.1:3307/test -o src/entity

在main文件加入entity模块即可。

生成的文件内容

在这里插入图片描述

指定表名

rust 复制代码
#[sea_orm(table_name = "cake", schema_name = "public")]
pub struct Model { ... }

指定列名

rust 复制代码
#[sea_orm(column_name = "name")]
pub name: String

四、增删改查实现

新增数据

复制代码
先了解ActiveValue和ActiveModel
rust 复制代码
use entity::user::ActiveModel as UserModel;
let user: UserModel =  UserModel{
        id: ActiveValue::NotSet,
        username: ActiveValue::Set("你好".to_owned()),
        birthday: ActiveValue::Set(Some(Local::now().naive_local())),
        sex: ActiveValue::Set(Some("1".to_owned())),
        address: ActiveValue::Set(Some("address".to_owned())),
    };

这里我们创建UserModel的ActiveModel模型,里面的值是ActiveValue类型,NotSet是不设置值。

创建ActiveModel方法还有别的,比如通过JSON字符,具体的可以看文档https://www.sea-ql.org/SeaORM/docs/next/basic-crud/insert/#convert-activemodel-back-to-model

然后执行插入方法,具体代码如下

rust 复制代码
use chrono::{ Local};
use sea_orm::{ActiveModelTrait, ActiveValue, Database, DatabaseConnection, IntoActiveModel};

pub mod entity;
use entity::user::Entity as UserDao;
use entity::user::ActiveModel as UserModel;
use entity::user::Model as Model;
#[tokio::main]
async fn main(){
    let db: DatabaseConnection = Database::connect("mysql://root:root@127.0.0.1:3307/test").await.unwrap();
    let user: UserModel =  UserModel{
        id: ActiveValue::NotSet,
        username: ActiveValue::Set("你好".to_owned()),
        birthday: ActiveValue::Set(Some(Local::now().naive_local())),
        sex: ActiveValue::Set(Some("1".to_owned())),
        address: ActiveValue::Set(Some("address".to_owned())),
    };
/*    let user: Model = Model{
        id: 1,
        username: "admin".to_string(),
        birthday: Some(Local::now().naive_local()),
        sex: Some("1".to_owned()),
        address: Some("address".to_owned()),
    };
    let active_model = user.into_active_model();*/
    let result = user.insert(&db).await.unwrap();
    println!("插入成功!:{:?}",result);
}

多个插入可以调用上述代码UserDao中的insert_many方法,传入ActiveModel数组

主键查找

rust 复制代码
use entity::user::Entity as UserDao;
 let option = UserDao::find_by_id(1).one(&db).await.unwrap();
    match option {
        None => {}
        Some(user) => println!("查询成功!:{:?}",user)
    }

条件查找

查找用户名是admin的一条用户

rust 复制代码
use crate::entity::user;
    use entity::user::Entity as UserDao;
    let result = UserDao::find().filter(user::Column::Username.eq("admin")).one(&db).await.unwrap();
    match result {
        None => {}
        Some(user) => println!("查询成功!:{:?}",user)
    }

查找地址是郑州的所有用户

rust 复制代码
    use crate::entity::user;
    use entity::user::Entity as UserDao;
    let result = UserDao::find().filter(user::Column::Address.eq("郑州")).all(&db).await.unwrap();
    println!("查询成功!:{:?}",result)

查找地址是郑州并且用户名包含admin的所有用户

rust 复制代码
    use crate::entity::user;
    use entity::user::Entity as UserDao;
    let result = UserDao::find().filter(
        Condition::all().add(user::Column::Address.eq("郑州")).add(user::Column::Username.like("%admin%"))
    ).all(&db).await.unwrap();
    println!("查询成功!:{:?}",result)

分页查找

rust 复制代码
 
use crate::entity::user;
    use entity::user::Entity as UserDao;
    let mut paginator = UserDao::find().filter(
        Condition::all().add(user::Column::Address.eq("郑州")).add(user::Column::Username.like("%admin%"))
    ).paginate(&db,50);
    //paginate(&db,50)此处第二个参数表示设置单页数量,此方法会返回Paginator对象。
    while let Some(user) = paginator.fetch_and_next().await.unwrap() {
    	//循环从paginate取数据,每次取50条,页数加一,直到没有数据
        println!("查询成功!:{:?}",user)
    }

如果直接获取第几页数据怎么做,下面有方法

rust 复制代码
    use crate::entity::user;
    use entity::user::Entity as UserDao;
    let mut paginator = UserDao::find().filter(
        Condition::all().add(user::Column::Address.eq("郑州")).add(user::Column::Username.like("%admin%"))
    ).paginate(&db,50);
    //此方法可直接取具体页数,注意是从零开始,需要前端页数加一
    let result = paginator.fetch_page(0).await;
    match result{
        Ok(vec_user) => {println!("{:?}", vec_user)}
        Err(_) => {}
    }

修改数据

修改主键为1的用用户名

rust 复制代码
    use entity::user::Entity as UserDao;
    let user = UserDao::find_by_id(1).one(&db).await.unwrap().unwrap();
    let mut active_model = user.into_active_model();
    active_model.username = ActiveValue::Set("修改后的用户名".to_owned());
    active_model.update(&db).await.unwrap();

如果想强制更新某个字段可以调用。

rust 复制代码
active_model.reset(user::Column::Address); //这样更新时字段就会强制带上,可以实现把字段置空

删除数据

很简单

rust 复制代码
    use entity::user::Entity as UserDao;
    let res = UserDao::delete_by_id(1).exec(&db).await.unwrap();

或者还有一种方法

rust 复制代码
    use entity::user::Entity as UserDao;
    let res = UserDao::find_by_id(1).one(&db).await.unwrap().unwrap();
    let active_model = res.into_active_model();
    active_model.delete(&db).await.unwrap();

数据库事务操作

可以手动调用db的begin和commit方法,以下是官方例子

rust 复制代码
let txn = db.begin().await?;

bakery::ActiveModel {
    name: Set("SeaSide Bakery".to_owned()),
    profit_margin: Set(10.4),
    ..Default::default()
}
.save(&txn)
.await?;

bakery::ActiveModel {
    name: Set("Top Bakery".to_owned()),
    profit_margin: Set(15.0),
    ..Default::default()
}
.save(&txn)
.await?;

txn.commit().await?;

总结

以上就是sea-orm入门使用教程,更具体的可以查看sea-orm官方文档https://www.sea-ql.org/SeaORM/docs/index/。后续我可能会再出一篇sea-orm的高级使用教程

相关推荐
蚂蚁背大象37 分钟前
Rust 所有权系统是为了解决什么问题
后端·rust
布列瑟农的星空1 小时前
前端都能看懂的rust入门教程(五)—— 所有权
rust
子玖2 小时前
go实现通过ip解析城市
后端·go
Java不加班2 小时前
Java 后端定时任务实现方案与工程化指南
后端
心在飞扬3 小时前
RAG 进阶检索学习笔记
后端
Moment3 小时前
想要长期陪伴你的助理?先从部署一个 OpenClaw 开始 😍😍😍
前端·后端·github
Das1_3 小时前
【Golang 数据结构】Slice 底层机制
后端·go
得物技术3 小时前
深入剖析Spark UI界面:参数与界面详解|得物技术
大数据·后端·spark
古时的风筝3 小时前
花10 分钟时间,把终端改造成“生产力武器”:Ghostty + Yazi + Lazygit 配置全流程
前端·后端·程序员