1、sql框架
sql类分为三层
-
驱动层
包括QSqlDriver , QSqlDriverCreator , QSqlDriverCreatorBase , QSqlDriverPlugin ,和 QSqlResult.
这一层提供了特定数据库和SQL API层之间的底层桥梁。
-
SQL API 层
提供了对数据库访问的功能, 连接使用 QSqlDatabase,数据库交互通过 QSqlQuery 实现。除了这两个类,这一层还支持 QSqlError , QSqlField , QSqlIndex ,和 QSqlRecord .
-
用户界面层
将数据从数据库链接到用户界面上的widget。包括 QSqlQueryModel , QSqlTableModel ,和 QSqlRelationalTableModel 。这些类用于支持Qt的MVC框架。
2、数据库连接
通常开发不会设计到驱动层的修改,主要是使用API 和 界面层
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); //指定Mysql驱动
db.setHostName("127.0.0.1"); //主机名
db.setPort(3306); //端口号 可不加默认为3306
db.setDatabaseName("mydb"); //数据库名
db.setUserName("root"); //用户名
db.setPassword("200291"); //用户密码
bool ok = db.open(); //开启数据库
如果指定报了QSqlDatabase: QMYSQL driver not loaded
错说明qt模块下没有mysql的动态链接库。可以产看qt缺少mysql驱动
解决。
如果想建立多个连接可以给QSqlDatabase::addDatabase()
传递第二个参数获得命名连接,例如:
QSqlDatabase firstDB = QSqlDatabase::addDatabase("QMYSQL", "firstName");
QSqlDatabase secondDB = QSqlDatabase::addDatabase("QMYSQL", "secondName");
在建立连接后可以调用静态函数QSqlDatabase::database()
来获取连接名,如果不传递任何参数,将会返回默认值。
QSqlDatabase defaultDB = QSqlDatabase::database();
QSqlDatabase firstDB = QSqlDatabase::database("firstName");
QSqlDatabase secondDB = QSqlDatabase::database("secondName");
删除数据库信息可以,先调用QSqlDatabase::close()
关闭数据库连接,然后调用静态函数QSqlDatabase::removeDatabase()
。
/* 删除默认数据库*/
defaultDb.close();
QSqlDatabase::removeDatabase();
/* 删除命名数据库*/
firstDB.close();
QSqlDatabase()::removeDatabase(firstName);
3、执行Sql语句
查询语句需要引入QSqlQuery
。exec()不仅可以使用DML(数据操纵语言,例如 insert, delete, updata, select),还可以使用DDL(数据定义语言,例如create table, 更改字段)。
查询结构
QSqlQuery squery;
if(!squery.exec("select * from user"))
qDebug() << "query failed!";
while(squery.next()) {
QString value = squery.value(1).toString();
qDebug() << value;
}
- exec()可以传入一个字符串,CRUD的任何一种。如果查询失败会返回
false
. - 查询成功后,查询到的数据以条为单元存储在QSqlQuery内,QSqlQuery的内部指针位于第一个记录之前的位置,所以要调用一次
next()
才可以读取数据。 value()
有两个重载分别是value(int)
和value(const QString&)
第一个重载传入的参数是该字段在数据库中是第几个字段,第二个重载传入的是数据库中字段的名。- 不同的数据库字段类型会自动映射到最接近的Qt等价类型中。这个为qt推荐类型的表 table .
获取读取数据的大小
如果选用的数据库驱动支持QSqlDriver::QuserySize
可以直接调用QSqlQuery::size()
否则可使用QSqlQusery::last()
跳转到最后一条语句,然后使用QSqlQuery::at()
(当前是第几条记录,从0开始)。QMYSQL
是支持的。
if(db.driver()->hasFeature(QSqlDriver::QuerySize))
qDebug() << squery.size();
else {
squery.last();
qDebug() << squery.at() + 1;
}
如果想获得所在记录有多少个字段可以使用record()
获得一个QSqlRecord
对象然后调用count()
获取目前记录有几个字段。
#include<QSqlRecord>
qDebug() << squery.record().count();
PS:如果所有记录只调用next()就可以,可以QSqlQuery::setForwardOnly(true)
,这样会导致一些向前访问的函数无法使用,但是可以减少迭代器的开销,对于数据量比较大的结果,会有明显的优化。
squery.setForwardOnly(true);
占位符使用
如果要执行多条结构相同的sql指令,使用占位符可以避免大量的字符串拼接操作。占位符由两种形式。
-
命名占位符
QSqlQuery query; query.prepare("INSERT INTO employee (id, name, salary) " "VALUES (:id, :name, :salary)"); query.bindValue(":id", 1001); query.bindValue(":name", "Thad Beaumont"); query.bindValue(":salary", 65000); query.exec();
-
匿名占位符
QSqlQuery query; query.prepare("INSERT INTO employee (id, name, salary) " "VALUES (?, ?, ?)"); query.addBindValue(1001); query.addBindValue("Thad Beaumont"); query.addBindValue(65000); query.exec();
对于执行多个
exec()
,prepare()
只需要设置一次。
事务
如果使用的数据库引擎支持事务,可以调用QSqlDatabase::transaction()
来开启事务。可以使用QSqlDriver::hasFeature(QSqlDriver::Transactions)
来检查使用的数据库是否支持。事务必须在查询语句使用之前开启。
QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'");
if (query.next()) {
int employeeId = query.value(0).toInt();
query.exec("INSERT INTO project (id, name, ownerid) "
"VALUES (201, 'Manhattan Project', "
+ QString::number(employeeId) + ')');
}
QSqlDatabase::database().commit();
4、Sql模型类
除了QSqlQuery
,Qt还封装了更高层次的模型类来访问数据库。主要的有三个QSqlQueryModel
、QSqlTableModel
和QSqlRelationTableModel
。这些类可以通过View
类来展示数据。例如QListView
和QTableView
。
QSqlQueryModel
类
QSqlQueryModel model;
model.setQuery("SELECT * FROM employee");
for (int i = 0; i < model.rowCount(); ++i) {
int id = model.record(i).value("id").toInt();
QString name = model.record(i).value("name").toString();
qDebug() << id << name;
}
在数据库连接建立的情况下,使用QSqlQuery::setQuery()
来进行数据库的查询,调用 record()
函数来分别处理每一条数据。
QSqlTableModel
类
- 查询
QSqlTableModel model;
model.setTable("employee");
model.setFilter("salary > 50000");
model.setSort(2, Qt::DescendingOrder);
model.select();
for (int i = 0; i < model.rowCount(); ++i) {
QString name = model.record(i).value("name").toString();
int salary = model.record(i).value("salary").toInt();
qDebug() << name << salary;
}
QSqlTableModel
进一步封装了对于数据库的查询,使用setTable()
来指定需要查询的数据表,该类只支持单表查询。setFilter()
可以设置查询条件。setSort()
指定查询结果的排序,传入的一个int
参数可以指定按照数据表的第几个字段进行排序,第二个参数指定排序方式。所有的设置必须在select()
函数之前才可以生效。
- 修改
for (int i = 0; i < model.rowCount(); ++i) {
QSqlRecord record = model.record(i);
double salary = record.value("salary").toInt();
salary *= 1.1;
record.setValue("salary", salary);
model.setRecord(i, record);
}
model.submitAll();
调用setValue()
可以修改数据条里的数据,setRecord()
可以修改数据表里的数据。调用submitAll()
可以保存所有的修改。同时也可以调用 model.setData(model.index(row, column), value)
来指定修改的那一条记录。
- 插入
model.insertRows(row, 1);
model.setData(model.index(row, 0), 1013);
model.setData(model.index(row, 1), "Peter Gordon");
model.setData(model.index(row, 2), 68500);
model.submitAll();
用法和修改类似。
- 删除
model.removeRows(row, 5);
model.submitAll();
QSqlRelationTableModel
类
该类支持把当前表的外键转换成外键表的其他字段
model->setTable("employee");
model->setRelation(2, QSqlRelation("city", "id", "name"));
model->setRelation(3, QSqlRelation("country", "id", "name"));
其他用法和QSqlTableModel
类似。
submit的策略
可以调用QSqlTableModel:: setEditStrategy
来修改model提交数据的策略。
常量 | 值 | 描述 |
---|---|---|
QSqlTableModel::OnFieldChange | 0 | 对模型的所有更改都将立即应用于数据库 |
QSqlTableModel::OnRowChange | 1 | 当用户选择不同的行时,将应用对行的更改。 |
QSqlTableModel::OnManualSubmit | 2 | 所有的更改都将缓存在模型中,直到 submitAll () 或 revertAll () 被调用。 |
5、在视图中展示数据
给view的widget设置model就可以在试图中显示数据。
QTableView *view = new QTableView;
view->setModel(model);
view->show();
对于可以读写的model,如果不希望用户修改数据可以调用setEditTriggers()
来禁止用户修改数据
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
对于查询出来的数据可以使用setHeadrData()
来重命名字段名。
model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Name"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("City"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Country"));