背景知识:
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();//获取EmpNo
QSqlQuery 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);
else
qryModel->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);
}