Rust数据库ORM框架SeaORM代码生成器sea-orm-codegen的使用,自动化生成实体模型与数据库交互代码

SeaORM Codegen 使用指南

安装

在项目目录中运行以下Cargo命令:

cargo add sea-orm-codegen

或者在你的Cargo.toml中添加以下行:

sea-orm-codegen = "1.1.14"

基本使用

sea-orm-codegen是SeaORM框架的代码生成器,可以自动从数据库结构生成Rust实体模型和交互代码。以下是完整示例:

use sea_orm_codegen::{DateTimeCrate, OutputFile};
use std::fs;

// 配置代码生成器
let output = sea_orm_codegen::generate_entity(
    "postgres://user:pass@localhost:5432/database", // 数据库连接URL
    "public",                                       // 数据库schema
    OutputFile {
        format: true,                               // 是否格式化生成的代码
        with_serde: "derive",                       // 是否包含serde支持
        with_copy_enums: true,                      // 是否生成可Copy的枚举
        date_time_crate: DateTimeCrate::Chrono,     // 使用的时间库(Chrono/Time)
        expanded_format: false,                     // 是否使用扩展格式
    },
    None,                                           // 表过滤条件
).unwrap();

// 将生成的代码写入文件
fs::write("src/entity.rs", output).unwrap();

生成代码示例

生成的代码会包含实体定义和相关操作,例如:

// 这是自动生成的实体模型示例
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "users")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub name: String,
    pub email: String,
    #[sea_orm(column_type = "DateTime")]
    pub created_at: DateTime,
    #[sea_orm(column_type = "DateTime")]
    pub updated_at: DateTime,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(has_many = "super::posts::Entity")]
    Posts,
}

impl Related<super::posts::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Posts.def()
    }
}

高级配置

可以通过配置控制生成代码的多个方面:

let output = sea_orm_codegen::generate_entity(
    "postgres://user:pass@localhost:5432/database",
    "public",
    OutputFile {
        format: true,
        with_serde: "derive",
        with_copy_enums: true,
        date_time_crate: DateTimeCrate::Chrono,
        expanded_format: true,  // 使用扩展格式
    },
    Some(vec!["users".to_string(), "posts".to_string()]), // 只生成指定表的代码
).unwrap();

完整使用示例

以下是一个完整的项目示例,展示如何使用SeaORM Codegen生成实体代码并在应用中使用:

// 1. 首先创建一个新的Rust项目
// cargo new seaorm-example
// cd seaorm-example

// 2. 添加依赖到Cargo.toml
/*
[dependencies]
sea-orm = { version = "0.10", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
sea-orm-codegen = "1.1.14"
tokio = { version = "1.0", features = ["full"] }
dotenv = "0.15"
*/

// 3. 创建代码生成脚本 generate.rs
use sea_orm_codegen::{DateTimeCrate, OutputFile};
use std::fs;

fn main() {
    // 从环境变量读取数据库连接字符串
    dotenv::dotenv().ok();
    let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");

    // 生成实体代码
    let output = sea_orm_codegen::generate_entity(
        &db_url,
        "public",
        OutputFile {
            format: true,
            with_serde: "derive",
            with_copy_enums: true,
            date_time_crate: DateTimeCrate::Chrono,
            expanded_format: false,
        },
        None,
    ).unwrap();

    // 写入文件
    fs::write("src/entity.rs", output).unwrap();
}

// 4. 创建主程序 main.rs
use sea_orm::Database;
use entity::prelude::*;
use tokio;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 连接数据库
    dotenv::dotenv().ok();
    let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    let db = Database::connect(db_url).await?;

    // 查询所有用户
    let users = User::find().all(&db).await?;
    println!("Found {} users", users.len());

    Ok(())
}

// 5. 创建mod.rs
pub mod entity;

项目结构

seaorm-example/
├── Cargo.toml
├── src/
│   ├── main.rs
│   ├── mod.rs
│   └── entity.rs (由generate.rs生成)
└── generate.rs (代码生成脚本)

SeaORM Codegen是SeaQL组织维护的项目,主要维护者为Chris Tsang和Billy Chan。


1 回复

SeaORM代码生成器sea-orm-codegen使用指南

简介

SeaORM是一个异步Rust ORM框架,而sea-orm-codegen是其配套的代码生成工具,可以自动从数据库结构生成Rust实体模型和交互代码,大幅提升开发效率。

安装

首先在Cargo.toml中添加依赖:

[dependencies]
sea-orm = { version = "0.12", features = ["sqlx-postgres", "runtime-async-std-native-tls"] }

[build-dependencies]
sea-orm-codegen = "0.12"

基本使用方法

  1. 创建一个build.rs文件:
