如何存储SQLite准备好的语句供以后使用?

时间:2014-12-18 17:47:45

标签: sqlite rust

现在我的代码使用rusqlite sqlite bindings打开数据库连接并在我的应用程序中执行一系列数据库操作,如下所示:

extern crate rusqlite;

use rusqlite::SqliteConnection;

struct MyAppState {
    db: SqliteConnection,
    // ... pretend there's other fields here ...
}

impl MyAppState {
    fn new() -> MyAppState {
        let db = SqliteConnection::open(":memory:").unwrap();
        MyAppState {
            db: db
        }
    }

    fn query_some_info(&mut self, arg: i64) -> i64 {
        let mut stmt = self.db.prepare("SELECT ? + 1").unwrap();
        let mut result_iter = stmt.query(&[&arg]).unwrap();
        let result = result_iter.next().unwrap().unwrap().get(0);

        result
    }
}

fn main() {
    let mut app = MyAppState::new();
    for i in range(0, 100) {
        let result = app.query_some_info(i);
        println!("{}", result);
    }
}

由于预处理语句存在于局部变量中,因此每次调用函数并生成局部变量时,我必须重新准备它,这似乎在某种程度上忽略了预处理语句的要点。理想情况下,我最多只准备一次所有语句,并在数据库连接期间将它们存储在MyAppState结构中。

但是,自SqliteStatement type is parameterized over the lifetime of the db connection以来,它借用了连接,并扩展了它所在的结构,我不再对结构做任何事情,比如按值返回struct或调用&mut self方法在它上面query_some_info并不需要在这里取&mut self,但我的实际程序中的一些代码除非一切都继续存在于RefCell中,这不是最差的,我猜,但仍然。)

通常当借阅检查员背叛我这样的时候,我的办法是放弃堆栈纪律并在这里和那里放一些Rc<RefCell< >>,直到一切顺利,但在这种情况下,类型中有一些生命周期方式,我不知道如何以一种安抚借阅检查器的方式来表达它。

理想情况下,我想编写只在db打开时才准备语句的代码,或者可能只在第一次使用它们时准备一次,然后再在第二次使用期间再次调用prepare。数据库连接,主要是保持rusqlite绑定的安全性,而不是针对sqlite3 C API编写代码或破坏抽象等等。我怎么样?

2 个答案:

答案 0 :(得分:4)

你确实是对的,在Rust中,兄弟姐妹的引用很尴尬。但是有一个很好的理由,它们不容易被所有权制度建模。

在这种特殊情况下,我建议你拆分结构:你可以将准备好的语句保存在db的生命周期中参数化的专用缓存 中; db应该在程序的顶部实例化并传递下来(想想依赖注入),以便依赖它的缓存可以比程序主函数更长。

这确实意味着db显然会被借用。

答案 1 :(得分:0)

Statement结构具有生存期参数Statement<'conn>。准备该语句时,必须引用一个超出该语句的Connection

extern crate rusqlite;

use rusqlite::{Connection, Statement};

struct MyAppState {
    db: Connection,
}

impl MyAppState {
    fn new() -> MyAppState {
        let db = Connection::open(":memory:").unwrap();
        MyAppState { db: db }
    }
}

struct PreparedStatement<'conn> {
    statement: Statement<'conn>,
}

impl<'conn> PreparedStatement<'conn> {
    pub fn new<'a>(conn: &'a Connection, sql: &str) -> PreparedStatement<'a> {
        PreparedStatement {
            statement: conn.prepare(sql).unwrap(),
        }
    }

    fn query_some_info(&mut self, arg: i64) -> i64 {
        let mut result_iter = self.statement.query(&[&arg]).unwrap();
        let result = result_iter.next().unwrap().unwrap().get(0);

        result
    }
}

fn main() {
    let app = MyAppState::new();
    let mut prepared_stmt = PreparedStatement::new(&app.db, "SELECT ? + 1");
    for i in 0..100 {
        let result = prepared_stmt.query_some_info(i);
        println!("{}", result);
    }
}

在Rust中,与某些其他语言不同,我发现将某些因素分解到函数中会改变其含义。它引入了新的生命周期,通常对您不利。但是在这种情况下,这正是需要的。