了解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
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()