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(())
}
代码说明
- 首先定义了
Character
枚举来表示表结构和字段名 - 使用
Table::create()
创建表结构,包含各种字段类型 - 使用
Query::insert()
插入数据 - 使用
Query::select()
查询数据 - 使用
Query::update()
更新数据 - 演示了 PostgreSQL 特有的 JSONB 和数组类型的使用
- 所有操作都通过 SQLx 执行,并使用了 async/await
总结
sea-query-postgres 提供了完整的 PostgreSQL 数据库操作能力,通过这个完整示例可以看到:
- 从表创建到CRUD操作的完整流程
- 类型安全的查询构建方式
- PostgreSQL 特有功能的支持
- 与异步生态的良好集成
这个示例可以作为使用 sea-query-postgres 的起点,根据实际需求进行扩展和修改。