Diesel通用的插入、更新和删除实现无法与表格一起使用。

huangapple go评论82阅读模式
英文:

Diesel generic implementation of insert, update and delete won't work with tables

问题

以下是您提供的内容的中文翻译:

我一直在尝试组装一个通用的数据库层实现,使用 Diesel 编写,为了准备从我们当前的数据库迁移。

然而,尽管获得了一个工作的 trait,但它拒绝使用使用 table! 宏创建的表。

Trait

use diesel::{
    query_builder::{InsertStatement, UpdateStatement, IntoUpdateTarget, DeleteStatement, AsQuery, QueryFragment},
    query_dsl::{
        methods::ExecuteDsl,
    },
    r2d2::R2D2Connection,
    Table, 
    RunQueryDsl, associations::HasTable, AsChangeset, Connection, 
};

pub trait DataStore {

    type Conn: R2D2Connection + 'static;

    fn insert<T, M>(conn: &mut Self::Conn, records: M)
    where
        T: Table +  HasTable<Table = T> ,
        M: diesel::Insertable<T>,
        T::FromClause: QueryFragment<<Self::Conn as Connection>::Backend>,
        InsertStatement<T, M::Values>: ExecuteDsl<Self::Conn>,
    {
        diesel::insert_into(T::table()).values(records).execute(conn);
    }

    fn update<T, U, V>(conn: &mut Self::Conn, table: T, values: V)
    where
        T: IntoUpdateTarget +  HasTable<Table = T> + Table,
        V: diesel::query_builder::AsChangeset<Target = T>,
        UpdateStatement<T, <T as IntoUpdateTarget>::WhereClause, <V as AsChangeset>::Changeset>: AsQuery +  ExecuteDsl<Self::Conn>,
    {
        diesel::update(T::table()).set(values).execute(conn);
    }

    fn delete<T, U, V>(conn: &mut Self::Conn, table: T, values: V)
    where
        T: IntoUpdateTarget + HasTable<Table = T> + Table,
        V: diesel::query_builder::AsChangeset<Target = T>,
        DeleteStatement<T, <T as IntoUpdateTarget>::WhereClause>: AsQuery + ExecuteDsl<Self::Conn> {
            diesel::delete(T::table()).execute(conn);
    }

}

Table

diesel::table! {
    testing (code) {
        code -> Text,
        user_id -> Integer,
    }
}

Usage

//! 与数据库相关的模块。
pub use diesel::Connection;

use super::DataStore;

/// 与数据库的连接。
pub struct PgConn(diesel::pg::PgConnection);

impl DataStore for diesel::pg::Pg {
    type Conn = diesel::pg::PgConnection;
}

impl std::fmt::Debug for PgConn {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "PgConn(..)")
    }
}

#[derive(Clone, Debug, Default, Eq, Insertable, PartialEq, Queryable, Selectable)]
#[diesel(table_name = testing)]
pub struct TestingRow {
    /// 用于注册的代码
    pub code: String,
    /// 用户 ID 
    pub user_id: i32,
}

pub struct DbWrapper<Db: R2D2Connection + 'static,  T: DataStore<Conn = Db>> {
    db: Db,
    phantom: PhantomData<T>
}

impl<Db: R2D2Connection + 'static, T: DataStore<Conn = Db>> DbWrapper<Db, T> {
  pub fn insert_row(&self, row: &TestingRow) -> Result<()> {
        // 错误发生在这里
        T::insert::<testing, _>(*self.db.get(), row.clone());
        Ok(())
  }

}

参考

我的实现是基于此答案的:https://stackoverflow.com/questions/58589018/writing-diesel-crud-operations-for-generic-types

英文:

I've been trying to assemble a generic implementation of our database layer written using diesel in preparation for a migration from our current db.

However despite getting a working trait, it refuses to work with tables created using the table! macro.

Trait

use diesel::{
    query_builder::{InsertStatement, UpdateStatement, IntoUpdateTarget, DeleteStatement, AsQuery, QueryFragment},
    query_dsl::{
        methods::ExecuteDsl,
    },
    r2d2::R2D2Connection,
    Table, 
    RunQueryDsl, associations::HasTable, AsChangeset, Connection, 
};

pub trait DataStore {

    type Conn: R2D2Connection + &#39;static;

    fn insert&lt;T, M&gt;(conn: &amp;mut Self::Conn, records: M)
    where
        T: Table +  HasTable&lt;Table = T&gt; ,
        M: diesel::Insertable&lt;T&gt;,
        T::FromClause: QueryFragment&lt;&lt;Self::Conn as Connection&gt;::Backend&gt;,
        InsertStatement&lt;T, M::Values&gt;: ExecuteDsl&lt;Self::Conn&gt;,
    {
        diesel::insert_into(T::table()).values(records).execute(conn);
    }

    fn update&lt;T, U, V&gt;(conn: &amp;mut Self::Conn, table: T, values: V)
    where
        T: IntoUpdateTarget +  HasTable&lt;Table = T&gt; + Table,
        V: diesel::query_builder::AsChangeset&lt;Target = T&gt;,
        UpdateStatement&lt;T, &lt;T as IntoUpdateTarget&gt;::WhereClause, &lt;V as AsChangeset&gt;::Changeset&gt;: AsQuery +  ExecuteDsl&lt;Self::Conn&gt;,
    {
        diesel::update(T::table()).set(values).execute(conn);
    }