use sea_orm_codegen::{EntityTransformer, OutputFile};
use std::{env, fs, path::Path};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest = Path::new(&out_dir).join("entity");
    
    // 数据库连接URL
    let url = "postgres://user:pass@localhost:5432/database";
    
    // 生成实体代码
    let output = EntityTransformer::transform(url)
        .expect("Failed to generate entities");
    
    fs::create_dir_all(&dest).unwrap();
    
    for OutputFile { name, content } in output.files.iter() {
        fs::write(dest.join(name), content).unwrap();
    }
}
  1. 在main.rs或lib.rs中引入生成的实体:
mod entity {
    include!(concat!(env!("OUT_DIR"), "/entity/mod.rs"));
}

use entity::*;

配置选项

可以通过EntityTransformer的配置方法自定义生成行为:

let output = EntityTransformer::transform(url)
    .with_table_suffix("Table")  // 表名后缀
    .with_derive("Serialize, Deserialize")  // 添加额外derive
    .with_date_time_crate("chrono")  // 指定日期时间库
    .with_lazy_loading(true)  // 启用延迟加载
    .expect("Failed to generate entities");

示例输出

生成的实体代码示例(假设有users表):

// entity/users.rs
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "users")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub name: String,
    pub email: String,
    #[sea_orm(column_type = "DateTime")]
    pub created_at: DateTime,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

使用生成的实体

use entity::users::Entity as Users;

// 查询所有用户
let users = Users::find().all(db).await?;

// 创建新用户
let new_user = users::ActiveModel {
    name: Set("John Doe".to_owned()),
    email: Set("john@example.com".to_owned()),
    ..Default::default()
};
new_user.insert(db).await?;

// 条件查询
let user = Users::find()
    .filter(users::Column::Email.eq("john@example.com"))
    .one(db)
    .await?;

高级功能

  1. 生成枚举类型(如果数据库有枚举列):
#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "user_role")]
pub enum Role {
    #[sea_orm(string_value = "admin")]
    Admin,
    #[sea_orm(string_value = "user")]
    User,
}
  1. 自定义表名映射:
#[derive(Debug, Clone, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "tbl_user")]  // 映射到数据库中的tbl_user表
pub struct Model {
    // 字段定义
}

完整示例

下面是一个完整的项目结构和使用示例:

  1. 项目结构:
my_project/
├── Cargo.toml
├── build.rs
├── src/
│   ├── main.rs
│   └── ...
└── migrations/  # 数据库迁移文件(可选)
  1. Cargo.toml配置:
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"

[dependencies]
sea-orm = { version = "0.12", features = ["sqlx-postgres", "runtime-async-std-native-tls"] }
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }

[build-dependencies]
sea-orm-codegen = "0.12"
  1. build.rs完整示例:
use sea_orm_codegen::{EntityTransformer, OutputFile};
use std::{env, fs, path::Path};

fn main() -> std::io::Result<()> {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest = Path::new(&out_dir).join("entity");
    
    // 数据库连接URL - 请替换为实际的数据库连接信息
    let url = "postgres://postgres:password@localhost:5432/my_database";
    
    // 生成实体代码并配置选项
    let output = EntityTransformer::transform(url)
        .with_derive("Serialize, Deserialize")
        .with_date_time_crate("chrono")
        .expect("Failed to generate entities");
    
    // 创建输出目录
    fs::create_dir_all(&dest)?;
    
    // 写入生成的文件
    for OutputFile { name, content } in output.files.iter() {
        fs::write(dest.join(name), content)?;
    }
    
    Ok(())
}
  1. main.rs完整示例:
mod entity {
    include!(concat!(env!("OUT_DIR"), "/entity/mod.rs"));
}

use entity::*;
use sea_orm::{Database, DatabaseConnection, EntityTrait, ActiveModelTrait, ColumnTrait, QueryFilter};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 建立数据库连接
    let db: DatabaseConnection = Database::connect("postgres://postgres:password@localhost:5432/my_database").await?;
    
    // 使用生成的实体
    let users = entity::users::Entity::find().all(&db).await?;
    println!("Found {} users", users.len());
    
    // 创建新用户
    let new_user = entity::users::ActiveModel {
        name: sea_orm::ActiveValue::Set("Alice".to_owned()),
        email: sea_orm::ActiveValue::Set("alice@example.com".to_owned()),
        ..Default::default()
    };
    let insert_result = new_user.insert(&db).await?;
    println!("Created user with ID: {}", insert_result.id);
    
    // 条件查询
    let user = entity::users::Entity::find()
        .filter(entity::users::Column::Email.eq("alice@example.com"))
        .one(&db)
        .await?;
    
    if let Some(user) = user {
        println!("Found user: {} - {}", user.name, user.email);
    }
    
    Ok(())
}

注意事项

  1. 确保数据库连接URL正确且有足够权限
  2. 生成代码会在每次编译时执行,适合开发环境
  3. 生产环境建议将生成的代码提交到版本控制
  4. 对数据库结构的修改需要重新生成代码

通过sea-orm-codegen可以大幅减少样板代码编写,让开发者更专注于业务逻辑实现。

回到顶部