O/R映射
模型(model)在内部通过O/R映射对象访问数据库. 映射对象是一个基本的一对一关系, 被当作ORM对象. 可以通过下图来表示:
一条记录对应一个对象.虽然经常有一对多的关系.
因为模型(model)是一个要返回浏览器的集合信息, 你需要理解如何访问和生成数据库的ORM对象.
以下DBMS驱动是支持的(等同于Qt支持的驱动):
- MySQL
- PostgreSQL
- SQLite
- ODBC
- Oracle
- DB2
- InterBase
按照这些描述, 让我们比对数据库和面对对象语言术语的对应关系.
** 术语对应表**
面向对象 | 关系型数据库RDB |
---|---|
类Class | 表Table |
对象Object | 记录Record |
属性Property | 字段Field |
数据库连接信息
数据库的连接信息定义在配置文件(config/database.ini). 配置文件的内容分成3节: product, test,和dev. 在网页应用启动命令字符串(treefrog)指定选项(-e), 可以切换数据库.此外, 你可以增加一个节.
参数可以通过下面的方式来设置:
| 参数Parameters | 描述Description |
|——————–|—————————————————————————————————————————————————-|
| driverType | 从这些驱动中选择Driver typeChoose from:
QMYSQL, QPSQL, QSQLITE, QODBC, QOCI, QDB2, QIBASE |
| databaseName | 如果是SQLite,含有路径的数据库文件名,如db/dbfile Database name Specify the file path in the case of SQLite, e.g. *db/dbfile |
| hostName | 主机名称Host name |
| port | 端口Port |
| userName | 用户名User name |
| password | 密码 Password |
| connectOptions | 连接选项
Referto 更多信息参看Qt文档documentationQSqlDatabase::setConnectOptions() |
这样, 当你启动网页应用时, 系统将自动管理这些数据库连接. 对于开发者来说, 不需要处理数据库的连接和关闭.
在数据库创建一个表后, 在配置文件的dev节设置连接信息.
下面的节将使用教程中创建的BlogObject类作为例子.
读取ORM对象
这是一个基本的操作. 它用来从一个指定的表中查找内容. 通过findByPrimaryKey()方法传递的id用来在表中查找匹配它的内容(id设置为主键). 如何查询到有匹配的, 记录的内容将被读取.
int id;
id = ...
TSqlORMapper<BlogObject> mapper;
BlogObject blog = mapper.findByPrimaryKey(id);
你还可以使用findAll()方法读取所有记录.
TSqlORMapper<BlogObject> mapper;
QList<BlogObject> list = mapper.findAll();
你必须特别小心, 有这种可能性:当记录数量特别大时, 读取所有记录可能消耗内存. 你可以用setLimit()方法设置一个数组的上限.
ORM内部会生成SQL语句. 要查看执行了什么样的查询语句, 请查看查询记录.
迭代器
如果你想一条一条的出来查询结果, 你可以使用迭代器.
TSqlORMapper<BlogObject> mapper;
mapper.find(); // 执行查询
TSqlORMapperIterator<BlogObject> i(mapper);
while (i.hasNext()) { // 迭代器
BlogObject obj = i.next();
// 处理过程..
}
指定查询条件读取ORM对象
查询条件通过Tcriteria类指定. 查询Title字段内容为”Hello world”的单条记录, 可以通过如下方式:
TCriteria crt(BlogObject::Title, "Hello world");
BlogObject blog = mapper.findFirst(crt);
if ( !blog.isNull() ) {
// 如果记录存在
} else {
// 如果记录不存在
}
你还可以组合多个条件.
// 条件为 title = "Hello World" 并且 create_at > "2011-01-25T13:30:00"
TCriteria crt(BlogObject::Title, tr("Hello World"));
QDateTime dt = QDateTime::fromString("2011-01-25T13:30:00", Qt::ISODate);
crt.add(BlogObject::CreatedAt, TSql::GreaterThan, dt); // 增加Add到运算符
TSqlORMapper<BlogObject> mapper;
BlogObject blog = mapper.findFirst(crt);
:
如果希望建立一个”或”运算符, 可以使用addOr()方法.
// 条件为 title = "Hello World" 并且 create_at > "2011-01-25T13:30:00" )
TCriteria crt(BlogObject::Title, tr("Hello World"));
QDateTime dt = QDateTime::fromString("2011-01-25T13:30:00", Qt::ISODate);
crt.addOr(BlogObject::CreatedAt, TSql::GreaterThan, dt); // 增加AddOr到运算符
:
如果用addOr()方法增加查询条件, 查询条件会用括号闭合. 如果组合使用add()和addOr()方法, 使用它们时需要小心它们的顺序.
注意
记住, 当一起使用AND和OR运算时, AND运算有优先权. 也就是说, 当混合使用AND和OR运算时, 先计算AND运算符, 然后在计算OR运算. 如果你希望安装顺序执行计算, 需要使用扩号.
创建ORM对象
按照创建普通对象的方式创建ORM对象, 然后设置它的属性. 使用create()方法插入到数据库中.
BlogObject blog;
blog.id = ...
blog.title = ...
blog.body = ...
blog.create(); // 插入到数据库中
更新一个ORM对象
为了更新ORM对象, 在读取记录前需要首先创建一个ORM对象. 一旦从数据库中取得ORM对象, 你可以设置它的属性, 然后用update()方法更新它的状态.
TSqlORMapper<BlogObject> mapper;
BlogObject blog = mapper.findByPrimaryKey(id);
blog.title = ...
blog.update();
删除ORM对象
删除ORM对象意味着删除它的记录.
先读取ORM对象,然后通过remove()方法删除它.
TSqlORMapper<BlogObject> mapper;
BlogObject blog = mapper.findByPrimaryKey(id);
blog.remove();
像其他的方法一样, 你可以直接删除符合条件的记录, 不需要创建一个ORM对象.
//删除Title字段是"Hello"的记录
TSqlORMapper<BlogObject> mapper;
mapper.removeAll( TCriteria(BlogObject::Title, tr("Hello")) );
自动ID序列号
在一些数据库系统, 有字段的自动编号功能. 例如, 在MySQL的AUTO_INCREMENT属性, 或者PostgreSQL的serial类型.
Treefrog框架也设计了这种机制.也就是说, 下面的例子数字是自动分配的. 没有必要去更新或者新建这个字段.
首先, 创建一个还有自动序列的字段的表. 然后, 使用生成器命令创建模型(model)后, 我们不再需要人工更新字段(这里是’id’).
MySQL范例:
CREATE TABLE animal ( id INT PRIMARY KEY AUTO_INCREMENT, ...
PostgreSQL范例:
CREATE TABLE animal ( id SERIAL PRIMARY KEY, ...
新建和更新时自动保存日期和时间
经常有这样的例子, 当一条记录被记录时希望能够保存其日期和时间. 因为这是个例行的实现, 字段名称可以提前根据规则设定, 到达框架可以自己自动处理.
保存记录创建时的日期和时间,可以使用created_at字段名. 更新记录的日期和时间,使用updated_at字段名代替. 为达到目的, 我们使用TIMESTAMP类型.如何有这样的字段, ORM对象将自动设置时间戳.
项 | 字段名 |
---|---|
保存创建时的日期和时间 | created_at |
保存修改时的日期和时间 | updated_at 或 modified_at |
自动保存日期和时间也可以在数据库端进行处理. 我的建议是, 虽然在数据库中也可以很好的实现, 在框架中实现更好一些.
两种方式用哪种并不重要, 我想我们都不计较, 但是用框架实现, 你可定义你想要的复杂的字段名称, 并且让数据库做其他事.
乐观锁
乐观锁是在要更新记录时确认记录没有被其他人更新, 而不用在数据库锁定记录. 如果已经发生了其他更新, 就会放弃更新.
使用一个自增的整形(integer)类型的字段lock_revision是前提条件. Lock revision在每次更新时增加. 当读取到的值与更要更新的值不相同时, 意味着已经被其他人更新了. 只有在值相同时才进行更新. 通过这样的方式, 我们能够安全的更新记录. 因为没有使用锁, 可以预期会节省了数据库系统的内存和提升了处理速度, 虽然它们是比较轻微的.
要在SQlObject中使用乐观锁的优势, 在表中增加一个整形字段,并命名为lock_revision. 使用生成器创建一个类.当调用TSqlObject::remove()和TSqlObject:: update()方法时, 乐观锁将激活.