了解Poco C++ Poco::Data 数据库基本操作(二)
目录
Statement
前面我们提到了Statement,但是到目前为止,我们只使用了session,至少从代码上来看是这样的。
而实际上,我们已经在使用statement了,只不过它在代码中隐藏了起来。我们来看看Session类中的<<操作符:
1 2 3 4 5 6 |
template <typename T> Statement operator << (const T& t) /// Creates a Statement with the given data as SQLContent { return _statementCreator << t; } |
这里<<操作符创建了一个statement并将其返回。前面例子中我们使用<<操作符创建了statement,但是这个statement并没有分配到变量,一直到"now"部分executed了这个隐藏的statement,然后将其销毁。
我们将前面的例子拿一个出来修改一下:
1 2 3 4 |
std::string name("zrKing"); Statement stmt = (session << "INSERT INTO tbA VALUES(?)",use(name)); stmt.execute(); poco_assert(stmt.done()); |
当execute执行以后,数据已经插入了。可以使用stmt.done()来确认操作是否已经完全完成。
Prepared Statements
上例中省略的代码now子句创建了一个prepared statement.
prepared statement 的优势在于性能。我们假设有如下的循环:
1 2 3 4 5 6 7 |
std::string name; Statement stmt = (session << "INSERT INTO tbA VALUES(?)", use(name)); for(int i = 0; i<100; i++) { name.append("x"); stmt.execute(); } |
在上例中,如果不使用prepated statement,我们需要创建和解析Statement 100次。而现在我们只需要创建并解析一次Statement,然后使用占位符匹配不同的值,多次执行来将name插入到数据中。
当然,对于如何将一系列值插入到数据库,上例的代码并不是最优解。通过Poco::Data与STL容器协作可以更好的解决这个问题。具体的我们将在"STL容器"这一节来讲。
异步执行
到目前为止,我们看到的所有代码中,statement都是同步执行的。换句话说,不管是通过调用execute()方法还是直接调用或通过now子句调用,直到请求执行完成,程序控制权是不会交回到调用者手中的,程序将等待直到执行过程完成。现在我们可以让execute()方法立即执行并返回(当然了,事实上execute仍在一个独立的线程中执行),这是statement的异步执行。在这里我们将来了解这一过程,以及这一过程中需要注意的事情。
通过调用executeAsync()方法,任何一个statement对象都可以进行异步操作。此方法返回Statement::Result类型的const引用。该引用可以用来确认后台是否执行完成,并返回statement取到的rows:
1 2 3 4 |
Statement stmt = (ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age)); Statement::Result result = stmt.executeAsync(); // ... do something else Statement::ResultType rows = result.wait(); |
此例中,statement并没有做什么实质性的改变。和我们常用的同步执行没有什么两样。然而,这里有一种将statement永久变成异步的方式:
1 2 3 4 5 |
Statement stmt = (ses << "SELECT (age) FROM Person", into(age)); stmt.setAsync(true); // make stmt asynchronous stmt.execute(); // executes asynchronously // ... do something else Statement::ResultType rows = stmt.wait(); // synchronize and retrieve the number of rows |
然后,下面的操作操作达到的效果和上面setAync(true)是一样的:
1 2 3 4 |
Statement stmt = (ses << "SELECT (age) FROM Person", into(age), async); // asynchronous statement stmt.execute(); // executes asynchronously // ... do something else Statement::ResultType rows = stmt.wait(); |
在第一个例子中,我们拿到了statement执行的Result.然而在后面的两个例子中,我们拿不到execute()的返回值,Result将在某个时候返回。我们不等待execute()的返回值是因为对于异步statement,execute()将总是返回zero(这是正常的,因为程序也不知道将有多少rows会返回)。我们需要记住的是,在异步状态下,execute立即返回,并不会等待statement执行完成。
提醒
当异步执行时,确保相应的同步执行。当你同步执行失败时,你就会遇到各种奇怪的问题。下面的代码通常会抛出异常:
1 2 3 4 |
Statement stmt = (ses << "SELECT (age) FROM Person", into(age), async); // asynchronous statement Statement::Result result = stmt.execute(); // executes asynchronously stmt.execute(); // throws InvalidAccessException |
我们强调通常,因为它不是每次都发生,这取决于第一个在后台的execute()是否先于执行第二个调用完成。因此,为避免发生异常,墙裂建议 在下一步调用前,总是调用wait()