Qt:数据库编程
包含头文件 #include <QtSql>
在工程的.pro文件中加入 QT += sql
一、连接数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL","ConnA");//驱动名,连接名
db.setHostName("music");
db.setDatabaseName("qt532");
db.setUserName("mysql");
db.setPassword("mysql");
db.open(); //打开数据库连接
二、操作数据库
1、查询:
QSqlQuery query;
query.exec("SELECT title,year FROM cd WHERE year >= 1998");
//QSqlQuery query("SELECT title,year FROM cd WHERE year >= 1998");
//可以通过对查询调用isActive()来检查是否有错误发生,若没有,查询会变成“激活”状态,可用next()
if(!query.isActive())
QMessageBox::warning(this,tr("Database error"),
query.lastError().text());
while(query.next()){ //遍历查询结果
QString title = query.value(0).toString();
int year = query.value(1).toInt();
std::cerr << qPrintable(title) << ":" << year << std::endl;
}
2、插入:
QSqlQuery query("INSERT INTO cd(id,artistid,title,year)"
"VALUES (203,102,"living",2002)");
//之后调用numRowsAffected()返回受SQL语句影响的行数(error,return -1)
* 插入多条记录(或想避免将数值转换成字符串):
//Oracle风格语法及命名占位符实例:
QSqlQuery query;
query.prepare("INSERT INTO cd(id,artistid,title,year)"
"VALUES (:id, :artistid, :title, :year)");
query.bindValue(":id",203);
query.bindValue(":artistid",102);
query.bindValue(":title","Living");
query.bindValue(":year",2002);
query.exec();
//ODBC风格:
QSqlQuery query;
query.prepare("INSERT INTO cd(id,artistid,title,year)"
"VALUES (?,?,?,?)");
query.addBindValue(203);
query.addBindValue(102);
query.addBindValue("Living");
query.addBindValue(2002);
query.exec();
//exec()调用之后,可以用bindValue()或addBindValue()来赋值绑定新值,然后再次调用exec()
//并利用这些新值进行查询.
3、更新操作:
query.exec("UPDATE cd SET artistid = 100 WHERE id = 30");
4、删除操作:
query.exec("DELETE FROM cd where id = 121");
三、事务操作:
发起一个事务操作:代表数据库连接的QSqlDatabase对象调用transaction()
结束一个事务操作:commit()或rollback()
QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("SELECT id FROM artistid WHERE name = 'kaka'");
if(query.next()){
int artistId = query.value(0).toInt();
query.exec("INSERT INTO cd(id,artistid,title,year)"
"VALUES(201,"+QString::number(artistId)
+",'Ride',1997)");
}
QSqlDatabase::database().commit();
测试数据库是否支持事务:
QSqlDriver *driver = QSqlDatabase::database().driver();
if(driver->hasFeature(QSqlDriver::Transactions)) .....
每连接一次只能处理一个活动的事务,对于希望一次执行多个事务的操作,建立多连接将十分有用。
/******************************************************************************************************/
除了QSqlQuery类,Qt还提供了三种用于访问数据库的高层类:
QSqlQueryModel,QSqlTableModel,QSqlRelationalTableModel
1、QSqlQueryModel类提供了一个只读的数据模型用于表示SQL操作的结果:
QSqlQueryModel model;
model.setQuery("SELECT * FROM automobile");
for(int i = 0; i < model.rowCount(); ++i){
int carnumber = model.record(i).value("carnumber").toInt();
QString kind = model.record(i).value("kind").toString();
qDebug() << kind << carnumber;
}
2、QSqlTableModel 提供了一个可读写的数据模型用于操作单个SQL表:
QSqlTableModel model;
model.setTable("automobile");
model.setFilter("price > 120");
model.select();
//等价于SELECT * FROM automobile WHERE price > 120
for(int i = 0; i < model.rowCount(); ++i){
QString kind = model.record(i).value("kind").toString();
int carnumber = model.record(i).value("carnumber").toInt();
qDebug() << kind << carnumber;
}
//操作大数据集时,建议通过索引指定字段:
int kindIndex = model.record().indexOf("kind");
int carIndex = model.record().indexOf("carnumber");
for(int i = 0; i < model.rowCount(); ++i){
QSqlRecord record = model.record(i);
QString kind = record.value(kindIndex).toString();
int carnumber = record.value(carIndex).toInt();
qDebug() << kind << carnumber;
}
为了在数据库表中插入记录,可调用insertRow()来创建一个新的空行,然后使用setData设置每一个列的值:
QSqlTableModel model;
model.setTable("cd");
int row = 0;
model.insertRows(row,1);
model.setData(model.index(row,0),113);
model.setData(model.index(row,1),"shanghai");
model.setData(model.index(row,2),224);
model.setData(model.index(row,3),2003);
model.submitAll();
为了更新某一记录,首先必须把QSqlTableModel定位到要修改的记录上(如select),然后提取这条记录,
更新想改变的字段并将更改过的数据回写到数据库中:
QSqlTableModel model;
model.setTable("cd");
model.select();
if(model.rowCount() == 1){
QSqlRecord record = model.record(0);
record.setValue("title","sea");
record.setValue("year",record.value("year").toInt() + 1);
model.setRecord(0,record);
//model.removeRows(0,1); //要删除的行号,行数
model.submitAll();
}
/* if(model.rowCount() > 0){
model.removeRows(0,model.rowCount());
model.submitAll();
} */
3、QSqlRelationalTableModel是对QSqlTableMode的扩展,它提供对外键的支持:
QSqlRelationalTableModel model;
model.setTable("cars");
model.setRelation(2,QSqlRelation("factory","id","name"));
//cars表中2(factoryid)字段是factory表中id字段的外键,但显示为factory表中的name字段
/////////////////////////////////////////////////////////////////////////////////////////
这三个类均可以作为数据源在Qt的视图类中表示,如QListView,QTableView,QTreeView:
QTableView *view = new QTableView;
view->setModel(model);
view->show();
model->setHeaderData(0,Qt::Horizontal,QObject::tr("ID"));
model->setHeaderData(1,Qt::Horizontal,QObject::tr("品牌"));
model->setHeaderData(2,Qt::Horizontal,QObject::tr("产家"));
model->setHeaderData(3,Qt::Horizontal,QObject::tr("时间"));
对于可读写的模型类QSqlTableModel和QSqlRelationalTableModel,视图允许用户编辑其中的字段,
也可以禁止用户编辑:
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
可以将同一个数据模型用于多个视图,一旦用户通过其中某个视图编辑了数据模型,其他视图也会立即更新。
*虽然QSqlTableModel对单个表的操作进行了优化并提供了读写支持,但如果用户希望对任意的数据库操作结果
集进行更大程度的灵活控制,可以直接从QSqlTableModel的基类QSqlQueryModel继承,并通过重写flags()函数
和setData()函数将其变为可读写的。
//为查询模型的“字段3”增加可写属性,但保持其他字段只读属性不变:
Qt::ItemFlags EditableModel::flags(const QModelIndex &index) const{
Qt::ItemFlags flags = QSqlQueryModel::flags(index);
if(index.column() == 3)
flags |= Qt::ItemIsEditable;
return flags;
}
bool EditableModel::setData(const QModelIndex &index,const QVariant &value,int role){
if(index.column() != 3)
return false;
QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(),0);
int id = data(primaryKeyIndex).toInt();
clear();
bool ok;
ok = setName(id,value.toString());
refresh();
return ok;
} //修改了表中“字段3”的内容,然后刷新显示
bool EditableModel::setName(int personId,const QString &name){
QSqlQuery query;
query.prepare("update person set name = ? where id = ?");
query.addBindValue(name);
query.addBindValue(personId);
return query.exec();
}
/******************************************************************************************************/
//以一个控制台程序的形式,使用SQLite数据库完成大批量数据的增删改查操作,并打印其耗时。
#include <QCoreApplication>
#include <QTextCodec>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QTime>
#include <QSqlError>
#include <QDebug>
#include <QtSql>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if(!db.open()){
qDebug()<<QObject::tr("打开数据库失败");
return false;
}
QSqlQuery query;
bool bSuccess = query.exec("CREATE TABLE automobil(id INT PRIMARY KEY,"
"attribute VARCHAR,"
"type VARCHAR,"
"kind VARCHAR,"
"nation INT,"
"carnumber INT,"
"elevaltor INT,"
"distance INT,"
"oil INT,"
"temperature INT)");
if(bSuccess)
qDebug()<<QObject::tr("数据库表创建成功!\n");
else
qDebug()<<QObject::tr("数据库表创建失败\n");
QTime t;
t.start();
query.prepare("INSERT INTO automobil"
"VALUES(?,?,?,?,?,?,?,?,?,?)");
long records = 10000;
for(int i = 0; i < records; ++i){
query.bindValue(0,i);
query.bindValue(1,"奔驰");
query.bindValue(2,"宝马");
query.bindValue(3,"奥迪");
query.bindValue(4,rand()%100);
query.bindValue(5,rand()%10000);
query.bindValue(6,rand()%300);
query.bindValue(7,rand()%20000);
query.bindValue(8,rand()%52);
query.bindValue(9,rand()%100);
bSuccess = query.exec();
if(!bSuccess){
QSqlError lastError = query.lastError();
qDebug()<<lastError.driverText()<<QString(QObject::tr("插入失败"));
}
}
qDebug()<<QObject::tr("插入 %1 条记录,耗时:%2 ms").arg(records).arg(t.elapsed());
t.restart();
bSuccess = query.exec("SELECT * FROM automobil ORDER BY id DESC");
if(bSuccess){
qDebug()<<QObject::tr("排序 %1 条记录,耗时:%2 ms").arg(records).arg(t.elapsed());
}else{
qDebug()<<QObject::tr("排序失败!");
}
t.restart();
for(int i = 0; i < records; ++i){
query.clear();
query.prepare(QString("UPDATE automobil SET"
"attribute=?,type=?,kind=?,"
"nation=?,carnumber=?,elevaltor=?,"
"distance=?,oil=?,temperature=?"
"WHERE id=%1").arg(i));
query.bindValue(0,i);
query.bindValue(0,"奔驰");
query.bindValue(1,"宝马");
query.bindValue(2,"奥迪");
query.bindValue(3,rand()%100);
query.bindValue(4,rand()%10000);
query.bindValue(5,rand()%300);
query.bindValue(6,rand()%200000);
query.bindValue(7,rand()%52);
query.bindValue(8,rand()%100);
bSuccess = query.exec();
if(!bSuccess){
QSqlError lastError = query.lastError();
qDebug()<<lastError.driverText()<<QString(QObject::tr("更新失败"));
}
}
qDebug()<<QObject::tr("更新 %1 条记录,耗时 %2 ms").arg(records).arg(t.elapsed());
t.restart();
query.exec("DELETE FROM automobil WHERE id = 1500");
qDebug()<<QObject::tr("删除一条记录,耗时 %1 ms").arg(t.elapsed());
return a.exec();
}
在工程的.pro文件中加入 QT += sql
一、连接数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL","ConnA");//驱动名,连接名
db.setHostName("music");
db.setDatabaseName("qt532");
db.setUserName("mysql");
db.setPassword("mysql");
db.open(); //打开数据库连接
二、操作数据库
1、查询:
QSqlQuery query;
query.exec("SELECT title,year FROM cd WHERE year >= 1998");
//QSqlQuery query("SELECT title,year FROM cd WHERE year >= 1998");
//可以通过对查询调用isActive()来检查是否有错误发生,若没有,查询会变成“激活”状态,可用next()
if(!query.isActive())
QMessageBox::warning(this,tr("Database error"),
query.lastError().text());
while(query.next()){ //遍历查询结果
QString title = query.value(0).toString();
int year = query.value(1).toInt();
std::cerr << qPrintable(title) << ":" << year << std::endl;
}
2、插入:
QSqlQuery query("INSERT INTO cd(id,artistid,title,year)"
"VALUES (203,102,"living",2002)");
//之后调用numRowsAffected()返回受SQL语句影响的行数(error,return -1)
* 插入多条记录(或想避免将数值转换成字符串):
//Oracle风格语法及命名占位符实例:
QSqlQuery query;
query.prepare("INSERT INTO cd(id,artistid,title,year)"
"VALUES (:id, :artistid, :title, :year)");
query.bindValue(":id",203);
query.bindValue(":artistid",102);
query.bindValue(":title","Living");
query.bindValue(":year",2002);
query.exec();
//ODBC风格:
QSqlQuery query;
query.prepare("INSERT INTO cd(id,artistid,title,year)"
"VALUES (?,?,?,?)");
query.addBindValue(203);
query.addBindValue(102);
query.addBindValue("Living");
query.addBindValue(2002);
query.exec();
//exec()调用之后,可以用bindValue()或addBindValue()来赋值绑定新值,然后再次调用exec()
//并利用这些新值进行查询.
3、更新操作:
query.exec("UPDATE cd SET artistid = 100 WHERE id = 30");
4、删除操作:
query.exec("DELETE FROM cd where id = 121");
三、事务操作:
发起一个事务操作:代表数据库连接的QSqlDatabase对象调用transaction()
结束一个事务操作:commit()或rollback()
QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("SELECT id FROM artistid WHERE name = 'kaka'");
if(query.next()){
int artistId = query.value(0).toInt();
query.exec("INSERT INTO cd(id,artistid,title,year)"
"VALUES(201,"+QString::number(artistId)
+",'Ride',1997)");
}
QSqlDatabase::database().commit();
测试数据库是否支持事务:
QSqlDriver *driver = QSqlDatabase::database().driver();
if(driver->hasFeature(QSqlDriver::Transactions)) .....
每连接一次只能处理一个活动的事务,对于希望一次执行多个事务的操作,建立多连接将十分有用。
/******************************************************************************************************/
除了QSqlQuery类,Qt还提供了三种用于访问数据库的高层类:
QSqlQueryModel,QSqlTableModel,QSqlRelationalTableModel
1、QSqlQueryModel类提供了一个只读的数据模型用于表示SQL操作的结果:
QSqlQueryModel model;
model.setQuery("SELECT * FROM automobile");
for(int i = 0; i < model.rowCount(); ++i){
int carnumber = model.record(i).value("carnumber").toInt();
QString kind = model.record(i).value("kind").toString();
qDebug() << kind << carnumber;
}
2、QSqlTableModel 提供了一个可读写的数据模型用于操作单个SQL表:
QSqlTableModel model;
model.setTable("automobile");
model.setFilter("price > 120");
model.select();
//等价于SELECT * FROM automobile WHERE price > 120
for(int i = 0; i < model.rowCount(); ++i){
QString kind = model.record(i).value("kind").toString();
int carnumber = model.record(i).value("carnumber").toInt();
qDebug() << kind << carnumber;
}
//操作大数据集时,建议通过索引指定字段:
int kindIndex = model.record().indexOf("kind");
int carIndex = model.record().indexOf("carnumber");
for(int i = 0; i < model.rowCount(); ++i){
QSqlRecord record = model.record(i);
QString kind = record.value(kindIndex).toString();
int carnumber = record.value(carIndex).toInt();
qDebug() << kind << carnumber;
}
为了在数据库表中插入记录,可调用insertRow()来创建一个新的空行,然后使用setData设置每一个列的值:
QSqlTableModel model;
model.setTable("cd");
int row = 0;
model.insertRows(row,1);
model.setData(model.index(row,0),113);
model.setData(model.index(row,1),"shanghai");
model.setData(model.index(row,2),224);
model.setData(model.index(row,3),2003);
model.submitAll();
为了更新某一记录,首先必须把QSqlTableModel定位到要修改的记录上(如select),然后提取这条记录,
更新想改变的字段并将更改过的数据回写到数据库中:
QSqlTableModel model;
model.setTable("cd");
model.select();
if(model.rowCount() == 1){
QSqlRecord record = model.record(0);
record.setValue("title","sea");
record.setValue("year",record.value("year").toInt() + 1);
model.setRecord(0,record);
//model.removeRows(0,1); //要删除的行号,行数
model.submitAll();
}
/* if(model.rowCount() > 0){
model.removeRows(0,model.rowCount());
model.submitAll();
} */
3、QSqlRelationalTableModel是对QSqlTableMode的扩展,它提供对外键的支持:
QSqlRelationalTableModel model;
model.setTable("cars");
model.setRelation(2,QSqlRelation("factory","id","name"));
//cars表中2(factoryid)字段是factory表中id字段的外键,但显示为factory表中的name字段
/////////////////////////////////////////////////////////////////////////////////////////
这三个类均可以作为数据源在Qt的视图类中表示,如QListView,QTableView,QTreeView:
QTableView *view = new QTableView;
view->setModel(model);
view->show();
model->setHeaderData(0,Qt::Horizontal,QObject::tr("ID"));
model->setHeaderData(1,Qt::Horizontal,QObject::tr("品牌"));
model->setHeaderData(2,Qt::Horizontal,QObject::tr("产家"));
model->setHeaderData(3,Qt::Horizontal,QObject::tr("时间"));
对于可读写的模型类QSqlTableModel和QSqlRelationalTableModel,视图允许用户编辑其中的字段,
也可以禁止用户编辑:
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
可以将同一个数据模型用于多个视图,一旦用户通过其中某个视图编辑了数据模型,其他视图也会立即更新。
*虽然QSqlTableModel对单个表的操作进行了优化并提供了读写支持,但如果用户希望对任意的数据库操作结果
集进行更大程度的灵活控制,可以直接从QSqlTableModel的基类QSqlQueryModel继承,并通过重写flags()函数
和setData()函数将其变为可读写的。
//为查询模型的“字段3”增加可写属性,但保持其他字段只读属性不变:
Qt::ItemFlags EditableModel::flags(const QModelIndex &index) const{
Qt::ItemFlags flags = QSqlQueryModel::flags(index);
if(index.column() == 3)
flags |= Qt::ItemIsEditable;
return flags;
}
bool EditableModel::setData(const QModelIndex &index,const QVariant &value,int role){
if(index.column() != 3)
return false;
QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(),0);
int id = data(primaryKeyIndex).toInt();
clear();
bool ok;
ok = setName(id,value.toString());
refresh();
return ok;
} //修改了表中“字段3”的内容,然后刷新显示
bool EditableModel::setName(int personId,const QString &name){
QSqlQuery query;
query.prepare("update person set name = ? where id = ?");
query.addBindValue(name);
query.addBindValue(personId);
return query.exec();
}
/******************************************************************************************************/
//以一个控制台程序的形式,使用SQLite数据库完成大批量数据的增删改查操作,并打印其耗时。
#include <QCoreApplication>
#include <QTextCodec>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QTime>
#include <QSqlError>
#include <QDebug>
#include <QtSql>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if(!db.open()){
qDebug()<<QObject::tr("打开数据库失败");
return false;
}
QSqlQuery query;
bool bSuccess = query.exec("CREATE TABLE automobil(id INT PRIMARY KEY,"
"attribute VARCHAR,"
"type VARCHAR,"
"kind VARCHAR,"
"nation INT,"
"carnumber INT,"
"elevaltor INT,"
"distance INT,"
"oil INT,"
"temperature INT)");
if(bSuccess)
qDebug()<<QObject::tr("数据库表创建成功!\n");
else
qDebug()<<QObject::tr("数据库表创建失败\n");
QTime t;
t.start();
query.prepare("INSERT INTO automobil"
"VALUES(?,?,?,?,?,?,?,?,?,?)");
long records = 10000;
for(int i = 0; i < records; ++i){
query.bindValue(0,i);
query.bindValue(1,"奔驰");
query.bindValue(2,"宝马");
query.bindValue(3,"奥迪");
query.bindValue(4,rand()%100);
query.bindValue(5,rand()%10000);
query.bindValue(6,rand()%300);
query.bindValue(7,rand()%20000);
query.bindValue(8,rand()%52);
query.bindValue(9,rand()%100);
bSuccess = query.exec();
if(!bSuccess){
QSqlError lastError = query.lastError();
qDebug()<<lastError.driverText()<<QString(QObject::tr("插入失败"));
}
}
qDebug()<<QObject::tr("插入 %1 条记录,耗时:%2 ms").arg(records).arg(t.elapsed());
t.restart();
bSuccess = query.exec("SELECT * FROM automobil ORDER BY id DESC");
if(bSuccess){
qDebug()<<QObject::tr("排序 %1 条记录,耗时:%2 ms").arg(records).arg(t.elapsed());
}else{
qDebug()<<QObject::tr("排序失败!");
}
t.restart();
for(int i = 0; i < records; ++i){
query.clear();
query.prepare(QString("UPDATE automobil SET"
"attribute=?,type=?,kind=?,"
"nation=?,carnumber=?,elevaltor=?,"
"distance=?,oil=?,temperature=?"
"WHERE id=%1").arg(i));
query.bindValue(0,i);
query.bindValue(0,"奔驰");
query.bindValue(1,"宝马");
query.bindValue(2,"奥迪");
query.bindValue(3,rand()%100);
query.bindValue(4,rand()%10000);
query.bindValue(5,rand()%300);
query.bindValue(6,rand()%200000);
query.bindValue(7,rand()%52);
query.bindValue(8,rand()%100);
bSuccess = query.exec();
if(!bSuccess){
QSqlError lastError = query.lastError();
qDebug()<<lastError.driverText()<<QString(QObject::tr("更新失败"));
}
}
qDebug()<<QObject::tr("更新 %1 条记录,耗时 %2 ms").arg(records).arg(t.elapsed());
t.restart();
query.exec("DELETE FROM automobil WHERE id = 1500");
qDebug()<<QObject::tr("删除一条记录,耗时 %1 ms").arg(t.elapsed());
return a.exec();
}