    fn delete&lt;T, U, V&gt;(conn: &amp;mut Self::Conn, table: T, values: V)
    where
        T: IntoUpdateTarget + HasTable&lt;Table = T&gt; + Table,
        V: diesel::query_builder::AsChangeset&lt;Target = T&gt;,
        DeleteStatement&lt;T, &lt;T as IntoUpdateTarget&gt;::WhereClause&gt;: AsQuery + ExecuteDsl&lt;Self::Conn&gt; {
            diesel::delete(T::table()).execute(conn);
    }

}

Table

diesel::table! {
    testing (code) {
        code -&gt; Text,
        user_id -&gt; Integer,
    }
}

Usage

//! Module for database related stuff.
pub use diesel::Connection;

use super::DataStore;

/// Connection to the DB.
pub struct PgConn(diesel::pg::PgConnection);

impl DataStore for diesel::pg::Pg {
    type Conn = diesel::pg::PgConnection;
}

impl std::fmt::Debug for PgConn {
    fn fmt(&amp;self, f: &amp;mut std::fmt::Formatter&lt;&#39;_&gt;) -&gt; std::fmt::Result {
        write!(f, &quot;PgConn(..)&quot;)
    }
}

#[derive(Clone, Debug, Default, Eq, Insertable, PartialEq, Queryable, Selectable)]
#[diesel(table_name = testing)]
pub struct TestingRow {
    /// Code used to sign up
    pub code: String,
    /// user_id 
    pub user_id: i32,
}

pub struct DbWrapper&lt;Db: R2D2Connection + &#39;static,  T: DataStore&lt;Conn = Db&gt; &gt; {
    db: Db,
    phantom: PhantomData&lt;T&gt;
}

impl&lt;Db: R2D2Connection + &#39;static, T: DataStore&lt;Conn = Db&gt;&gt; DbWrapper&lt;Db, T&gt; {
  pub fn insert_row(&amp;self, row: &amp;TestingRow) -&gt; Result&lt;()&gt; {
        // Error occurs here
        T::insert::&lt;testing, _&gt;(*self.db.get(), row.clone());
        Ok(())
  }

}

Reference

My implementation was based off the answer available here: https://stackoverflow.com/questions/58589018/writing-diesel-crud-operations-for-generic-types

答案1

得分: 1

调用具有通用参数的方法时,编译器必须验证所有通用边界是否有效。在您的情况下,您是在另一个通用上下文中调用该方法,这意味着有一些边界无法在不对外部上下文的通用边界施加限制的情况下验证。这会影响所有依赖于您的 DbT 通用类型的边界。
您可以通过将来自 DataStore::insert 的相关边界复制到 DbWrapper 的实现中,并在其中用正确的特定类型替换一些部分来解决这个问题:

impl<Db: R2D2Connection + 'static, T: DataStore<Conn = Db>> DbWrapper<Db, T>
where
    <testing::table as QuerySource>::FromClause: QueryFragment<<Db as Connection>::Backend>,
    InsertStatement<testing::table, <TestingRow as diesel::Insertable<testing::table>>::Values>: ExecuteDsl<Db>,
{
    fn get(&self) -> &mut Db {
        todo!()
    }

    pub fn insert_row(&self, row: &TestingRow) -> QueryResult<()> {
        T::insert::<testing::table, _>(&mut *self.get(), row.clone());
        Ok(())
    }
}

言归正传:通常不建议以这种方式编写通用的 diesel 代码,因为这很快会导致复杂的 trait 边界。

英文:

To call a method with generic arguments the compiler must verify that all generic bounds are valid. In your case you are calling that method inside of another generic context, which means that there are a few bounds that cannot be verified without placing restrictions on the generic bounds in the outer context. That affects all the bounds that depend on your Db or T generic type.
You can fix that by duplicating the relevant bounds from DataStore::insert on the DbWrapper impl and replacing some parts with the correct specific types there:

impl&lt;Db: R2D2Connection + &#39;static, T: DataStore&lt;Conn = Db&gt;&gt; DbWrapper&lt;Db, T&gt;
where
    &lt;testing::table as QuerySource&gt;::FromClause: QueryFragment&lt;&lt;Db as Connection&gt;::Backend&gt;,
    InsertStatement&lt;testing::table, &lt;TestingRow as diesel::Insertable&lt;testing::table&gt;&gt;::Values&gt;: ExecuteDsl&lt;Db&gt;,
{
    fn get(&amp;self) -&gt; &amp;mut Db {
        todo!()
    }

    pub fn insert_row(&amp;self, row: &amp;TestingRow) -&gt; QueryResult&lt;()&gt; {
        T::insert::&lt;testing::table, _&gt;(&amp;mut *self.get(), row.clone());
        Ok(())
    }
}

That written: It's generally not advised to write generic diesel code in that way as it leads to complex trait bounds really soon.

huangapple
  • 本文由 发表于 2023年5月11日 15:42:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76225196.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定