【问题标题】:Implement default trait API in database module by diesel r2d2?通过柴油 r2d2 在数据库模块中实现默认特征 API?
【发布时间】:2022-01-22 22:49:01
【问题描述】:

我对生锈/柴油还很陌生。尝试使用 crate 柴油和 r2d2 实现具有默认特征 API 的数据库模块(避免重复代码工作)。

这是model.rs

use chrono::NaiveDate;
use diesel;
use diesel::Queryable;

#[derive(Queryable)]
pub struct NetWorthModel {
    pub fund_code: String,
    pub date: NaiveDate,
    pub create_time: i64,
    pub update_time: i64,
    pub payload: String,
}

这是database.rs

use diesel::connection::Connection;
use diesel::mysql::MysqlConnection;
use diesel::prelude::*;
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::sqlite::SqliteConnection;
use once_cell::sync::Lazy;
use r2d2::PooledConnection;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use chrono::prelude::NaiveDate;

static MYSQL_POOL_MAP: Lazy<Mutex<HashMap<String, Pool<ConnectionManager<MysqlConnection>>>>> =
    Lazy::new(|| Mutex::new(HashMap::new()));

static SQLITE_POOL_MAP: Lazy<Mutex<HashMap<String, Pool<ConnectionManager<SqliteConnection>>>>> =
    Lazy::new(|| Mutex::new(HashMap::new()));

pub struct MysqlDatabase {
    pool: Pool<ConnectionManager<MysqlConnection>>,
}

pub struct SqliteDatabase {
    pool: Pool<ConnectionManager<SqliteConnection>>,
}

pub trait Database<Conn> {
  fn connection(&self) -> Conn; // MysqlDatabase and SqliteDatabase will ipmlement this api

  fn paged_query(&self, fund_code: &str, start_date: NaiveDate, end_date: NaiveDate) -> Vec<NetWorthModel> {
    use super::schema::tb_net_worth::dsl;
    dsl::tb_net_worth
         .filter(dsl::fund_code.eq(fund_code))
         .filter(dsl::date.ge(start_date))
         .filter(dsl::date.lt(end_date))
         .load::<NetWorthModel>(&self.connection()) // here we use the abstract API
         .unwrap()
  }
}

impl Database<PooledConnection<ConnectionManager<MysqlConnection>>> for MysqlDatabase {
    fn connection(&self) -> PooledConnection<ConnectionManager<MysqlConnection>> {
        self.pool.get().unwrap()
    }
}

impl Database<PooledConnection<ConnectionManager<SqliteConnection>>> for SqliteDatabase {
    fn connection(&self) -> PooledConnection<ConnectionManager<SqliteConnection>> {
        self.pool.get().unwrap()
    }
}

Database trait 将实现所有的 API,它们应该适用于 mysql 和 sqlite。但这里是编译错误:

error[E0277]: the trait bound `Conn: Connection` is not satisfied
   --> src/quant/common/persistence/database.rs:221:45
    |
221 |                 .load::<NetWorthQueryModel>(&self.connection())
    |                  ----                       ^^^^^^^^^^^^^^^^^^ the trait `Connection` is not implemented for `Conn`
    |                  |
    |                  required by a bound introduced by this call
    |
    = note: required because of the requirements on the impl of `LoadQuery<Conn, NetWorthQueryModel>` for `diesel::query_builder::SelectStatement<table, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<And<And<diesel::expression::operators::Eq<columns::fund_code, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, GtEq<columns::date, diesel::expression::bound::Bound<diesel::sql_types::Date, NaiveDate>>>, Lt<columns::date, diesel::expression::bound::Bound<diesel::sql_types::Date, NaiveDate>>>>, diesel::query_builder::order_clause::OrderClause<Asc<columns::date>>, diesel::query_builder::limit_clause::LimitClause<diesel::expression::bound::Bound<diesel::sql_types::BigInt, i64>>, diesel::query_builder::offset_clause::OffsetClause<diesel::expression::bound::Bound<diesel::sql_types::BigInt, i64>>>`
help: consider restricting type parameter `Conn`
    |
147 | pub trait Database<Conn: diesel::Connection> {
    |                        ++++++++++++++++++++

按照编译器的建议,我添加了pub trait Database&lt;Conn: diesel::Connection&gt;,编译器又给了我一个错误:

error[E0277]: the trait bound `NaiveDate: FromSql<diesel::sql_types::Date, <Conn as Connection>::Backend>` is not satisfied
   --> src/quant/common/persistence/database.rs:221:18
    |
221 |                 .load::<NetWorthQueryModel>(&self.connection())
    |                  ^^^^ the trait `FromSql<diesel::sql_types::Date, <Conn as Connection>::Backend>` is not implemented for `NaiveDate`
    |
    = note: required because of the requirements on the impl of `diesel::Queryable<diesel::sql_types::Date, <Conn as Connection>::Backend>` for `NaiveDate`
    = note: 2 redundant requirements hidden
    = note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Text, diesel::sql_types::Date, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), <Conn as Connection>::Backend>` for `NetWorthQueryModel`
    = note: required because of the requirements on the impl of `LoadQuery<Conn, NetWorthQueryModel>` for `diesel::query_builder::SelectStatement<table, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<And<And<diesel::expression::operators::Eq<columns::fund_code, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, GtEq<columns::date, diesel::expression::bound::Bound<diesel::sql_types::Date, NaiveDate>>>, Lt<columns::date, diesel::expression::bound::Bound<diesel::sql_types::Date, NaiveDate>>>>, diesel::query_builder::order_clause::OrderClause<Asc<columns::date>>, diesel::query_builder::limit_clause::LimitClause<diesel::expression::bound::Bound<diesel::sql_types::BigInt, i64>>, diesel::query_builder::offset_clause::OffsetClause<diesel::expression::bound::Bound<diesel::sql_types::BigInt, i64>>>`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
    |
147 | pub trait Database<Conn: Connection> where NaiveDate: FromSql<diesel::sql_types::Date, <Conn as Connection>::Backend> {
    |                                      ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

似乎 rustc 无法检测到 ConnectionBackend。但我不知道我应该如何解决这个问题。

请帮我解决这个问题。

【问题讨论】:

    标签: generics rust traits rust-diesel r2d2


    【解决方案1】:

    解决此问题的正确方法是遵循编译器错误消息并引入必要的特征界限。没有它们就行不通。作为一般建议:尝试使用required because of 给出的最后一个边界,因为这样可以避免许多其他边界。

    一般来说:如果您不熟悉柴油并且生锈,那么尝试编写尝试抽象柴油的通用代码可能是不可取的。这需要提前了解 rusts 特征系统 + 柴油 API。

    【讨论】:

    • 在这个文档中:docs.rs/diesel/1.2.2/diesel/sql_types/struct.Date.html,说柴油已经为 chrono::NaiveDate 实现了 FromSql 和 ToSql 特征,我已经为柴油启用了“chrono”功能。它应该工作,不是吗?
    • 不,那不是真的。如您所见here impls 是为具体后端编写的(== 没有通用参数DB)。您的代码要求此 impl 存在于任何类型,但未实现。
    • 好的,我会尝试使用宏为两个不同的数据库生成相同的api。
    猜你喜欢
    • 2022-01-05
    • 2022-01-15
    • 2021-11-02
    • 2020-08-29
    • 2022-01-20
    • 2020-02-06
    • 2022-01-19
    • 1970-01-01
    • 2017-09-13
    相关资源
    最近更新 更多