Rust数据库查询构建器sea-query-postgres的使用:支持PostgreSQL的高效SQL生成与操作

Rust数据库查询构建器sea-query-postgres的使用:支持PostgreSQL的高效SQL生成与操作

安装

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

cargo add sea-query-postgres

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

sea-query-postgres = "0.5.0"

示例代码

以下是一个使用sea-query-postgres构建PostgreSQL查询的完整示例:

use sea_query::{PostgresQueryBuilder, Query, Iden, Expr};
use std::error::Error;

// 定义一个表结构
#[derive(Iden)]
enum User {
    Table,
    Id,
    Name,
    Email,
}

fn main() -> Result<(), Box<dyn Error>> {
    // 创建查询构建器
    let query = Query::select()
        .columns([User::Id, User::Name, User::Email])
        .from(User::Table)
        .and_where(Expr::col(User::Name).eq("John"))
        .limit(10)
        .offset(0)
        .to_owned();

    // 生成PostgreSQL SQL语句
    let (sql, values) = query.build(PostgresQueryBuilder);
    
    println!("SQL: {}", sql);
    println!("Values: {:?}", values);

    Ok(())
}

更复杂的示例

use sea_query::{PostgresQueryBuilder, Query, Iden, Expr, Order, Func};
use std::error::Error;

// 定义表结构
#[derive(Iden)]
enum Product {
    Table,
    Id,
    Name,
    Price,
    CategoryId,
}

#[derive(Iden)]
enum Category {
    Table,
    Id,
    Name,
}

fn main() -> Result<(), Box<dyn Error>> {
    // 构建复杂查询
    let query = Query::select()
        .columns([
            (Product::Table, Product::Id),
            (Product::Table, Product::Name),
            (Product::Table, Product::Price),
            (Category::Table, Category::Name),
        ])
        .from(Product::Table)
        .left_join(
            Category::Table,
            Expr::col((Product::Table, Product::CategoryId))
                .equals((Category::Table, Category::Id)),
        )
        .and_where(Expr::col(Product::Price).gte(100))
        .and_where(Expr::col(Category::Name).like("Electronics%"))
        .order_by(Product::Price, Order::Desc)
        .order_by(Product::Name, Order::Asc)
        .group_by_col((Category::Table, Category::Id))
        .having(Expr::expr(Foc::count(Expr::col(Product::Id))).gte(5))
        .limit(20)
        .offset(10)
        .to_owned();

    // 生成PostgreSQL SQL语句
    let (sql, values) = query.build(PostgresQueryBuilder);
    
    println!("SQL: {}", sql);
    println!("Values: {:?}", values);

    Ok(())
}

插入数据示例

use sea_query::{PostgresQueryBuilder, Query, Iden, Value};
use std::error::Error;

#[derive(Iden)]
enum User {
    Table,
    Id,
    Name,
    Email,
    Age,
}

fn main() -> Result<(), Box<dyn Error>> {
    // 构建插入查询
    let query = Query::insert()
        .into_table(User::Table)
        .columns([User::Name, User::Email, User::Age])
        .values_panic([
            "John Doe".into(),
            "john@example.com".into(),
            30.into(),
        ])
        .values_panic([
            "Jane Smith".into(),
            "jane@example.com".into(),
            25.into(),
        ])
        .returning_cols([User::Id, User::Name])
        .to_owned();

    // 生成PostgreSQL SQL语句
    let (sql, values) = query.build(PostgresQueryBuilder);
    
    println!("SQL: {}", sql);
    println!("Values: {:?}", values);

    Ok(())
}

更新数据示例

use sea_query::{PostgresQueryBuilder, Query, Iden, Expr};
use std::error::Error;

#[derive(Iden)]
enum User {
    Table,
    Id,
    Name,
    Email,
}

fn main() -> Result<(), Box<dyn Error>> {
    // 构建更新查询
    let query = Query::update()
        .table(User::Table)
        .values([
            (User::Name, "John Smith".into()),
            (User::Email, "john.smith@example.com".into()),
        ])
        .and_where(Expr::col(User::Id).eq(1))
        .to_owned();

    // 生成PostgreSQL SQL语句
    let (sql, values)極query.build(PostgresQueryBuilder);
    
    println!("SQL: {}", sql);
    println!("Values: {:?}", values);

    Ok(())
}

删除数据示例

use sea_query::{PostgresQueryBuilder, Query, Iden, Expr};
use std::error::Error;

