【问题标题】:Encapsulate a postgresql Transaction in a structure将 postgresql 事务封装在一个结构中
【发布时间】:2021-04-11 12:27:48
【问题描述】:

我正在尝试封装 Postgresql 事务,但遇到了一些生命周期问题。

这里是代码: Code

我理解错误消息:“返回引用当前函数拥有的数据的值”

但我不知道如何将“事务”保留在我的 SQLConnection 结构中。

use postgres::{Client, NoTls, Transaction};

pub struct SQLConnection<'a> {
    client: Client,
    transaction: Transaction<'a>,
}

impl<'a> SQLConnection<'a> {
    pub fn new(connect_string: &str) -> Self {
        let mut client = Client::connect(connect_string, NoTls).unwrap();
        let transaction = client.transaction().unwrap();
        Self {
            client,
            transaction,
        }
    }

    pub fn commit(&self) {
        let _ = self.transaction.commit();
    }

    pub fn rollback(&self) {
        let _ = self.transaction.rollback();
    }
}

【问题讨论】:

  • SQL事务的概念你清楚了吗?如果没有,您应该在继续之前查看一下。
  • 是的,SQL事务的概念非常清楚。它将许多查询组合在一起,以保持系统的一致性,直到您一次提交所有更改并使所有其他会话可见更改。

标签: postgresql rust lifetime


【解决方案1】:

您正在返回对您不保留的值的引用。如果没有自有价值,则无法获得参考。

在您的情况下,也没有理由同时保留客户和交易。您的连接应该包含 client 而不是 transaction,这是一个短暂的对象,不应仅用于操作。

因此您的连接应该是

pub struct SQLConnection {
    client : Client,
}

那么你应该,对于一个操作,获取一个transaction,使用它,然后在保持连接的同时丢弃它。

【讨论】:

  • 其实是我先做的。但留住客户并不是我真正想做的事情。例如,我想将相同的事务传递给执行“插入”的不同例程。但是我被卡住了,因为到目前为止无法封装 pg 事务。
【解决方案2】:

我找到了适合我需要的解决方案。我现在有 SQLConnection 和 SQLTransaction,我从我的连接中获得了 SQLTransation。 现在我可以使用我的事务来对我的基本 sql 操作进行分组。

这里是code

use postgres::{Client, NoTls, Transaction};

pub struct SQLConnection {
    client: Client,
}

impl SQLConnection {
    pub fn new(connect_string: &str) -> Self {
        let client = Client::connect(connect_string, NoTls).unwrap();
        Self { client }
    }

    pub fn transaction(&mut self) -> SQLTransaction {
        let t = self.client.transaction().unwrap();
        SQLTransaction { transaction: t }
    }
}

pub struct SQLTransaction<'a> {
    transaction: Transaction<'a>,
}

impl<'a> SQLTransaction<'a> {
    pub fn new(transaction: Transaction<'a>) -> Self {
        Self { transaction }
    }

    pub fn commit(self) {
        let _ = self.transaction.commit();
    }

    pub fn rollback(self) {
        let _ = self.transaction.rollback();
    }
}

【讨论】:

  • 我很好奇 SQLTransaction 包装器比直接使用 postgres::Transaction 增加了什么价值?此外,您的commit()rollback() 都只是忽略错误,这是非常危险的,因为它可能会导致程序继续认为一切都很好,而实际上并没有发生提交(或回滚)。如果您不希望发生错误,我建议将let _ = self.transaction.commit(); 替换为self.transaction.commit.unwrap();,这样如果确实发生错误,程序就会出现恐慌。
  • 您对错误处理完全正确。我不打算忽略它们,但它确实不是帖子的主题? 我可以传递 Transaction 本身,但我想将所有特定的 pg 结构封装到个人 crate 中以进行小型抽象。所以主程序不知道它与 postgresql 对话。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-27
  • 2013-01-05
相关资源
最近更新 更多