如何为具有自定义字段的类型派生Queryable,该自定义字段映射到柴油的多个列?

问题描述

我正在使用Diesel crate来执行一些数据库工作。在某些表中,表的两列应一起视为一个键。

数据库的许多地方都重复了这种模式,因此最好避免使用大量重复的复制粘贴代码来处理此模式。但是,我不能说服Diesel自动产生可在查询或插入中使用的类型。

考虑桌子

table! {
    records (iid) {
        iid -> Integer,id_0 -> BigInt,id_1 -> BigInt,data -> Text,}
}

和理想类型

#[derive(Debug,copy,Clone,FromsqlRow)]
pub struct RecordId {
    id_0: i64,id_1: i64,}

#[derive(Queryable,Debug)]
pub struct Record {
    pub iid: i32,pub id: RecordId,pub data: String,}

代码编译正常,但是当我尝试使用它时出现错误,例如:

pub fn find(connection: &sqliteConnection) -> types::Record {
    records
        .find(1)
        .get_result::<types::Record>(connection)
        .unwrap()
}

产生:

error[E0277]: the trait bound `(i32,types::RecordId,std::string::String): diesel::Queryable<(diesel::sql_types::Integer,diesel::sql_types::BigInt,diesel::sql_types::Text),_>` is not satisfied
  --> src/main.rs:76:21
   |
76 |     records.find(1).get_result::<types::Record>(connection).unwrap()
   |                     ^^^^^^^^^^ the trait `diesel::Queryable<(diesel::sql_types::Integer,_>` is not implemented for `(i32,std::string::String)`
   |
   = help: the following implementations were found:
             <(A,B,C) as diesel::Queryable<(SA,SB,SC),__DB>>
             <(A,C) as diesel::Queryable<diesel::sql_types::Record<(SA,SC)>,diesel::pg::Pg>>
   = note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Integer,_>` for `types::Record`
   = note: required because of the requirements on the impl of `diesel::query_dsl::LoadQuery<_,types::Record>` for `diesel::query_builder::SelectStatement<types::records::table,diesel::query_builder::select_clause::DefaultSelectClause,diesel::query_builder::distinct_clause::NodistinctClause,diesel::query_builder::where_clause::WhereClause<diesel::expression::operators::Eq<types::records::columns::iid,diesel::expression::bound::Bound<diesel::sql_types::Integer,i32>>>>`

如果我创建的版本不包含RecordId,但直接包含子部分,则没有错误

pub struct RecordDirect {
    pub iid: i32,pub id_0: i64,pub id_1: i64,}

// ...

pub fn find_direct(connection: &sqliteConnection) -> types::RecordDirect {
    records
        .find(1)
        .get_result::<types::RecordDirect>(connection)
        .unwrap()
}

类似地,我可以手动实现Queryable特性,也可以正常工作,

#[derive(Debug)]
pub struct RecordManual {
    pub iid: i32,}

impl Queryable<records::sqlType,diesel::sqlite::sqlite> for RecordManual {
    type Row = (i32,i64,String);
    fn build(row: Self::Row) -> Self {
        RecordManual {
            iid: row.0,id: RecordId {
                id_0: row.1,id_1: row.2,},data: row.3,}
    }
}

// ...

pub fn find_manual(connection: &sqliteConnection) -> types::RecordManual {
    records
        .find(1)
        .get_result::<types::RecordManual>(connection)
        .unwrap()
}

这种情况很难维护,我无法弄清楚如何使其能够插入使用-手动实现Insertable似乎比Queryable有点棘手。

为了使所有人都更容易使用它,我创建了一个存储库,其中包含一个几乎正在编译的小型复制器,其中包含本文中的代码块。 (通常我会把它放在生锈的操场上,但这不支持柴油)。您可以在https://github.com/mikeando/diesel_custom_type_demo上找到该代码

有没有办法使#[derive(Queryable)](和#[derive(Insertable)]在这种情况下起作用?


失败的初始情况的最小复制器是:

#[macro_use]
extern crate diesel;

use diesel::prelude::*;

mod types {
    use diesel::deserialize::Queryable;
    use diesel::sqlite::sqliteConnection;

    table! {
        records (iid) {
            iid -> Integer,}
    }

    #[derive(Debug,FromsqlRow)]
    pub struct RecordId {
        id_0: i64,}

    // Using a RecordId in a Record compiles,but 
    // produces an error when used in an actual query
    #[derive(Queryable,Debug)]
    pub struct Record {
        pub iid: i32,}
}

use types::records::dsl::*;

pub fn find(connection:&sqliteConnection) -> types::Record {
    records.find(1).get_result::<types::Record>(connection).unwrap()
}

解决方法

有没有办法使#[derive(Queryable)](和#[derive(Insertable)])在这种情况下起作用?

对于#[derive(Insertable)],只需在您的#[diesel(embedded)]字段中添加一个id并在两个结构上添加一个#[derive(Insertable)],就可以实现。有关详细信息,请参见Insertable的文档。 对于#[derive(Queryable)],这是不可能的,因为假设Queryable是从查询结果到结构的简单映射,并且基本假设输出的“形状”保持不变(至少对于派生而言)。 )。