问题描述
我正在使用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
是从查询结果到结构的简单映射,并且基本假设输出的“形状”保持不变(至少对于派生而言)。 )。