铁锈:正确使用柴油中的十进制类型

jan*_*w a 6 rust rust-diesel

我正在学习使用dieselorm 库,我的数据库使用DECIMAL(8,2)类型,但是当我Decimal在模型中使用时,出现错误

我正在使用Decimalrust_decimal

diesel = { version="1.4.8", features = ["mysql", "r2d2", "chrono", "numeric"] }

rust_decimal =  { version ="1.23", features = ["serde-with-str", "db-diesel-mysql"] }
rust_decimal_macros = "1.23"
Run Code Online (Sandbox Code Playgroud)

我的 mysql 表

diesel = { version="1.4.8", features = ["mysql", "r2d2", "chrono", "numeric"] }

rust_decimal =  { version ="1.23", features = ["serde-with-str", "db-diesel-mysql"] }
rust_decimal_macros = "1.23"
Run Code Online (Sandbox Code Playgroud)

柴油发电模式

CREATE TABLE `books` ( 
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, 
    `name` VARCHAR(20) NOT NULL , 
    `price` DECIMAL(8,2) UNSIGNED NOT NULL , 
    `user_id` BIGINT UNSIGNED NOT NULL , 
    `type` TINYINT(1) UNSIGNED DEFAULT '1' NOT NULL, 
    `create_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `update_at` DATETIME on update CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    PRIMARY KEY (`id`),
    KEY `user_id` (`user_id`),
    FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB ;
Run Code Online (Sandbox Code Playgroud)
table! {
    books (id) {
        id -> Unsigned<Bigint>,
        name -> Varchar,
        price -> Unsigned<Decimal>,
        user_id -> Unsigned<Bigint>,
        #[sql_name = "type"]
        type_ -> Unsigned<Tinyint>,
        create_at -> Datetime,
        update_at -> Datetime,
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我运行时遇到的错误cargo check

error[E0277]: the trait bound `rust_decimal::Decimal: FromSql<diesel::sql_types::Unsigned<diesel::sql_types::Numeric>, Mysql>` is not satisfied
    --> src/controller/api/book.rs:19:25
     |
19   |         Ok(books::table.load::<models::book::Book>(&conn)?)
     |                         ^^^^ the trait `FromSql<diesel::sql_types::Unsigned<diesel::sql_types::Numeric>, Mysql>` is not implemented for `rust_decimal::Decimal`
     |
     = help: the following implementations were found:
               <rust_decimal::Decimal as FromSql<diesel::sql_types::Numeric, Mysql>>
     = note: required because of the requirements on the impl of `Queryable<diesel::sql_types::Unsigned<diesel::sql_types::Numeric>, Mysql>` for `rust_decimal::Decimal`
     = note: 2 redundant requirements hidden
     = note: required because of the requirements on the impl of `Queryable<(diesel::sql_types::Unsigned<BigInt>, diesel::sql_types::Text, diesel::sql_types::Unsigned<diesel::sql_types::Numeric>, diesel::sql_types::Unsigned<BigInt>, diesel::sql_types::Unsigned<TinyInt>, diesel::sql_types::Datetime, diesel::sql_types::Datetime), Mysql>` for `Book`
     = note: required because of the requirements on the impl of `LoadQuery<_, Book>` for `books::table`
note: required by a bound in `load`
    --> /root/.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-1.4.8/src/query_dsl/mod.rs:1238:15
     |
1238 |         Self: LoadQuery<Conn, U>,
     |               ^^^^^^^^^^^^^^^^^^ required by this bound in `load`

For more information about this error, try `rustc --explain E0277`.
warning: `actix_backend` (bin "server") generated 6 warnings
error: could not compile `actix_backend` due to previous error; 6 warnings emitted
Run Code Online (Sandbox Code Playgroud)

这是我现在使用的 Rust 版本

cargo --version
cargo 1.60.0 (d1fd9fe 2022-03-01)
Run Code Online (Sandbox Code Playgroud)

我也尝试过使用bigdecimal也遇到同样的错误

wei*_*ich 6

根据diesel::sql_types::Unsigned<T>柴油的文档,不提供内置支持Unsigned<Decimal>。(与 example 相比,该页面上没有列出特定的ToSql// impl 。)对于FromSql(也只有一个/ impl for /没有一个 for .AsExpressionUnsigned<Integer>rust_numeric::DecimalFromSqlToSqlNumericDecimalUnsigned<Decimal>

这一切都意味着两个板条箱都不能支撑Unsigned<Decimal>开箱即用的立柱。您可以通过自行实现相应的特征来为此类列提供支持。这意味着为相应的新类型包装器实现FromSql/ ToSql+​​ 派生AsExpression/ 。FromSqlRow

这会产生如下代码:

use diesel::sql_types::{Unsigned, Decimal};
use diesel::serialize::{self, ToSql};
use diesel::deserialize::{self, FromSql};
use diesel::mysql::Mysql;

#[derive(AsExpression, FromSqlRow)]
#[sql_type = "Unsigned<Decimal>"] 
struct DecimalWrapper(rust_decimal::Decimal);


impl FromSql<Unsigned<Decimal>, Mysql> for DecimalWrapper {
    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
        <rust_decimal::Decimal as FromSql<Decimal, Mysql>>::from_sql(bytes).map(Self)
    }
}

impl ToSql<Unsigned<Decimal>, Mysql> for DecimalWrapper {
    fn to_sql<W: Write>(&self, out: &mut serialize::Output<'_, W, DB>) -> serialize::Result {
         <_ as ToSql<Decimal, Mysql>>::to_sql(&self.0, out)
    }
}
Run Code Online (Sandbox Code Playgroud)