如何存储 SQLite 准备好的语句供以后使用? [英] How to store SQLite prepared statements for later?

查看:43
本文介绍了如何存储 SQLite 准备好的语句供以后使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

extern crate rusqlite;使用 rusqlite::SqliteConnection;struct MyAppState {数据库:SqliteConnection,//... 假设这里还有其他字段...}impl MyAppState {fn new() ->我的应用状态{让 db = SqliteConnection::open(":memory:").unwrap();我的应用状态{数据库:数据库}}fn query_some_info(&mut self, arg: i64) ->i64 {让 mut stmt = self.db.prepare("SELECT ? + 1").unwrap();让 mut result_iter = stmt.query(&[&arg]).unwrap();让 result = result_iter.next().unwrap().unwrap().get(0);结果}}fn 主(){让 mut app = MyAppState::new();对于 i 在范围内 (0, 100) {让结果 = app.query_some_info(i);println!("{}", 结果);}}

由于prepared statement存在于局部变量中,这似乎在某种程度上错过了prepared statements的意义,因为每次调用函数和局部变量产生时我都必须重新准备它.理想情况下,我最多准备一次所有语句,并在数据库连接期间将它们存储在 MyAppState 结构中.

然而,由于 SqliteStatement 类型在 db 连接的生命周期内被参数化,它借用连接并通过扩展它所在的结构,我不能再对结构做任何事情,比如按值返回结构或者在其上调用 &mut self 方法(query_some_info 在这里并不真正需要使用 &mut self,但是我的一些代码除非一切都继续存在于 RefCell 中,否则实际程序会这样做,我想这还不是最糟糕的,但仍然如此).

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

理想情况下,我想编写的代码只在数据库打开时准备语句,或者可能只在第一次使用时准备一次,然后在此期间不再调用 preparedb 连接的持续时间,同时主要保持 rusqlite 绑定的安全性,而不是针对 sqlite3 C API 编写代码或破坏抽象或其他什么.我该怎么办?

解决方案

确实,兄弟引用在 Rust 中很尴尬.不过有一个很好的理由,它们不容易被所有权系统建模.

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

这确实意味着 db 将保持借用,显然.

Right now I have code that uses the rusqlite sqlite bindings to open a db connection and do a bunch of db operations in my application like this:

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);
    }
}

Since the prepared statement lives in a local variable, this seems to miss the point of prepared statements to some extent since I have to re-prepare it every time the function is called and the local variable comes into being. Ideally, I would prepare all my statements at most once and stash them in the MyAppState struct for the duration of the db connection.

However, since the SqliteStatement type is parameterized over the lifetime of the db connection, it borrows the connection and by extension the struct it lives in and I can't do anything with the struct anymore like return the struct by value or call &mut self methods on it (query_some_info doesn't really need to take &mut self here, but some code in my actual program does unless everything goes on to live in RefCells, which isn't the worst, I guess, but still).

Usually when the borrow checker betrays me like that, my recourse is to give up on stack discipline and put some Rc<RefCell< >> here and there until it all works out, but in this case there's some lifetimes in the types either way and I don't know how to word it in a way that appeases the borrow checker.

Ideally I'd like to write code that only prepares the statements right when the db gets opened, or maybe prepares them only once when they are first used, and then never calls prepare again during the duration of the db connection, while mostly keeping the safety of the rusqlite bindings rather than writing code against the sqlite3 C API or breaking abstraction or whatever. How do I?

解决方案

You are right, indeed, that sibling references are awkward in Rust. There is a good reason though, they are not easily modeled by the ownership system.

In this particular case, I would advise you to split the structure: you can keep the prepared statements in a dedicated cache also parametrized on the lifetime of the db for example; the db instead should be instantiated at the top of your program and passed down (think dependency injection) so that the cache that depends on it can outlive the program main function.

This does mean that the db will remain borrowed, obviously.

这篇关于如何存储 SQLite 准备好的语句供以后使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