您的位置:首页 > 编程语言 > Qt开发

QT与数据库连接

2016-05-26 16:08 459 查看
QSql 模块提供了访问 SQL 数据库的接口,这些接口独立于操作系统,独立于数据库系统。Qt 中有很多支持这个接口的类,这些类型通过 Qt 的 model/view 架构将数据库与用户界面结合起来。

数据库连接由 QSqlDatabase 类对象表示,Qt 通过驱动与不同的数据库 API 通讯。Qt Desktop Edition 版本中的 drivers 包括:

QDB2,IBM DB2 7.1 以上的数据库版本

QIBASE,Borland InterBase

QMYSQL,MySQL

QOCI,Qracel

QODBC,ODBC,包括 Microsoft SQL Server

QPSQL,PostgreSQL 7.3 及以上版本

QSQLITE,SQLite 3

QSQLITE2,SQLite 2

QTDS,Sybase Adaptive Server

Qt Open Sourcee Edition 版本只提供部分 drivers。在配置 Qt 时,用户可以在 Qt 程序中包含 SQL drivers,也可以将这些 drivers 作为 glugins 构建。

注意,在开发之前,需要保证具有数据库的驱动包,如果没有需要自行编译,不过据我所知好像5.2以后就自带了好几个数据库的驱动包,可到mingw/plugins/sqldriver文件夹下查看。

1. 建立数据库连接

在进行 SQL 查询之前,需要与数据库建立连接。通常,在程序执行前用户需要调用创建连接的函数以建立与数据库的连接。例如:

bool createConnection( )

{

QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("localhost");
db.setDatabaseName("root");
db.setUserName("swxc");
db.setPassword("****");
if (!db.open()) {
QMessageBox::critical(0, QObject::tr("Database Error"),
db.lastError().text());
return false;
}
return true;

}


QSqlDatabase::addDatabase( ) 用于创建 QSqlDatabase 对象。它的第一个参数指明了访问数据库的 driver;接下来,分别设置数据库的主机名,数据库名,用户名,密码;最后,打开数据库连接。

通常,可以在 main( ) 函数中调用 createConnection( ) :

int main( int argc, char *argv[ ] )

{

QApplication app(argc, argv);
if (!createConnection())
return 1;
...
return app.exec();

}


2. 执行 SELECT 命令

一旦建立连接,用户就可以使用 QSqlQuery 类型执行 SQL 命令。例如下面示例如何使用 SELECT 命令:

QSqlQuery query;

query.exec("SELECT title, year FROM cd WHERE year >= 2016") ;


或者直接写为:

QSqlQuery query("SELECT title, year FROM cd WHERE year >= 2016");


然后可以处理查询结果:

while (query.next())

{
QString title = query.value(0).toString();
int year = query.value(1).toInt();
std::cerr << qPrintable(title) << ": " << year << std::endl;
}


第一次调用 query.next( ) 时,查询记录指针指向第一条记录;接下来每调用一次 next( ),指针向后移一条记录,直到指针指向记录的尾端(尾端是最后一条记录的下一个位置),这时 next( ) 返回 false。

query.value( ) 函数返回记录中某个域的值,这个返回值的类型是 QVariant。像上面的例子中,查询记录中包括两个域:title 和 year,这些域从 0 开始编号。

除了上面的一些函数,像 first( ),last( ),previous( ),seek( ) 等函数也可用于对查询结果的记录指针进行偏移操作。但是对于访问大型的数据库时,通常的方法是在调用 exec( ) 之前,调用 QSqlQuery : : setForwardOnly(true) 进行指针偏移优化,然后在操作结果记录时只使用 next( ) 进行偏移。

在使用一个创建出来的 query 前,还可以调用 isActive( ) 查看是否有错误,如果没有错误就可以用上面的函数对结果进行查询操作:

if (!query.isActive())
QMessageBox::warning(this, tr("Database Error"),query.lastError().text());


3. 执行 INSERT 命令

QSqlQuery query("INSERT INTO cd (id, artistid, title, year) "

"VALUES (20, 10, 'Living in America', 2016)");


也可以使用点位符替代记录中的值:

QSqlQuery query;
query.prepare("INSERT INTO cd (id, artistid, title, year) "
"VALUES (:id, :artistid, :title, :year)");
query.bindValue(":id", 20);
query.bindValue(":artistid", 10);
query.bindValue(":title", "Living in America");
query.bindValue(":year", 2016);
query.exec();


上面的点位符是 Oracle 风格,不同的数据库管理系统有不同的点位符格式,下面的点位符具有 ODBC 风格:

QSqlQuery query;
query.prepare("INSERT INTO cd (id, artistid, title, year) "
"VALUES (?, ?, ?, ?)");
query.addBindValue(20);
query.addBindValue(10);
query.addBindValue("Living in America");
query.addBindValue(2016);
query.exec();


执行 exec( ) 后,可以再调用 addBindValue 或者 bindValue 函数为点位符绑定新的值继续进行操作。

4. SQL transaction(互动,交易,事务)

