前言
POCO::Data 是Poco的数据库抽象层,为C++提供统一的结构化数据库访问接口,使得C++以简单而自然的方式访问关系型数据库。使用它可以方便地从多种数据库中存取数据。目前Poco::Data支持的数据库连接类型包括 SQLite,MySQL及ODBC。POCO框架支持扩展,所以你也可以添加其他的本地连接扩展。
下面是一个使用Poco::Data::SQLite的一个简单例子:
#include "Poco/Data/Session.h"
#include "Poco/Data/SQLite/Connector.h"
#include
#include
using namespace Poco::Data::Keywords;
using Poco::Data::Session;
using Poco::Data::Statement;
struct Person
{
std::string name;
std::string address;
int age;
};
int main()
{
//注册SQLite连接器
Poco::Data::SQLite::Connector::registerConnector();
// 创建传话 session
Session session("SQLite", "sample.db");
// 删除表
session << "DROP TABLE IF EXISTS Person", now;
// 网创建表
session << "CREATE TABLE Person (Name VARCHAR(30), Address VARCHAR, Age INTEGER(3))", now;
// 插入记录
Person person =
{
"Bart Simpson",
"Springfield",
12
};
Statement insert(session);
insert << "INSERT INTO Person VALUES(?, ?, ?)",
use(person.name),
use(person.address),
use(person.age);
insert.execute();
person.name = "Lisa Simpson";
person.address = "Springfield";
person.age = 10;
insert.execute();
// 简单查询
Statement select(session);
select << "SELECT Name, Address, Age FROM Person",
into(person.name),
into(person.address),
into(person.age),
range(0, 1);
while (!select.done())
{
select.execute();
std::cout << person.name << " " << person.address << " " << person.age << std::endl;
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include "Poco/Data/Session.h"
#include "Poco/Data/SQLite/Connector.h"
#include
#include
usingnamespacePoco::Data::Keywords;
usingPoco::Data::Session;
usingPoco::Data::Statement;
structPerson
{
std::stringname;
std::stringaddress;
intage;
};
intmain()
{
//注册SQLite连接器
Poco::Data::SQLite::Connector::registerConnector();
// 创建传话 session
Sessionsession("SQLite","sample.db");
// 删除表
session<
// 网创建表
session<
// 插入记录
Personperson=
{
"Bart Simpson",
"Springfield",
12
};
Statementinsert(session);
insert<
use(person.name),
use(person.address),
use(person.age);
insert.execute();
person.name="Lisa Simpson";
person.address="Springfield";
person.age=10;
insert.execute();
// 简单查询
Statementselect(session);
select<
into(person.name),
into(person.address),
into(person.age),
range(0,1);
while(!select.done())
{
select.execute();
std::cout<
}
return0;
}
创建会话(Sessions)
通过Session 构造器可以创建Session:
Session session("SQLite","./sample.db");
1
Sessionsession("SQLite","./sample.db");
这个例子创建了一个SQLite类型的session。
第一个参数是希望创建的Session类型,目前支持的连接类型有”SQLite”,”ODBC”,”MySQL”。
第二个参数是数据库连接字符串,根据不同的数据库使用不同的连接字符串:
对于Sqlite数据库,数据库路径即可做为连接字符串
对于ODBC数据库,连接字符串类似于 “DSN=MyDSNName”.具体参数请参考ODBC驱动文档
对于MySQL数据,连接字符串由一系列键值对组成,例如 “host=localhist;port=3306;db=mydb;user=thename;password=thepwd;compress=true;auto-reconnect=true”;具体请教参MySQL官方文档。
存取数据
单数据集
假设我们有一个表 tbA,表中有一个字段 fN,插入记录时我们可以这么写:
std::string name="wandoer";
session << "INSERT INTO tbA VALUES("<
1
2
std::stringname="wandoer";
session<
是不是很简单。然而这种写法并不提倡,我们有更好的方式:占位符!使用占位符(placeholders)可以匹配变量和占位符,在执行语句时自动用变量替换占位符。普遍公认的占位符是问号(?),有些数据库也使用冒号(:)来做占位符。具体使用哪一种,请参考所选用数据库的说明文档。
那么上面的例子可以写做:
std::string name="wandoer";
session << "INSERT INTO tbA VALUES(?)",use(name), now;
1
2
std::stringname="wandoer";
session<
此例中使用use(name)来匹配占位符。use是Poco::Data::Keywords下提供的关键字,可以将变量和占位符绑定(Binding)。这么做真正的意义,是将变量与SQL语句分隔开,防止SQL注入式攻击。
从数据库中取数据的操作与之类似。into关键字将数据库返回的值与C++对象匹配起来。并且支持指定默认值以防数据库返回null值。
std::string name;
session << "SELECT fN FROM tbA",into(name),now;
session << "SELECT fN FROM tbA",into(name,0,std::string("default")),now;
1
2
3
std::stringname;
session<
session<
此处需要注意的是,指定默认值的into,其定义为:into(T& t, const Position& pos, const T& def),使用的是模样类。在有些编译器下,不能隐式转换数据类型,所以需要显示的让第一个参数和第三个参数的数据类型保持一致。
into还可以和use联合使用:
std::string name;
std::string match="wandoer";
session << "SELECT fN FROM tbA WHERE fN=?",into(name),use(match),now;
1
2
3
std::stringname;
std::stringmatch="wandoer";
session<
当然,现实中的数据库不会总像例子中这么简单。在一个表中往往有多列,这样需要使用多次into/use关键字:
std::string firstName("Peter");
std::string lastName("Junior");
int age = 0;
ses << "INSERT INTO PERSON VALUES (?, ?, ?)", use(firstName), use(lastName), use(age), now;
ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age), now;
1
2
3
4
5
std::stringfirstName("Peter");
std::stringlastName("Junior");
intage=0;
ses<
ses<
那么此时尤为重要的,就是into和use的顺序了。第一个占位符使用第一个use,第二个占位符使用第二个use,以此类推。into亦如是。
使用 NULL 实体
数据库中有些字段值可能为NULL.容纳NULL需要使用Poco::Nullable模板:
std::string firstName("Peter");
Poco::Nullable<:string> lastName("Junior");
Poco::Nullable age = 0;
ses << "INSERT INTO PERSON VALUES (?, ?, ?)", use(firstName), use(lastName), use(age), now;
ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age), now;
if (!lastName.isNull()) { ... }
1
2
3
4
5
6
std::stringfirstName("Peter");
Poco::Nullable<:string>lastName("Junior");
Poco::Nullableage=0;
ses<
ses<
if(!lastName.isNull()){...}
此例中使用的Poco::Nullable是一个轻量级的模板类。它可以包装任何允许为null的类型。至于空字符串(empty string)是不是null这个问题,不同的数据库有不同的规定。Poco::Data的Session提供了一种方式将其统一化:
emptyStringIsNull
forceEmptyString
如果你使用的数据库将empty strings看作null而你希望Poco::Data模仿这种行为,你可以这样做:
session.setFeature("emptyStringIsNull",true);
1
session.setFeature("emptyStringIsNull",true);
相反地,如果你的数据库将empty strins看作null而你不希望如此,你可以这样做:
session.setFeature("forceEmptyString",true);
1
session.setFeature("forceEmptyString",true);
很明显,以上两个选项是互斥的。如果将二者都设为true,那就等着Poco::Data抛出异常吧!
多数据集
Poco::Data支持批量操作。批量查询返回多数据集。所以into表达式需要扩展参数来确定使用的是哪个数据集:
typedef Tuple<:string std::string int> Person;
std::vector people;
Person pHomer, pLisa;
int aHomer(42), aLisa(10), aBart(0);
session << "SELECT * FROM Person WHERE Age = ?; "
"SELECT Age FROM Person WHERE FirstName = 'Bart'; "
"SELECT * FROM Person WHERE Age = ?",
into(pHomer, 0), use(aHomer),
into(aBart, 1),
into(pLisa, 2), use(aLisa),
now;
1
2
3
4
5
6
7
8
9
10
11
typedefTuple<:string>Person;
std::vectorpeople;
PersonpHomer,pLisa;
intaHomer(42),aLisa(10),aBart(0);
session<
"SELECT Age FROM Person WHERE FirstName = 'Bart'; "
"SELECT * FROM Person WHERE Age = ?",
into(pHomer,0),use(aHomer),
into(aBart,1),
into(pLisa,2),use(aLisa),
now;
Now关键字
最后来看看Now关键字。简单来讲,这是一个控制器。正如字面意思所表达的,它让statement 立即执行。如果不使用now,那么statement必须在需要执行的地方按步执行。这种操作我们在下一节中再讲