背景知识:
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: 编辑一个数据表,将代码字段通过关系与代码表关联,将代码字段的编辑转换为直观的内容选择编辑。
QSqlTableModel
直接设置一个数据表的名称,可以获取数据表的全部记录,结果是可编辑的。实现对数据的编辑、插入、删除等操作。实现数据的排序和过滤。
一般是模型使用QSqlTableModel,视图使用QTableView。
优点:
- 简单易用,可以通过model直接操作字段数据
- 支持编辑,直接在视图中添加、修改、删除数据。
- 内置排序和过滤功能
缺点
- 只有用户单张表数据,无法执行自定义的sql语句
- 性能稍差,需要额外处理字段信息和刷新模型
- 不支持事务操作
常用的api函数
//设置数据表的名称,不立即读取数据
virtual void setTable(const QString &tableName);
//从字段获取到字段的索引号
int fieldIndex(const QString &fieldName) const;
//数据表的主索引
QSqlIndex primaryKey() const;
//设置编辑策略
virtual void setEditStrategy(EditStrategy strategy);
enum EditStrategy {OnFieldChange, OnRowChange, OnManualSubmit};
OnFieldChange:字段变化立即更新到数据库
OnRowChange:当前行变换时更新到数据库
OnManualSubmit:所有修改暂时缓存,只有手动调submitAll才保存修改到数据库
bool isDirty() const;//若有未更新到数据库的修改,返回true.
bool submitAll();//提交所有未更新的修改到数据库
void revertAll();//取消所有未更新的修改
QSqlError lastError() const;//获取错误信息
//设置模型的表头
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value,nt role = Qt::EditRole) override;
//模型的行数
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
//一条空记录获取字段名,没有值
QSqlRecord record() const;
//获取某行的记录数据
QSqlRecord record(int row) const;
//QSqlRecord的value函数,获取某个字段的值
QVariant value(const QString& name) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
//QModelIndex 类的data函数,从而得到某行某列的数据
inline QVariant data(int role = Qt::DisplayRole) const;或者
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
//QSqlRecord类设置某个字段的值
void setValue(int i, const QVariant& val);
void setValue(const QString& name, const QVariant& val);
//插入列
inline bool insertColumn(int column, const QModelIndex &parent = QModelIndex());
//在某row行前插入数据
inline bool insertRow(int row, const QModelIndex &parent = QModelIndex());
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool insertRecord(int row, const QSqlRecord &record);
//设置某行某列的数据
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
//删除某行记录
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
inline bool removeRow(int row, const QModelIndex &parent = QModelIndex());
//修改某行的值
bool setRecord(int row, const QSqlRecord &record);
//调用其基类QSqlQueryModel的setQuery函数,实现精准过滤,并显示部分字段。
void setQuery(const QSqlQuery &query);
void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase());
tabModel->QSqlQueryModel::setQuery("select empNo, Name from employee where Name='王五'");
setQuery函数相当于QSqlQuery对象执行了exec函数
//按某一列排序和排序规则,需要调用select函数才生效,实际上是sql上午ORDER BY子句
virtual void setSort(int column, Qt::SortOrder order);
//设置记录数据的过滤条件,过滤的字符串实际上为sql语句where后的字段。调用setFilter后无需调用select函数就可以立即刷新记录
virtual void setFilter(const QString &filter);
//查询数据表的数据,并使用设置的排序和过滤规则。这是查询并显示全部的字段数据
virtual bool select();
//清除释放所有数据
void clear() override;
例子
- 数据库使用SQLite数据库,格式为.db3
- 模型使用QSqlTableModel,视图使用QTableView
- 因为QSqlTableModel可编辑,可使用代理处理编辑操作。
- QSqlTableModel的数据和界面其他控件通过QDataWidgetMapper进行关联
- 这里使用的OnManualSubmit:所有修改暂时缓存,只有手动调submitAll才保存修改到数据库。
打开数据库:
void QSqlWidgetForm::on_pushButton_openDB_clicked()
{
QString strFilter = "access数据库(*.mdb);;SQL Lite数据库(*.db *.db3)";
QString strDBname = QFileDialog::getOpenFileName(this,"选择数据库文件","", strFilter);
if (strDBname.isEmpty())
return;
if(QSqlDatabase::contains("MyAccessDB"))
{
QSqlDatabase::removeDatabase("MyAccessDB");
}
QString strName;
if(strDBname.right(3) == "mdb")
{
m_db = QSqlDatabase::addDatabase("QODBC", "MyAccessDB");
strName = QString("DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};FIL={MS Access};DBQ=%1").arg(strDBname);
}
else if(strDBname.right(3) == "db3" ||strDBname.right(2) == "db" )
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
strName = strDBname;
}
m_db.setDatabaseName(strName);
if (!m_db.isOpen())
{
if (!m_db.open())
{
qDebug() << m_db.lastError().text();
return ;
}
}
QStringList tablelist = m_db.tables();
ui->comboBox_table->clear();
ui->comboBox_table->addItems(tablelist);
ui->comboBox_table->setCurrentIndex(0);
m_pTableModel = new QSqlTableModel(this,m_db);//设置数据库
}
打开数据表、设置表头、选择模型、数据映射、信号槽
void QSqlWidgetForm::on_pushButton_openTable_clicked()
{
QString strTableName = ui->comboBox_table->currentText();
m_pTableModel->clear();
m_pTableModel->setTable(strTableName); //设置数据表
m_pTableModel->setEditStrategy(QSqlTableModel::OnManualSubmit);//编辑策略
m_pTableModel->setSort(m_pTableModel->fieldIndex("id"),Qt::AscendingOrder); //增序排序
if (!(m_pTableModel->select()))//查询数据
{
return ;
}
m_pTableModel->setHeaderData(m_pTableModel->fieldIndex("id"),Qt::Horizontal,"工号");
m_pTableModel->setHeaderData(m_pTableModel->fieldIndex("name"),Qt::Horizontal,"姓名");
m_pTableModel->setHeaderData(m_pTableModel->fieldIndex("sex"),Qt::Horizontal,"性别");
ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
ui->tableView->setModel(m_pTableModel);//设置数据模型
theSelection = new QItemSelectionModel(m_pTableModel);//关联选择模型
ui->tableView->setSelectionModel(theSelection); //设置选择模型
connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
connect(theSelection,SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
this,SLOT(on_currentRowChanged(QModelIndex,QModelIndex)));
QStringList strList;
strList<<"男"<<"女";
bool isEditable=false;
delegateSex.setItems(strList,isEditable);
ui->tableView->setItemDelegateForColumn(m_pTableModel->fieldIndex("sex"),&delegateSex);
ui->pushButton_saveData->setEnabled(false); //有未保存修改时可用
ui->pushButton_CancleSave->setEnabled(false);
//创建界面组件与数据模型的字段之间的数据映射
dataMapper= new QDataWidgetMapper();
dataMapper->setModel(m_pTableModel);//设置数据模型
dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
dataMapper->addMapping(ui->lineEdit_ID,m_pTableModel->fieldIndex("id"));
dataMapper->addMapping(ui->lineEdit_Name,m_pTableModel->fieldIndex("name"));
dataMapper->addMapping(ui->lineEdit_sex,m_pTableModel->fieldIndex("sex"));
}
添加表操作
void QSqlWidgetForm::on_pushButton_AddTable_clicked()
{
QSqlQuery query = QSqlQuery(m_db);
query.clear();
QString strTablename = ui->lineEdit_TableName->text();
QString strquery =QString("CREATE TABLE %1 ( id VARCHAR(8) PRIMARY KEY, name VARCHAR(20),sex VARCHAR(4) )").arg(strTablename);
bool ret = query.exec(strquery);
query.clear();
if(!ret)
{
qDebug() << "创建表失败:" << query.lastError().text();
return;
}
ui->comboBox_table->addItem(strTablename);
ui->comboBox_table->setCurrentText(strTablename);
}
删除表
void QSqlWidgetForm::on_pushButton_DeleteTable_clicked()
{
QSqlQuery query = QSqlQuery(m_db);
QString strTablename = ui->comboBox_table->currentText();
query.clear();
bool ret = false;
QString strquery = QString("drop table %1").arg(strTablename);
ret = query.prepare(strquery);
ret = query.exec();
query.clear();
if(!ret)
{
qDebug() << "删除表失败:" << query.lastError().text();
return;
}
int nIndex = ui->comboBox_table->findText(strTablename);
ui->comboBox_table->removeItem(nIndex);
ui->comboBox_table->setCurrentText(0);
m_pTableModel->clear();
}
插入数据
void QSqlWidgetForm::on_pushButton_AddData_clicked()
{
int row = m_pTableModel->rowCount();
bool ret = m_pTableModel->insertRow(row,QModelIndex()); //在末尾添加一个记录
if(!ret)
{
qDebug() << "插入数据行失败";
return;
}
ret = m_pTableModel->setData(m_pTableModel->index(m_pTableModel->rowCount()-1,0),m_pTableModel->rowCount()); //自动生成编号
m_pTableModel->setData(m_pTableModel->index(m_pTableModel->rowCount()-1,1),"秦汉");
m_pTableModel->setData(m_pTableModel->index(m_pTableModel->rowCount()-1,2),"男");
if(!ret)
{
qDebug() << "插入数据设置数据失败";
}
}
删除数据
void QSqlWidgetForm::on_pushButton_DeleteData_clicked()
{
bool ret = m_pTableModel->removeRow(m_pTableModel->rowCount()-1); //删除最后一行
if(!ret)
{
qDebug() << "删除数据失败";
return;
}
}
保存修改
void QSqlWidgetForm::on_pushButton_saveData_clicked()
{
bool res = m_pTableModel->submitAll();
ui->pushButton_saveData->setEnabled(false); //有未保存修改时可用
ui->pushButton_CancleSave->setEnabled(false);
}
取消修改
void QSqlWidgetForm::on_pushButton_CancleSave_clicked()
{
m_pTableModel->revertAll();
ui->pushButton_saveData->setEnabled(false); //有未保存修改时可用
ui->pushButton_CancleSave->setEnabled(false);
}
排序
void QSqlWidgetForm::on_pushButton_sort_clicked()
{
m_pTableModel->setSort(0,m_bSort == true ? Qt::AscendingOrder : Qt::DescendingOrder);
m_pTableModel->select();
m_bSort = !m_bSort;
}
过滤
void QSqlWidgetForm::on_pushButton_Filter_clicked()
{
QString strsex = ui->lineEdit_sex->text();
QString strtable = ui->comboBox_table->currentText();
// QString strFilter = QString("sex='%1' ").arg(strsex);
// m_pTableModel->setFilter(strFilter);
QString strQuery =QString("select * from %1 where sex='%2'").arg(strtable).arg(strsex);
m_pTableModel->QSqlQueryModel::setQuery(strQuery,m_db);
}
或者是直接调用QSqlQueryModel的setQuery函数,这样视图就会按实际查询的字段显示
关闭数据库
void QSqlWidgetForm::on_pushButton_closeDB_clicked()
{
if (m_db.isOpen())
{
m_db.close();
}
}
其他
void QSqlWidgetForm::on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
bool b = m_pTableModel->isDirty();
ui->pushButton_saveData->setEnabled(b); //有未保存修改时可用
ui->pushButton_CancleSave->setEnabled(b);
}
void QSqlWidgetForm::on_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
bool b = m_pTableModel->isDirty();
ui->pushButton_saveData->setEnabled(b); //有未保存修改时可用
ui->pushButton_CancleSave->setEnabled(b);
dataMapper->setCurrentIndex(current.row()); //更细数据映射的行号
}