rust - 如何为具有映射到多个柴油列的自定义字段的类型派生 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::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, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` is not implemented for `(i32, types::RecordId, std::string::String)`
|
= help: the following implementations were found:
<(A, B, C) as diesel::Queryable<(SA, SB, SC), __DB>>
<(A, B, C) as diesel::Queryable<diesel::sql_types::Record<(SA, SB, SC)>, diesel::pg::Pg>>
= note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` 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 data: String,
}
// ...
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,
pub id: RecordId,
pub data: String,
}
impl Queryable<records::SqlType, diesel::sqlite::Sqlite> for RecordManual {
type Row = (i32, i64, 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,
id_0 -> BigInt,
id_1 -> BigInt,
data -> Text,
}
}
#[derive(Debug, Copy, Clone, FromSqlRow)]
pub struct RecordId {
id_0: i64,
id_1: 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,
pub id: RecordId,
pub data: String,
}
}
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
字段中添加 a 并#[derive(Insertable)]
在两个结构上添加 a 即可。有关详细信息,请参阅文档Insertable
。因为#[derive(Queryable)]
这是不可能的,因为Queryable
应该是从查询结果到结构的简单映射,基本假设输出的“形状”保持不变(至少对于派生而言)。
推荐阅读
- biopython - 在两个 gb 文件中搜索匹配的序列
- swiftui - 你能像 ToolbarContent 的命名块一样使用闭包吗 - ToolbarItem
- javascript - 禁止语句不删除消息并禁止用户
- asp.net-mvc - 如何将此 .net 代码转换为 .net 核心代码
- java - 为什么 println() 函数在这种情况下表现不同?
- git - 在 $git push -u origin main 命令之后,Git bash 没有请求任何身份验证,实际上什么都不做
- anaconda - 无法在 Win10 中使用 miniconda 安装 GCC
- java - 面对 java.lang.NoSuchMethodError: oracle.i18n.text.converter.CharacterConverter.toOracleStringWithReplacement 异常
- jenkins - PATH 中没有这样的可执行文件 D:\Git\bin\git.exe: /usr/local/sbin, /usr/local/bin, /usr/sbin, /usr/bin, /root/bin
- r - 如何从 .pdf 文件中正确提取所有表并将其写入 R 中的 .csv 文件?