#[derive(Iden)]
enum User {
    Table,
    Id,
}

fn main() -> Result<(), Box<dyn Error>> {
    // 构建删除查询
    let query = Query::delete()
        .from_table(User::Table)
        .and_where(Expr::col(User::Id).eq(1))
        .to_owned();

    // 生成PostgreSQL SQL语句
    let (sql, values) = query.build(PostgresQueryBuilder);
    
    println!("SQL: {}", sql);
    println!("Values: {:?}", values);

    Ok(())
}

完整示例(事务处理)

以下是使用sea-query-postgres进行事务处理的完整示例:

use sea_query::{PostgresQueryBuilder, Query, Iden, Expr, Value};
use std::error::Error;
use tokio_postgres::{Client, NoTls};

// 定义表结构
#[derive(Iden)]
enum Account {
    Table,
    Id,
    UserId,
    Balance,
}

#[derive(Iden)]
enum Transaction {
    Table,
    Id,
    AccountId,
    Amount,
    Type,
}

async fn transfer_funds(
    client: &mut Client,
    from_account: i32,
    to_account: i32,
    amount: i64,
) -> Result<(), Box<dyn Error>> {
    // 开始事务
    let transaction = client.transaction().await?;

    // 1. 检查转出账户余额是否足够
    let check_balance_query = Query::select()
        .column(Account::Balance)
        .from(Account::Table)
        .and_where(Expr::col(Account::Id).eq(from_account))
        .to_owned();

    let (check_sql, check_values) = check_balance_query.build(PostgresQueryBuilder);
    let row = transaction.query_one(&check_sql, &check_values).await?;
    let current_balance: i64 = row.get(0);
    
    if current_balance < amount {
        return Err("Insufficient funds".into());
    }

    // 2. 扣除转出账户金额
    let deduct_query = Query::update()
        .table(Account::Table)
        .value(Account::Balance, Expr::col(Account::Balance).sub(amount))
        .and_where(Expr::col(Account::Id).eq(from_account))
        .to_owned();

    let (deduct_sql, deduct_values) = deduct_query.build(PostgresQueryBuilder);
    transaction.execute(&deduct_sql, &deduct_values).await?;

    // 3. 增加转入账户金额
    let add_query = Query::update()
        .table(Account::Table)
        .value(Account::Balance, Expr::col(Account::Balance).add(amount))
        .and_where(Expr::col(Account::Id).eq(to_account))
        .to_owned();

    let (add_sql, add_values) = add_query.build(PostgresQueryBuilder);
    transaction.execute(&add_sql, &add_values).await?;

    // 4. 记录交易
    let record_query = Query::insert()
        .into_table(Transaction::Table)
        .columns([
            Transaction::AccountId,
            Transaction::Amount,
            Transaction::Type,
        ])
        .values_panic([
            from_account.into(),
            amount.into(),
            "DEBIT".into(),
        ])
        .values_panic([
            to_account.into(),
            amount.into(),
            "CREDIT".into(),
        ])
        .to_owned();

    let (record_sql, record_values) = record_query.build(PostgresQueryBuilder);
    transaction.execute(&record_sql, &record_values).await?;

    // 提交事务
    transaction.commit().await?;

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let (mut client, connection) = tokio_postgres::connect(
        "host=localhost user=postgres dbname=test",
        NoTls,
    ).await?;

    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("Connection error: {}", e);
        }
    });

    // 转账示例
    transfer_funds(&mut client, 1, 2, 100).await?;

    Ok(())
}

这些示例展示了如何使用sea-query-postgres构建各种类型的PostgreSQL查询。该库提供了类型安全的查询构建方式,支持复杂的SQL操作,同时保持了代码的可读性和可维护性。


1 回复

Rust数据库查询构建器sea-query-postgres的使用:支持PostgreSQL的高效SQL生成与操作

简介

sea-query-postgres 是 SeaQuery 生态系统中专门为 PostgreSQL 设计的查询构建器,它提供了类型安全、符合人体工程学的 API 来构建和执行 SQL 查询。作为 SeaORM 的基础组件之一,它也可以独立使用。

主要特点:

  • 完全类型安全的查询构建
  • 支持 PostgreSQL 特有功能
  • 防止 SQL 注入
  • 可组合的查询结构
  • 与 async/await 兼容

完整示例

下面是一个完整的示例,展示如何使用 sea-query-postgres 进行数据库操作:

use sea_query::{*, postgres::Postgres};
use sea_query::Iden;
use sqlx::postgres::PgPoolOptions;

// 定义表结构
#[derive(Iden)]
enum Character {
    Table,
    Id,
    Character,
    FontSize,
    SizeW,
    SizeH,
    FontId,
    Metadata,
    Tags
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    // 1. 创建数据库连接池
    let pool = PgPoolOptions::new()
        .connect("postgres://user:pass@localhost/db")
        .await?;

    // 2. 创建表
    let create_table = Table::create()
        .table(Character::Table)
        .if_not_exists()
        .col(
            ColumnDef::new(Character::Id)
                .integer()
                .not_null()
                .auto_increment()
                .primary_key(),
        )
        .col(ColumnDef::new(Character::FontSize).integer())
        .col(ColumnDef::new(Character::Character).string())
        .col(ColumnDef::new(Character::SizeW).integer())
        .col(ColumnDef::new(Character::SizeH).integer())
        .col(ColumnDef::new(Character::FontId).integer())
        .col(ColumnDef::new(Character::Metadata).json_binary())
        .col(ColumnDef::new(Character::Tags).array(sea_query::ArrayType::String))
        .to_owned();

    let sql = create_table.to_string(Postgres);
    println!("创建表SQL: {}", sql);
    
    // 执行创建表
    sqlx::query(&sql).execute(&pool).await?;

    // 3. 插入数据
    let insert_query = Query::insert()
        .into_table(Character::Table)
        .columns([
            Character::Character,
            Character::FontSize,
            Character::SizeW,
            Character::SizeH,
        ])
        .values_panic([
            "A".into(),
            12.into(),
            5.into(),
            10.into(),
        ])
        .to_owned();

    let (insert_sql, insert_values) = insert_query.build_any(&PostgresQueryBuilder);
    sqlx::query_with(&insert_sql, insert_values)
        .execute(&pool)
        .await?;

    // 4. 查询数据
    let select_query = Query::select()
        .columns([Character::Id, Character::Character])
        .from(Character::Table)
        .limit(10)
        .to_owned();

    let (select_sql, select_values) = select_query.build_any(&PostgresQueryBuilder);
    let rows = sqlx::query_with(&select_sql, select_values)
        .fetch_all(&pool)
        .await?;

    for row in rows {
        let id: i32 = row.get("id");
        let character: String = row.get("character");
        println!("查询结果 - id: {}, character: {}", id, character);
    }

    // 5. 更新数据
    let update_query = Query::update()
        .table(Character::Table)
        .values([
            (Character::FontSize, 24.into()),
            (Character::SizeW, 10.into()),
            (Character::SizeH, 20.into()),
        ])
        .and_where(Expr::col(Character::Id).eq(1))
        .to_owned();

    let (update_sql, update_values) = update_query.build_any(&PostgresQueryBuilder);
    sqlx::query_with(&update_sql, update_values)
        .execute(&pool)
        .await?;

    // 6. 使用PostgreSQL特有功能(JSONB和数组)
    let pg_special_query = Query::insert()
        .into_table(Character::Table)
        .columns([Character::Metadata, Character::Tags])
        .values_panic([
            Value::from(serde_json::json!({"key": "value"}).to_string()),
            Value::from(ArrayType::String(vec!["tag1".into(), "tag2".into()]))
        ])
        .to_owned();

    let (pg_special_sql, pg_special_values) = pg_special_query.build_any(&PostgresQueryBuilder);
    sqlx::query_with(&pg_special_sql, pg_special_values)
        .execute(&pool)
        .await?;

    Ok(())
}

代码说明

  1. 首先定义了 Character 枚举来表示表结构和字段名
  2. 使用 Table::create() 创建表结构,包含各种字段类型
  3. 使用 Query::insert() 插入数据
  4. 使用 Query::select() 查询数据
  5. 使用 Query::update() 更新数据
  6. 演示了 PostgreSQL 特有的 JSONB 和数组类型的使用
  7. 所有操作都通过 SQLx 执行,并使用了 async/await

总结

sea-query-postgres 提供了完整的 PostgreSQL 数据库操作能力,通过这个完整示例可以看到:

  1. 从表创建到CRUD操作的完整流程
  2. 类型安全的查询构建方式
  3. PostgreSQL 特有功能的支持
  4. 与异步生态的良好集成

这个示例可以作为使用 sea-query-postgres 的起点,根据实际需求进行扩展和修改。

回到顶部