背景知识:
Qt SQL的API分为不同层:
- 驱动层
驱动层 对于QT是基于C++来实现的框架,该层主要包括QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorbase、QSqlDriverPlugin and QSqlResult。这一层提供了特定数据库和SQL API层之间的底层桥梁。
- SQL API层
SQL API层 对于SQL API 层提供了数据库的访问相关类,其中,QSqlDataBase类进行连接,QSqlQuery完成数据库的交互。除此之外,还有QSqlError、QSqlField、QSqlIndex and QSqlRecord类。
- 用户接口层
用户接口层 用户接口层的几个类实现将数据库中的数据链接到窗口部件上,这些类是使用模型/视图框架实现的,他们是更高层次的抽象,主要包括QSqlQureyModel,QSqlTableModel and QSqlRelationalTableModel。
用户接口层的类使用模型/视图框架实现了将数据库中的数据链接到窗口控件上。
QTableView是常用的内容显示视图组件。数据模型类有:QSqlQueryModel 、QSqlTableModel 、QSqlRelationalTableModel
QSqlQueryModel :通过设置SELECT语句查询获取内容,Model数据是只读的,不能进行编辑。
QSqlTableModel : 直接设置一个数据表的名称,可以获取数据表的全部记录,结果是可编辑的。
QSqlRelationalTableModel: 编辑一个数据表,将代码字段通过关系与代码表关联,将代码字段的编辑转换为直观的内容选择编辑。
QSqlQueryModel
QSqlQueryModel :QSqlQueryModel是QSqlTableModel的父类。通过设置SELECT语句查询获取内容,Model数据是只读的,不能进行编辑。意味着不能再如tableview进行编辑修改数据,也不会有代理出现。
所以需要结合QSqlQuery一起使用。
那如果想编辑修改,只能通过弹出一个对话框的,添加控件的方式进行修改,再结合QSqlQuery操作。
- tableview显示数据,即对模型设置。那调用setQuery函数即可。
- 修改某条数据,把tableview的数据传到对话框,有2种办法。第一种:直接获取模型某行的数据(调用record函数),第二种:通过QSqlQuery执行sql查询语句。然后把修改后的数据通过QSqlQuery语句更新到数据库,数据模型再重新查询语句(调用QSqlQueryModel 类的query().exec()因为前面执行过一次查询或者setQuery),则会自动更新到tableview.
常用的api函数
//清除数据模型,释放所有获得的数据
virtual void clear();
//返回当前关联的QSqlQuery()对象
QSqlQuery query() const;
//设置一个QSqlQuery对象,获取数据
void setQuery(const QSqlQuery &query);
void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase());
//返回一个空记录,包含当前查询的字段信息
QSqlRecord record() const;
//返回行号为row的记录
QSqlRecord record(int row) const;
QSqlQuery
执行任意的SQL语句,直接和数据库打交道,可进行insert、delete、update等操作
优点:
- 完全自由编写和执行sql语句,可做任意的查询和操作
- 性能高,可直接操作数据库结构
- 支持事务
缺点:
- 需要自行处理结果集,较复杂
- 不支持直接编辑。需要自行在程序中处理添加、修改和删除。
//绑定sql语句
bool prepare(const QString& query);
void bindValue(const QString& placeholder, const QVariant& val,
QSql::ParamType type = QSql::In);
void bindValue(int pos, const QVariant& val, QSql::ParamType type = QSql::In);
void addBindValue(const QVariant& val, QSql::ParamType type = QSql::In);
//执行sql语句
bool exec();
bool exec(const QString& query);
//执行sql语句后,结果的遍历
bool next();
bool previous();
bool first();
bool last();
//某条数据的字段的值
QVariant value(int i) const;
QVariant value(const QString& name) const;
例如
QSqlQuery query = QSqlQuery(db);
query.clear();
bool ret = query.prepare("select * from User WHERE userId=?");
query.bindValue(0,strid);
bool ok = query.exec();
while(query.next())//每一行的数据
{
QString qvalue = query.value("userId").toString();
QString qvalue1 = query.value("username").toString();
qDebug() << qvalue << qvalue1 <<endl;
}
//执行sql语句后,当前位置的结果,位置由bool next();bool previous();
bool first();bool last();决定。从而知道是某行的数据
QSqlRecord record() const;
//执行sql语句
void setQuery(const QSqlQuery &query);
void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase());
//返回当前关联的QSqlQuery 对象
QSqlQuery query() const;
不带参数:QSqlQuery query = QSqlQuery(db);query.clear();bool ret = query.exec("CREATE TABLE User ( userId VARCHAR(20) PRIMARY KEY, username VARCHAR(20) )");
query.clear();带参数:QSqlQuery query = QSqlQuery(db);bool bret = false;query.clear();bret = query.prepare("insert into User (userId,username) values (?,?)");query.addBindValue(strid);query.addBindValue(strname);bret = query.exec();
query.clear();
或者QSqlQuery query = QSqlQuery(db);query.clear();query.prepare("delete from User WHERE userId=?");query.bindValue(0,strid);bool ok = query.exec();query.clear();
例子
- 数据库使用SQLite数据库,格式为.db3
- 模型使用QSqlQueryModel ,视图使用QTableView
打开数据库
void MainWindow::on_actOpenDB_triggered()
{QString aFile=QFileDialog::getOpenFileName(this,"选择数据库文件","","SQL Lite数据库(*.db *.db3)");if (aFile.isEmpty())return;//打开数据库DB=QSqlDatabase::addDatabase("QSQLITE"); //添加 SQL LITE数据库驱动DB.setDatabaseName(aFile); //设置数据库名称if (!DB.open()) //打开数据库{QMessageBox::warning(this, "错误", "打开数据库失败",QMessageBox::Ok,QMessageBox::NoButton);return;}//打开数据表openTable();
}
打开数据表,查询数据表的数据到模型
void MainWindow::openTable()
{//打开数据表qryModel=new QSqlQueryModel(this);theSelection=new QItemSelectionModel(qryModel);qryModel->setQuery("SELECT empNo, Name, Gender, Height, Birthday, Mobile, Province, City, Department, "" Education, Salary FROM employee order by empNo");if (qryModel->lastError().isValid()){QMessageBox::information(this, "错误", "数据表查询错误,错误信息\n"+qryModel->lastError().text(),QMessageBox::Ok,QMessageBox::NoButton);return;}qryModel->setHeaderData(0,Qt::Horizontal,"工号");qryModel->setHeaderData(1,Qt::Horizontal,"姓名");qryModel->setHeaderData(2,Qt::Horizontal,"性别");qryModel->setHeaderData(3,Qt::Horizontal,"身高");qryModel->setHeaderData(4,Qt::Horizontal,"出生日期");qryModel->setHeaderData(5,Qt::Horizontal,"手机");qryModel->setHeaderData(6,Qt::Horizontal,"省份");qryModel->setHeaderData(7,Qt::Horizontal,"城市");qryModel->setHeaderData(8,Qt::Horizontal,"部门");qryModel->setHeaderData(9,Qt::Horizontal,"学历");qryModel->setHeaderData(10,Qt::Horizontal,"工资");ui->tableView->setModel(qryModel);ui->tableView->setSelectionModel(theSelection);
// ui->tableView->resizeColumnsToContents();
// ui->tableView->horizontalHeader()->setStretchLastSection(true);选择行变化时
// connect(theSelection,SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
// this,SLOT(on_currentRowChanged(QModelIndex,QModelIndex)));ui->actOpenDB->setEnabled(false);ui->actRecInsert->setEnabled(true);ui->actRecDelete->setEnabled(true);ui->actRecEdit->setEnabled(true);ui->actScan->setEnabled(true);
}
添加数据,先使用QSqlQuery 添加到数据库,再从数据库查询到model,最后显示到view
void MainWindow::on_actRecInsert_triggered()
{//插入记录
// QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
// int curRecNo=theSelection->currentIndex().row();// QSqlRecord curRec=qryModel->record(curRecNo); //获取当前记录
// int empNo=curRec.value("EmpNo").toInt();QSqlQuery query;query.exec("select * from employee where EmpNo =-1"); //实际不查询出记录,只查询字段信息
// query.prepare("select * from employee where EmpNo = :ID");
// query.bindValue(":ID",empNo);
// query.exec();
// query.first();// if (!query.isValid())
// return;QSqlRecord curRec=query.record();//获取当前记录,实际为空记录curRec.setValue("EmpNo",qryModel->rowCount()+3000);WDialogData *dataDialog=new WDialogData(this);Qt::WindowFlags flags=dataDialog->windowFlags();dataDialog->setWindowFlags(flags | Qt::MSWindowsFixedSizeDialogHint); //设置对话框固定大小dataDialog->setInsertRecord(curRec); //插入记录int ret=dataDialog->exec();// 以模态方式显示对话框if (ret==QDialog::Accepted) //OK键被按下{QSqlRecord recData=dataDialog->getRecordData();query.prepare("INSERT INTO employee (EmpNo,Name,Gender,Height,Birthday,Mobile,Province,"" City,Department,Education,Salary,Memo,Photo) "" VALUES(:EmpNo,:Name, :Gender,:Height,:Birthday,:Mobile,:Province,"" :City,:Department,:Education,:Salary,:Memo,:Photo)");query.bindValue(":EmpNo",recData.value("EmpNo"));query.bindValue(":Name",recData.value("Name"));query.bindValue(":Gender",recData.value("Gender"));query.bindValue(":Height",recData.value("Height"));query.bindValue(":Birthday",recData.value("Birthday"));query.bindValue(":Mobile",recData.value("Mobile"));query.bindValue(":Province",recData.value("Province"));query.bindValue(":City",recData.value("City"));query.bindValue(":Department",recData.value("Department"));query.bindValue(":Education",recData.value("Education"));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(),QMessageBox::Ok,QMessageBox::NoButton);else //插入,删除记录后需要重新设置SQL语句查询
// qryModel->setQuery("SELECT empNo, Name, Gender, Height, Birthday, Mobile, Province, City,"
// "Department,Education, Salary FROM employee order by empNo"); //设置数据表qryModel->query().exec();//数据模型重新查询数据,更新tableView显示}delete dataDialog;
}
删除数据
void MainWindow::on_actRecDelete_triggered()
{//删除当前记录int curRecNo=theSelection->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(),QMessageBox::Ok,QMessageBox::NoButton);else //插入,删除记录后需要重新设置SQL语句查询qryModel->query().exec();ui->tableView->update();
// qryModel->setQuery("SELECT empNo, Name, Gender, Height, Birthday, Mobile, Province, City,"
// "Department,Education, Salary FROM employee order by empNo"); //设置数据表
}
修改数据
void MainWindow::on_actRecEdit_triggered()
{//编辑当前记录
// QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引int curRecNo=theSelection->currentIndex().row();updateRecord(curRecNo);
}void MainWindow::updateRecord(int recNo)
{ //更新一条记录QSqlRecord curRec=qryModel->record(recNo); //获取当前记录int empNo=curRec.value("EmpNo").toInt();//获取EmpNoQSqlQuery query; //查询出当前记录的所有字段query.prepare("select * from employee where EmpNo = :ID");query.bindValue(":ID",empNo);query.exec();query.first();if (!query.isValid()) //是否为有效记录return;curRec=query.record();//获取当前记录的数据WDialogData *dataDialog=new WDialogData(this); //创建对话框Qt::WindowFlags flags=dataDialog->windowFlags();dataDialog->setWindowFlags(flags | Qt::MSWindowsFixedSizeDialogHint); //设置对话框固定大小dataDialog->setUpdateRecord(curRec);//调用对话框函数更新数据和界面int ret=dataDialog->exec();// 以模态方式显示对话框if (ret==QDialog::Accepted) //OK键被按下{QSqlRecord recData=dataDialog->getRecordData(); //获得对话框返回的记录query.prepare("update employee set Name=:Name, Gender=:Gender,Height=:Height,"" Birthday=:Birthday, Mobile=:Mobile, Province=:Province,"" City=:City, Department=:Department, Education=:Education,"" Salary=:Salary, Memo=:Memo, Photo=:Photo "" where EmpNo = :ID");query.bindValue(":Name",recData.value("Name"));query.bindValue(":Gender",recData.value("Gender"));query.bindValue(":Height",recData.value("Height"));query.bindValue(":Birthday",recData.value("Birthday"));query.bindValue(":Mobile",recData.value("Mobile"));query.bindValue(":Province",recData.value("Province"));query.bindValue(":City",recData.value("City"));query.bindValue(":Department",recData.value("Department"));query.bindValue(":Education",recData.value("Education"));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(),QMessageBox::Ok,QMessageBox::NoButton);elseqryModel->query().exec();//数据模型重新查询数据,更新}delete dataDialog;
}
查询数据
查询数据,直接调用setQuery函数即可,这样view只显示查询的字段
void MainWindow::on_actScan_triggered()
{qryModel->setQuery("select empNo, Name from employee where empNo='1001'");
}
或者是好像属于执行了2次查询了
void MainWindow::on_actScan_triggered()
{QSqlQuery qryEmpList(DB); //员工工资信息列表bool bok = qryEmpList.prepare("SELECT empNo, Name, Gender FROM employee where empNo=?");qryEmpList.bindValue(0,"1001");bok = qryEmpList.exec();//这步必须执行,qryModel->setQuery(qryEmpList);}