QT数据库:QSqlQuery使用
QSqlQuery 简介
QSqlQuery 是能运行任何 SQL 语句的类,如 SELECT、INSERT、UPDATE、DELETE 等 SQL
语句。所以使用 QSqlQuery 几乎能进行任何操作,例如创建数据表、修改数据表的字段定义、进行数据统计等。如果运行的是 SELECT 语句,它查询出的数据可以作为一个数据集,但是并不能作为模型/视图结构中的数据模型。
QSqlTableModel 和 QSqlQueryModel 一般用于基于记录的操作,如数据浏览和修改,而 QSqlQuery 能通过运行 SQL 语句实现对数据进行批量修改。
QSqlQuery 类中常用的函数如下表所示:
创建QSqlQuery对象(构造函数)
QSqlQuery(const QSqlDatabase &db)
QSqlQuery(const QString &query = QString(), const QSqlDatabase &db = QSqlDatabase())
创建 QSqlQuery 对象时可以传递 SQL 语句和数据库连接,如果不传递任何参数,就表示不设
置 SQL 语句,并使用默认的数据库连接。
SQL 语句的设置和运行
1、直接使用exec(QString)接口函数
QSqlQuery query;
query.exec("SELECT * FROM employee"); //查询数据
query.exec("UPDATE employee SET Salary=6000 where Gender='女'"); //更新数据
2、使用带参数的SQL语句(适合动态生成)
可以使用函数 prepare()设置带有参数的 SQL 语句,然后用函
数 bindValue()设置 SQL 语句中的各参数值,再用函数 exec()运行 SQL 语句。
QSqlQuery query;
query.prepare("SELECT empNo, Name, Gender, Salary FROM employee " " WHERE Gender =:sex AND Salary >=:salary");
query.bindValue(":sex", "男");
query.bindValue(":salary", 5000);
query.exec();
bindValue()函数的原型定义如下:
void QSqlQuery::bindValue(const QString &placeholder, const QVariant &val, QSql::ParamType paramType = QSql::In)
其中,placeholder 是 SQL 语句中用于占位的参数名;val 是参数的值;paramType 是参数类型,默认值为 QSql::In,表示传递给数据库的值。若 paramType 设置为 QSql::Out,表示该参数是一个返回值,在运行函数 exec()后,这个参数会被数据库返回的值覆盖。
还有另一种参数形式的 bindValue()函数,其原型定义如下:
void QSqlQuery::bindValue(int pos, const QVariant &val, QSql::ParamType paramType = QSql::In)
其中,参数 pos 是占位符位置序号,第一个参数位置序号为 0;val 是参数值;paramType 是参数
类型,默认值为 QSql::In。
在使用?或":参数名"作为占位符时可以用按序号设置参数的形式:
QSqlQuery query;
query.prepare("UPDATE employee SET Department=?, Salary=? WHERE EmpNo =?");
query.bindValue(0, "技术部");
query.bindValue(1, 5000);
query.bindValue(2, 2006);
query.exec();
QSqlQuery query;
query.prepare("SELECT empNo, Name, Gender, Salary FROM employee " " WHERE Gender =:sex AND Salary >=:salary");
query.bindValue(0 "男");
query.bindValue(1, 5000);
query.exec();
使用?占位时,还可以使用addBindValue()按顺序添加参数值:
QSqlQuery query;
query.prepare("UPDATE employee SET Department=?, Salary=? WHERE EmpNo =?");
query.addBindValue("技术部");
query.addBindValue(6000);
query.addBindValue(1007);
query.exec();
其中addBindValue()函数原型定义如下(无需给出占位符的序号):
void QSqlQuery::addBindValue(const QVariant &val, QSql::ParamType paramType = QSql::In)
记录移动
如果 QSqlQuery 运行的是 SELECT 语句,会返回一个数据集,并且有一个当前行。first()、
previous()、next()、last()等函数可用于进行当前行的移动。函数 record()返回当前行的记录,其函数原型定义如下:QSqlRecord QSqlQuery::record()
注意区分QSqlQueryModel也有该函数接口,record()可以带参数,不带参数返回的是字段名。
而在QSqlQuery中,record()没有任何参数,如果当前行是有效的,返回的 QSqlRecord 对象包含当前记录的数据,否则返回的是一条空记录。
使用函数 seek()可以定位到指定序号的记录,这个函数原型定义如下:
bool QSqlQuery::seek(int index, bool relative = false)
其中,参数 relative 表示绝对位置(false)或相对位置(true)。若 relative 为 false,参数 index 表示需要移动到的绝对位置,数据集的首记录位置为 0。若 relative 为 true,参数 index 表示相对于当前位置移动的行数,index 为正数表示向尾记录方向移动,index 为负数表示向首记录方向移动。
示例程序解读
主窗口构造函数
设置了tableView组件不能进行编辑(QSqlQueryModel获取的数据是只读的,因此与该模型关联的组件也应该设置成不可编辑),选择只能选择单行。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setCentralWidget(ui->tableView);
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); //不能编辑
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); //行选择
ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection); //单行选择
ui->tableView->setAlternatingRowColors(true);
}
打开数据表
创建QSqlQueryModel 类对象 qryModel,从数据表 employee 里查询除字段的数据,并作为界面上的视图组件 tableView 的数据模型。并创建选择模型,没有为currentRowChanged()信号设置槽函数,即不对记录移动进行处理。
//打开数据表
void MainWindow::selectData()
{
qryModel= new QSqlQueryModel(this);
selModel= new QItemSelectionModel(qryModel,this);
ui->tableView->setModel(qryModel);
ui->tableView->setSelectionModel(selModel);
qryModel->setQuery("SELECT empNo,Name, Gender, Birthday, Province, Department,"
"Salary FROM employee order by empNo");
if (qryModel->lastError().isValid())
{
QMessageBox::information(this, "错误", "数据表查询错误,错误信息\n"
+qryModel->lastError().text());
return;
}
QSqlRecord rec=qryModel->record(); //获取空记录,用于获取字段序号
// connect(theSelection,&QItemSelectionModel::currentRowChanged,
// this, &MainWindow::do_currentRowChanged);
//设置字段显示标题
qryModel->setHeaderData(rec.indexOf("empNo"), Qt::Horizontal, "工号");
qryModel->setHeaderData(rec.indexOf("Name"), Qt::Horizontal, "姓名");
qryModel->setHeaderData(rec.indexOf("Gender"), Qt::Horizontal, "性别");
qryModel->setHeaderData(rec.indexOf("Birthday"), Qt::Horizontal, "出生日期");
qryModel->setHeaderData(rec.indexOf("Province"), Qt::Horizontal, "省份");
qryModel->setHeaderData(rec.indexOf("Department"), Qt::Horizontal, "部门");
qryModel->setHeaderData(rec.indexOf("Salary"), Qt::Horizontal, "工资");
ui->actOpenDB->setEnabled(false);
ui->actRecInsert->setEnabled(true);
ui->actRecDelete->setEnabled(true);
ui->actRecEdit->setEnabled(true);
ui->actScan->setEnabled(true);
}
记录编辑对话框
由于主窗口中的tableView是只读的,因此为了实现对特定记录进行修改,需要创建一个记录编辑对话框,在主界面点击编辑记录会弹出来。
对话框类的内容如下:
#ifndef TDIALOGDATA_H
#define TDIALOGDATA_H
#include <QDialog>
#include <QSqlRecord>
QT_BEGIN_NAMESPACE
namespace Ui { class TDialogData; }
QT_END_NAMESPACE
class TDialogData : public QDialog
{
Q_OBJECT
private:
QSqlRecord m_record; //保存一条记录的数据
public:
TDialogData(QWidget *parent = nullptr);
~TDialogData();
void setUpdateRecord(QSqlRecord &recData); //更新记录
void setInsertRecord(QSqlRecord &recData); //插入记录
QSqlRecord getRecordData(); //获取界面输入的数据
private slots:
void on_btnClearPhoto_clicked(); //清除照片
void on_btnSetPhoto_clicked(); //设置照片
private:
Ui::TDialogData *ui;
};
#endif // TDIALOGDATA_H
QSqlRecord 类型的私有变量 m_record 用于保存一条记录的数据,插入一条记录时,程序创建对话框后要调用函数 setInsertRecord()初始化对话框的数据。编辑一条记录时,程序创建对话框后要调用函数 setUpdateRecord()初始化对话框的数据。调用对话框的程序可以在对话框的“确定”按钮被点击后,调用函数 getRecordData()获得对话框中输入的记录数据。
//编辑记录,更新记录数据到界面
void TDialogData::setUpdateRecord(QSqlRecord &recData)
{
m_record=recData; //记录存入私有变量
ui->spinEmpNo->setEnabled(false); //员工编号不允许编辑
setWindowTitle("更新记录");
//根据recData的数据更新界面显示
ui->spinEmpNo->setValue(recData.value("empNo").toInt());
ui->editName->setText(recData.value("Name").toString());
ui->comboSex->setCurrentText(recData.value("Gender").toString());
ui->editBirth->setDate(recData.value("Birthday").toDate());
ui->comboProvince->setCurrentText(recData.value("Province").toString());
ui->comboDep->setCurrentText(recData.value("Department").toString());
ui->spinSalary->setValue(recData.value("Salary").toInt());
ui->editMemo->setPlainText(recData.value("Memo").toString());
QVariant va=recData.value("Photo");
if (!va.isValid()) //图片字段内容为空
ui->LabPhoto->clear();
else //显示图片
{
QByteArray data=va.toByteArray();
QPixmap pic;
pic.loadFromData(data);
ui->LabPhoto->setPixmap(pic.scaledToWidth(ui->LabPhoto->size().width()));
}
}
//插入记录,无需更新界面显示,但是要存储recData的字段结构
void TDialogData::setInsertRecord(QSqlRecord &recData)
{
m_record=recData; //保存recData到私有变量
ui->spinEmpNo->setEnabled(true); //插入的记录,员工编号允许编辑
setWindowTitle("插入新记录");
ui->spinEmpNo->setValue(recData.value("empNo").toInt());
}
//点击"确定"按钮后,界面数据保存到记录变量mRecord
QSqlRecord TDialogData::getRecordData()
{
m_record.setValue("empNo", ui->spinEmpNo->value());
m_record.setValue("Name", ui->editName->text());
m_record.setValue("Gender", ui->comboSex->currentText());
m_record.setValue("Birthday",ui->editBirth->date());
m_record.setValue("Province", ui->comboProvince->currentText());
m_record.setValue("Department", ui->comboDep->currentText());
m_record.setValue("Salary", ui->spinSalary->value());
m_record.setValue("Memo", ui->editMemo->toPlainText());
//照片编辑时已经修改了mRecord的photo字段的值
return m_record; //以记录作为返回值
}
在主界面点击编辑或添加某条记录时,会传入QSqlRecord类型的引用recData,recData就是主界面与对话框通信的变量,主界面通过该数据向对话框传递用于显示,在对话框中更改了数据保存后会传递到主界面。
另外对于BLOB类型数据,这里为图像,需要为数据类型提供一个修改更新接口:
void TDialogData::on_btnClearPhoto_clicked()
{ //清除照片
ui->LabPhoto->clear();
m_record.setNull("Photo"); //Photo字段清空
}
void TDialogData::on_btnSetPhoto_clicked()
{//设置照片
QString aFile=QFileDialog::getOpenFileName(this,"选择图片文件","", "照片(*.jpg)");
if (aFile.isEmpty())
return;
QByteArray data;
QFile* file=new QFile(aFile);
file->open(QIODevice::ReadOnly);
data = file->readAll();
file->close();
m_record.setValue("Photo",data); //图片保存到Photo字段
QPixmap pic;
pic.loadFromData(data);
ui->LabPhoto->setPixmap(pic.scaledToWidth(ui->LabPhoto->size().width()));
}
记录编辑
在主界面中当点击编辑(或者双击记录时)时,会获取当前记录的索引,然后创建一个上述的编辑对话框对记录进行编辑,根据索引在qryModel模型中拿到该记录,通过调用对话框的接口与该记录的引用就能实现对记录的修改。这里需要注意修改QSqlRecord的recData后需要通过QSqlQuery来执行SQL语句来更新到数据库。
void MainWindow::on_actRecEdit_triggered()
{//编辑当前记录
int curRecNo=selModel->currentIndex().row();
updateRecord(curRecNo);
}
void MainWindow::on_tableView_doubleClicked(const QModelIndex &index)
{ //tableView上双击,编辑当前记录
int curRecNo=index.row();
updateRecord(curRecNo);
}
void MainWindow::updateRecord(int recNo)
{ //更新一条记录
QSqlRecord curRec=qryModel->record(recNo); //获取数据模型的一条记录
int empNo=curRec.value("EmpNo").toInt(); //获取EmpNo
QSqlQuery query(DB);
query.prepare("select * from employee where EmpNo = :ID");
query.bindValue(":ID",empNo);
query.exec();
query.first();
if (!query.isValid()) //无有效记录
return;
curRec=query.record(); //获取当前记录
TDialogData *dataDialog=new TDialogData(this); //创建对话框
Qt::WindowFlags flags=dataDialog->windowFlags();
dataDialog->setWindowFlags(flags | Qt::MSWindowsFixedSizeDialogHint); //对话框固定大小
dataDialog->setUpdateRecord(curRec); //更新对话框的数据和界面
int ret=dataDialog->exec(); //显示对话框
if (ret==QDialog::Accepted)
{
QSqlRecord recData=dataDialog->getRecordData(); //获得对话框返回的记录
query.prepare("update employee set Name=:Name, Gender=:Gender,"
" Birthday=:Birthday, Province=:Province,"
" Department=:Department, Salary=:Salary,"
" Memo=:Memo, Photo=:Photo "
" where EmpNo = :ID");
query.bindValue(":Name", recData.value("Name"));
query.bindValue(":Gender", recData.value("Gender"));
query.bindValue(":Birthday",recData.value("Birthday"));
query.bindValue(":Province",recData.value("Province"));
query.bindValue(":Department", recData.value("Department"));
query.bindValue(":Salary", recData.value("Salary"));
query.bindValue(":Memo", recData.value("Memo"));
query.bindValue(":Photo", recData.value("Photo"));
query.bindValue(":ID", empNo);
if (!query.exec())
QMessageBox::critical(this, "错误", "记录更新错误\n"+query.lastError().text());
else
{
// Qt 6.5.1中修改
QString str= qryModel->query().executedQuery(); //获取执行过的SQL语句
qryModel->setQuery(str); //重新执行SQL语句
// qryModel->query().exec(); //数据模型重新查询数据,更新tableView显示
}
}
delete dataDialog; //删除对话框
}
函数 updateRecord()的输入参数 recNo 是数据模型 qryModel 当前记录的行号。程序先获取当 前记录的 EmpNo 字段的值,即工号,然后使用一个 QSqlQuery 对象从数据表里查询出关于这个员 工的所有字段的一条记录。由于 EmpNo 是数据表 employee 的主键字段,不允许出现重复,因此 只会查询出一条记录,查询出的这条完整记录被保存到变量 curRec 中。
程序创建对话框 dataDialog,调用函数 setUpdateRecord()将保存完整记录的 curRec 传递给对话框,对话框 dataDialog 以模态方式显示。如果点击“确定”按钮,程序再通过函数 getRecordData()
获取对话框编辑后的记录数据。
程序里使用 QSqlQuery 对象运行带有参数的 UPDATE 语句更新一条记录。更新成功后,数据库
需要将数据模型 qryModel 的 SQL 语句重新运行一次,这样才可以更新 tableView 的显示内容。
插入记录
与编辑记录类似,只是插入需首先用 QSqlQuery 对象 query 运行一条 SQL 语句“select * from employee where EmpNo = -1”, 这样不会查询到记录,其目的是得到一条空记录 curRec。创建对话框 dataDialog 后,我们调用对话框的函数 setInsertRecord()及传入的空记录初始化对话框的数据用于显示。
/插入记录
void MainWindow::on_actRecInsert_triggered()
{
QSqlQuery query;
query.exec("select * from employee where EmpNo =-1"); //实际查不出记录,只查询字段信息
QSqlRecord curRec=query.record(); //获取当前记录,实际为空记录
curRec.setValue("EmpNo",qryModel->rowCount()+3000);
TDialogData *dataDialog=new TDialogData(this);
Qt::WindowFlags flags=dataDialog->windowFlags();
dataDialog->setWindowFlags(flags | Qt::MSWindowsFixedSizeDialogHint); //对话框固定大小
dataDialog->setInsertRecord(curRec); //插入记录
int ret=dataDialog->exec();
if (ret==QDialog::Accepted)
{
QSqlRecord recData=dataDialog->getRecordData();
query.prepare("INSERT INTO employee (EmpNo,Name,Gender,Birthday,Province,"
" Department,Salary,Memo,Photo) "
" VALUES(:EmpNo,:Name, :Gender,:Birthday,:Province,"
" :Department,:Salary,:Memo,:Photo)");
query.bindValue(":EmpNo",recData.value("EmpNo"));
query.bindValue(":Name",recData.value("Name"));
query.bindValue(":Gender",recData.value("Gender"));
query.bindValue(":Birthday",recData.value("Birthday"));
query.bindValue(":Province",recData.value("Province"));
query.bindValue(":Department",recData.value("Department"));
query.bindValue(":Salary",recData.value("Salary"));
query.bindValue(":Memo",recData.value("Memo"));
query.bindValue(":Photo",recData.value("Photo"));
if (!query.exec())
QMessageBox::critical(this, "错误", "插入记录错误\n"+query.lastError().text());
else //插入,删除记录后需要重新设置SQL语句查询
{
QString sqlStr=qryModel->query().executedQuery(); //执行过的SELECT语句
qryModel->setQuery(sqlStr); //重新查询数据
}
}
delete dataDialog;
}
删除记录
从数据模型 qryModel 的当前记录获取工号,然后用一个 QSqlQuery 对象运行一条 DELETE
语句删除这条记录。删除记录后需要重新设置数据模型 qryModel 的 SQL 语句并查询数据,以更
新数据集和 tableView 的显示内容。
//删除当前记录
void MainWindow::on_actRecDelete_triggered()
{
int curRecNo=selModel->currentIndex().row();
QSqlRecord curRec=qryModel->record(curRecNo); //获取当前记录
if (curRec.isEmpty()) //当前为空记录
return;
int empNo=curRec.value("EmpNo").toInt(); //获取员工编号
QSqlQuery query;
query.prepare("delete from employee where EmpNo = :ID");
query.bindValue(":ID",empNo);
if (!query.exec())
QMessageBox::critical(this, "错误", "删除记录出现错误\n"+query.lastError().text());
else //插入,删除记录后需要重新设置SQL语句查询
{
QString sqlStr=qryModel->query().executedQuery();// 执行过的SELECT语句
qryModel->setQuery(sqlStr); //重新查询数据
}
}
遍历记录
有两种方式,一种是使用了两个 QSqlQuery 变量,其中 qryEmpList 用于查询 EmpNo 和 Salary 这两个字段的全部记录,qryUpdate 用于运行一条带有参数的 UPDATE 语句,每次更新一条记录的 Salary 字段数据。qryEmpList 被设置为仅能前向移动,这样可以提高程序运行效率。
另一种方式是只需运行一条 SQL 语句,功能完全相同。
//涨工资,遍历记录
void MainWindow::on_actScan_triggered()
{
//遍历记录的方式
// QSqlQuery qryUpdate; //用于临时执行SQL语句
// qryUpdate.prepare("UPDATE employee SET Salary=:Salary WHERE EmpNo = :ID");
// QSqlQuery qryEmpList;
// qryEmpList.setForwardOnly(true); //设置为仅能前向移动,提高查询性能
// qryEmpList.exec("SELECT empNo,Salary FROM employee ORDER BY empNo");
// qryEmpList.first();
// while (qryEmpList.isValid()) //当前记录有效
// {
// int empID=qryEmpList.value("empNo").toInt();
// float salary=1000+qryEmpList.value("Salary").toFloat();
// qryUpdate.bindValue(":ID",empID);
// qryUpdate.bindValue(":Salary",salary);
// qryUpdate.exec();
// qryEmpList.next();//移动到下一条记录,
// }
// qryModel->query().exec(); //数据模型重新查询数据,更新tableView的显示
// QMessageBox::information(this, "提示", "涨工资计算完毕");
// /直接执行SQL语句的方式
QSqlQuery qryUpdate;
qryUpdate.exec("UPDATE employee SET Salary=Salary+1000");
//在Qt 6.5 中修改的代码
QString str= qryModel->query().executedQuery(); //获取执行过的SQL语句
qryModel->setQuery(str); //重新执行SQL语句
// qryModel->query().exec(); //数据模型重新查询数据,更新tableView的显示, Qt 6.2.1中的代码,用Qt 6.5编译有错误
QMessageBox::information(this, "提示", "涨工资计算完毕");
}
原文地址:https://blog.csdn.net/qq_46144191/article/details/144389962
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!