使用Diesel ORM映射Postgres视图时遇FromSqlRow trait绑定错误
问题描述
尝试用Diesel ORM从Postgres现有数据库查询视图,定义了映射结构体、添加了schema,但运行cargo check时出现以下错误:
error[E0277]: the trait bound `WithdrawBalanceVw: FromSqlRow<Untyped, Pg>` is not satisfied --> src/data/database_query_repository.rs:18:25 | 18 | .get_result(&mut connection)?; | ---------- ^^^^^^^^^^^^^^^ the trait `FromSqlRow<Untyped, Pg>` is not implemented for `WithdrawBalanceVw` | | | required by a bound introduced by this call | = help: the following other types implement trait `FromSqlRow<ST, DB>`: <(T1, T0) as FromSqlRow<(ST1, Untyped), __DB>> <(T1, T2, T0) as FromSqlRow<(ST1, ST2, Untyped), __DB>> <(T1, T2, T3, T0) as FromSqlRow<(ST1, ST2, ST3, Untyped), __DB>> <(T1, T2, T3, T4, T0) as FromSqlRow<(ST1, ST2, ST3, ST4, Untyped), __DB>> <(T1, T2, T3, T4, T5, T0) as FromSqlRow<(ST1, ST2, ST3, ST4, ST5, Untyped), __DB>> <(T1, T2, T3, T4, T5, T6, T0) as FromSqlRow<(ST1, ST2, ST3, ST4, ST5, ST6, Untyped), __DB>> <(T1, T2, T3, T4, T5, T6, T7, T0) as FromSqlRow<(ST1, ST2, ST3, ST4, ST5, ST6, ST7, Untyped), __DB>> <(T1, T2, T3, T4, T5, T6, T7, T8, T0) as FromSqlRow<(ST1, ST2, ST3, ST4, ST5, ST6, ST7, ST8, Untyped), __DB>> and 23 others = note: required for `Untyped` to implement `load_dsl::private::CompatibleType<WithdrawBalanceVw, Pg>` = note: required for `query_builder::sql_query::UncheckedBind<SqlQuery, uuid::Uuid, diesel::sql_types::Uuid>` to implement `LoadQuery<'_, PooledConnection<ConnectionManager<PgConnection>>, WithdrawBalanceVw>` note: required by a bound in `get_result` --> /Users/xxx/.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-2.1.0/src/query_dsl/mod.rs:1723:15 | 1723 | Self: LoadQuery<'query, Conn, U>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_result`
相关代码如下:
映射结构体
#[derive(Debug, Serialize, Deserialize, Queryable)] #[diesel(table_name=WithdrawBalanceVw)] pub struct WithdrawBalanceVw { //#[diesel(deserialize_as="AccountId")] pub account_id: Uuid, //#[diesel(deserialize_as="AccountNumber")] pub account_number: String, //#[diesel(deserialize_as="LedgerBalance")] pub ledger_balance: f64, //#[diesel(deserialize_as="AvailableBalance")] pub available_balance: f64, //#[diesel(deserialize_as="WithdrawalBalance")] pub withdrawal_balance: f64 }
schema.rs中的表定义
diesel::table! { WithdrawBalanceVw { AccountId -> Uuid, AccountNumber -> Varchar, LedgerBalance -> Numeric, AvailableBalance -> Numeric, WithdrawableBalance -> Numeric } } diesel::allow_tables_to_appear_in_same_query!( ... WithdrawBalanceVw, );
查询代码
let mut connection: diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<PgConnection>> = get_connection()?; let query = "select * from public.\"WithdrawBalance_vw\" where \"AccountId\" = ? limit 1"; let response = diesel::sql_query(query); let response = response.bind::<Uuid, _>(account_id) .get_result(&mut connection)?;
依赖配置
... diesel = { version = "2.0.0", features = ["postgres", "chrono", "serde_json", "uuid", "r2d2"] } lazy_static = "1.4.0" serde = { version = "1.0.137", features = ["serde_derive", "derive"] } serde_json = "1.0.103" uuid = { version = "1.4.1", features = ["v4", "serde"] }
解决方案
1. 替换无类型查询为Diesel类型化查询
sql_query属于无类型查询,需要结构体实现QueryableByName trait,而当前只derive了Queryable(适用于类型化查询)。优先用Diesel类型化查询,避免手动写SQL:
use schema::WithdrawBalanceVw; use diesel::prelude::*; // ... let response = WithdrawBalanceVw::table .filter(WithdrawBalanceVw::AccountId.eq(account_id)) .limit(1) .get_result::<WithdrawBalanceVw>(&mut connection)?;
2. 修复字段名不匹配问题
schema中定义的是WithdrawableBalance,但结构体里是withdrawal_balance,字段名不对应会导致映射失败。二选一修改:
- 修改结构体字段名:
pub withdrawable_balance: f64, - 给结构体字段添加映射属性:
#[diesel(column_name = "WithdrawableBalance")] pub withdrawal_balance: f64
3. 修复类型不匹配问题
Postgres的Numeric类型不能直接映射到Rust的f64,需要用Diesel兼容的类型:
- 先给Diesel依赖添加
numeric特性:diesel = { version = "2.0.0", features = ["postgres", "chrono", "serde_json", "uuid", "r2d2", "numeric"] } - 两种处理方式:
- 方式1:直接用
BigDecimal(推荐,避免精度丢失):use bigdecimal::BigDecimal; // 结构体字段修改为: pub ledger_balance: BigDecimal, pub available_balance: BigDecimal, pub withdrawable_balance: BigDecimal, - 方式2:手动转换为
f64(适合非金融场景):use bigdecimal::BigDecimal; #[derive(Debug, Serialize, Deserialize, Queryable)] #[diesel(table_name=WithdrawBalanceVw)] pub struct WithdrawBalanceVw { pub account_id: Uuid, pub account_number: String, #[diesel(deserialize_as = "BigDecimal")] pub ledger_balance: f64, #[diesel(deserialize_as = "BigDecimal")] pub available_balance: f64, #[diesel(column_name = "WithdrawableBalance", deserialize_as = "BigDecimal")] pub withdrawal_balance: f64 }
- 方式1:直接用
4. 统一视图名称大小写
schema中定义的表名是WithdrawBalanceVw,但查询SQL里是WithdrawBalance_vw,需确保和数据库实际视图名称一致:
- 修改schema中的表定义:
diesel::table! { "WithdrawBalance_vw" { // 用引号包裹实际名称 AccountId -> Uuid, AccountNumber -> Varchar, LedgerBalance -> Numeric, AvailableBalance -> Numeric, WithdrawableBalance -> Numeric } } - 同步修改结构体的表名属性:
#[diesel(table_name = "WithdrawBalance_vw")]
5. (可选)坚持用sql_query的处理方案
如果必须使用手动SQL查询,给结构体deriveQueryableByName并明确字段映射:
use bigdecimal::BigDecimal; use diesel::sql_types::{Uuid, Varchar, Numeric}; #[derive(Debug, Serialize, Deserialize, Queryable, QueryableByName)] #[diesel(table_name=WithdrawBalanceVw)] pub struct WithdrawBalanceVw { #[diesel(sql_type = Uuid)] pub account_id: Uuid, #[diesel(sql_type = Varchar)] pub account_number: String, #[diesel(sql_type = Numeric, deserialize_as = BigDecimal)] pub ledger_balance: f64, #[diesel(sql_type = Numeric, deserialize_as = BigDecimal)] pub available_balance: f64, #[diesel(column_name = "WithdrawableBalance", sql_type = Numeric, deserialize_as = BigDecimal)] pub withdrawal_balance: f64 }
总结
优先使用Diesel类型化查询,利用编译器检查避免字段名、类型不匹配问题;处理金融数据时优先用BigDecimal防止精度丢失;确保schema定义与数据库实际视图的名称、字段完全一致。
内容的提问来源于stack exchange,提问作者Cizaphil
相关产品推荐
相关产品推荐