Qt 可以在支持交易功能的数据库上进行这个操作。交易作用在 QSqlDatabase 对象上,完成交易需要调用 commit( ) 或者是 rollback( )。下面的示例展示在一个交易中,执行查询和插入的操作:

QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("SELECT id FROM artist WHERE name = 'Gluecifer'");
if (query.next()) {
int artistId = query.value(0).toInt();
query.exec("INSERT INTO cd (id, artistid, title, year) "
"VALUES (21, " + QString::number(artistId)
+ ", 'Riding the Tiger', 2016)");
}
QSqlDatabase::database().commit();


QSqlDatabase::database( ) 函数返回一个 QSqlDatabase 对象,这个对象是在 createConnection( ) 函数中创建的。如果交易创建失败,transaction( ) 函数将返回 false。hasFeature( ) 函数可以用来检查某个数据库是否支持交易操作:

QSqlDriver *driver = QSqlDatabase::database().driver();
if (driver->hasFeature(QSqlDriver::Transactions))


5. 创建多个数据库连接

QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL", "OTHER");
db.setHostName("swxctx.edu");
db.setDatabaseName("swxc");
db.setUserName("root");
db.setPassword("****");


通过传递给 database( ) driver 的名称,可以得到对应的 QSqlDatabase 对象的指针:

QSqlDatabase::database(); // 返回 QPSQL driver 的db

QSqlDatabase db = QSqlDatabase::database("OTHER"); // 返回 OTHER driver 的db


通过返回的 db 可以初始化执行 SQL 操作的 QSqlQuery 对象:

QSqlQuery query(db); // OTHER driver 关联的 db
query.exec("SELECT id FROM artist WHERE name = 'swxctx'");


6. QSqlTableModel

为了避免使用原始的 SQL 操作,Qt 提供了更高级的 QSqlTableModel 类,封装原始的 SQL 操作,提供更方便的接口。这个类可以直接操作一个数据库不需要涉及 GUI,或者它也可以作为 QListView 和 QTableView 的数据来源。下面是使用这个类进行 SELECT 操作的例子:

QSqlTableModel model;
model.setTable("cd");
model.setFilter("year >= 2016");
model.select();


这个代码相当于使用 SQL :

SELECT * FROM cd WHERE year >= 2016

遍历数据库中每一个记录的方法也很直接:

for (int i = 0; i < model.rowCount(); ++i) {
QSqlRecord record = model.record(i);
QString title = record.value("title").toString();
int year = record.value("year").toInt();
std::cerr << qPrintable(title) << ": " << year << std::endl;
}


其中 QSqlTableModel : : record( ) 取得一个给定的记录,value( ) 通过域名,或者是域的索引取得域值。在大型的数据库中操作时,建议用域的索引指定某个域。例如:

int titleIndex = model.record().indexOf("title");
int yearIndex = model.record().indexOf("year");
for (int i = 0; i < model.rowCount(); ++i) {
QSqlRecord record = model.record(i);
QString title = record.value(titleIndex).toString();
int year = record.value(yearIndex).toInt();
std::cerr << qPrintable(title) << ": " << year << std::endl;
}


插入的操作也很直接,insertRow( ) 插入一行空的记录,setData( ) 则设置此行记录中每一个域值:

QSqlTableModel model;
model.setTable("cd");
int row = 0;
model.insertRows(row, 1);
model.setData(model.index(row, 0), 110);
model.setData(model.index(row, 1), "swxctx");
model.setData(model.index(row, 2), 220);
model.setData(model.index(row, 3), 2016);
model.submitAll();


插入的行实际位于数据库中哪一行,取决于当前数据库记录的排序次序。如果插入失败,submitAll( ) 函数返回 false。

为了更新一条记录,首先从 QSqlTableModel 中找到该记录的位置。然后抽出记录,更新域值,再将记录写入数据库:

QSqlTableModel model;
model.setTable("cd");
model.setFilter("id = 12");
model.select();
if (model.rowCount() == 1) {
QSqlRecord record = model.record(0);
record.setValue("title", "Melody A.M.");
record.setValue("year", record.value("year").toInt() + 1);
model.setRecord(0, record);
model.submitAll();
}


更新记录的另一种办法是使用 setData( ):

model.select();
if (model.rowCount() == 1) {
model.setData(model.index(0, 1), "Melody A.M.");
model.setData(model.index(0, 3),
model.data(model.index(0, 3)).toInt() + 1);
model.submitAll();
}


删除一条记录的只得类似更新操作:

model.setTable("cd");
model.setFilter("id = 12");
model.select();
if (model.rowCount() == 1) {
model.removeRows(0, 1);
model.submitAll();
}


这个例子中,removeRows( ) 第一个参数是得到的 model 中的第一行(以 0 索引),第二个参数是需要删除的记录条数(这里是一条)。删除所有满足条件的记录则如下:

model.setTable("cd");
model.setFilter("year < 2015");
model.select();
if (model.rowCount() > 0) {
model.removeRows(0, model.rowCount());
model.submitAll();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: