You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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=&quot;AccountId&quot;)]
    pub account_id: Uuid,

    //#[diesel(deserialize_as=&quot;AccountNumber&quot;)]
    pub account_number: String,

    //#[diesel(deserialize_as=&quot;LedgerBalance&quot;)]
    pub ledger_balance: f64,

    //#[diesel(deserialize_as=&quot;AvailableBalance&quot;)]
    pub available_balance: f64,

    //#[diesel(deserialize_as=&quot;WithdrawalBalance&quot;)]
    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兼容的类型:

  1. 先给Diesel依赖添加numeric特性:
    diesel = { version = "2.0.0", features = ["postgres", "chrono", "serde_json", "uuid", "r2d2", "numeric"] }
    
  2. 两种处理方式:
    • 方式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
      }
      

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:54:57