QT开发(四十九)——数据库用户接口层

    用户接口层主要包括Qt SQL模块中的QSqlQueryModelQSqlTableModelQSqlRelationalTableModel。用户接口层的类实现了将数据库中的数据链接到窗口部件上,是使用模型/视图框架实现的,是更高层次的抽象,即便不熟悉SQL也可以操作数据库。需要注意的是,在使用用户接口层的类之前必须先实例化QCoreApplication对象。

    QT中使用了自己的机制来避免使用SQL语句,提供了更简单的数据库操作及数据显示模型,分别是只读的QSqlQueryModel、操作单表的QSqlTableModel和支持外键的QSqlRelationalTableModel

一、QSqlQueryModel

1QSqlQueryModel简介

    QSqlQueryModel类为SQL结果集提供了只读的数据模型,是执行SQL语句和遍历结果集的高级接口。QSqlQueryModel基于下层的QSqlQuery构建,用于提供数据给QTableView等视图类。

QSqlQueryModel模型默认是只读的,通过QSqlQueryModel派生自定义类重写setData()和flags()函数可以实现读写,或是使用QSqlTableModel

2QSqlQueryModel成员函数

QSqlQueryModel::QSqlQueryModel(QObject *parent = Q_NULLPTR)

使用给定的parent构建一个QSqlQueryModel空对象

[virtual] void QSqlQueryModel::clear()

清除模型并释放申请的任何资源

void QSqlQueryModel::setQuery(const QSqlQuery &query)

重置模型并设置数据源为给定的queryQuery必须处于活跃状态

void QSqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase())

void QSqlQueryModel::setQuery(const QSqlQuery &query)

在给定的数据库连接db中执行查询query,如果数据库连接未指定或非法,使用默认的数据库连接

[protected] void QSqlQueryModel::setLastError(const QSqlError &error)

设置数据库发生的最后一个错误到error

[virtual] QVariant QSqlQueryModel::data(const QModelIndex &item, int role = Qt::DisplayRole) const

返回item数据项的role的值

3QSqlQueryModel实例

    QSqlQueryModel *model = new QSqlQueryModel;

    model->setQuery("select * from student");

    model->setHeaderData(0, Qt::Horizontal, tr("id"));

    model->setHeaderData(1, Qt::Horizontal, tr("name"));

    QTableView *view = new QTableView;

    view->setModel(model);

    view->show();

4、创建自定义QSqlQueryModel

    QSqlQueryModel默认是只读的,在窗口上并不能对表格中的内容进行修改。如果要按照自己的需要显示数据和修改数据,可以创建自己的模型。要想使其可读写,需要自己的类继承自QSqlQueryModel,并且重写setData()和flags()两个函数。如果要改变数据的显示,就要重写data() 函数。

Qt::ItemFlags flags(const QModelIndex &index) const;

bool setData(const QModelIndex &index, const QVariant &value, int role);

QVariant data(const QModelIndex &item, int role=Qt::DisplayRole) const;

SqlQueryModel.h文件:

#define SQLQUERYMODEL_H
 
#include 
#include 
#include 
#include 
#include 
#include 
 
class SqlQueryModel : public QSqlQueryModel
{
    Q_OBJECT
public:
    SqlQueryModel(QObject *parent = 0);
 
protected:
    Qt::ItemFlags flags(const QModelIndex &index) const;
    bool setData(const QModelIndex &index, const QVariant &value, int role);
    QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
private:
    bool setName(int studentId, const QString &name);
    void refresh();
};
 
#endif // SQLQUERYMODEL_H

SqlQueryModel.cpp文件:

#

include "SqlQueryModel.h"
#include 
 
 
SqlQueryModel::SqlQueryModel(QObject *parent):QSqlQueryModel(parent)
{
}
 
Qt::ItemFlags SqlQueryModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flags = QSqlQueryModel::flags(index);
    if (index.column() == 1) //第二个属性可更改
        flags |= Qt::ItemIsEditable;
    return flags;
}
 
bool SqlQueryModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.column() < 1 || index.column() > 2)
        return false;
    QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
    int id = data(primaryKeyIndex).toInt(); //获取id号
    clear();
    bool ok;
    if (index.column() == 1) //第二个属性可更改
        ok = setName(id, value.toString());
    refresh();
    return ok;
}
 
QVariant SqlQueryModel::data(const QModelIndex &index, int role) const
{
    QVariant value = QSqlQueryModel::data(index, role);
 
    //第一个属性的字体颜色为红色
    if (role == Qt::TextColorRole && index.column() == 0)
        return qVariantFromValue(QColor(Qt::red));
    return value;
}
 
bool SqlQueryModel::setName(int studentId, const QString &name)
{
    QSqlQuery query;
    query.prepare("update student set name = ? where id = ?");
    query.addBindValue(name);
    query.addBindValue(studentId);
    return query.exec();
}
 
void SqlQueryModel::refresh()
{
    setQuery("select * from student");
    setHeaderData(0, Qt::Horizontal, QObject::tr("id"));
    setHeaderData(1, Qt::Horizontal, QObject::tr("name"));
}

 

 

Main.cpp文件:

#include "SqlQueryModel.h"
#include 
#include 
#include 
#include 
#include 
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(":memory:");
    if(!db.open())
        return false;
    QSqlQuery query;
    query.exec("create table student (id int primary key, name vchar)");
    query.exec("insert into student values (0,'scorpio')");
    query.exec("insert into student values (1,'alex')");
    query.exec("insert into student values (2,'alexander')");
 
    QSqlQueryModel *model = new QSqlQueryModel;
    model->setQuery("select * from student");
    model->setHeaderData(0, Qt::Horizontal, QObject::tr("id"));
    model->setHeaderData(1, Qt::Horizontal, QObject::tr("name"));
    QTableView *view = new QTableView;
    view->setModel(model);
    view->setWindowTitle("QSqlQueryModel");
    view->show();
 
    SqlQueryModel *mymodel = new SqlQueryModel;
    mymodel->setQuery("select * from student");
    mymodel->setHeaderData(0, Qt::Horizontal, QObject::tr("id"));
    mymodel->setHeaderData(1, Qt::Horizontal, QObject::tr("name"));
    QTableView *view1 = new QTableView;
    view1->setWindowTitle("SqlQueryModel"); //修改窗口标题
    view1->setModel(mymodel);
    view1->show();
    return a.exec();
}

 

 

二、QSqlTableModel

1QSqlTableModel简介

    QSqlTableModel提供了一个一次只能操作单个SQL表的读写模型,它是QSqlQuery的更高层次的替代品,可以浏览和修改独立的SQL表,并且只需编写很少的代码,而且不需要了解SQL语法。

    QSqlTableModel类为单个数据库表提供了可编辑的数据模型,是从单个表中读写数据库记录的高级接口。QSqlTableModel基于下层的QSqlQuery构建,用于提供数据给QTableView等视图类。

2QSqlTableModel成员函数

QSqlTableModel::QSqlTableModel(QObject *parent = Q_NULLPTR, QSqlDatabase db = QSqlDatabase())

构建一个QSqlTableModel空对象,设置父对象为parent,数据库连接为db,如果db非法,使用默认数据库连接。

[signal] void QSqlTableModel::beforeDelete(int row)

[signal] void QSqlTableModel::beforeInsert(QSqlRecord &record)

[signal] void QSqlTableModel::beforeUpdate(int row, QSqlRecord &record)

QSqlDatabase QSqlTableModel::database() const

返回模型的数据库连接

[virtual protected] bool QSqlTableModel::deleteRowFromTable(int row)

从当前活跃的数据库表中删除给定的行

EditStrategy QSqlTableModel::editStrategy() const

返回当前的编辑策略

int QSqlTableModel::fieldIndex(const QString &fieldName) const

返回字段名为fieldName的字段的索引,如果没有相应字段返回-1

QString QSqlTableModel::filter() const

返回当前设置的过滤器

bool QSqlTableModel::insertRecord(int row, const QSqlRecord &record)

row行插入一条记录record,如果row为负数,追加到尾部

bool QSqlTableModel::isDirty(const QModelIndex &index) const

如果index位置的值是脏值,返回true。脏值是被模型修改单尚未写入数据库的值

bool QSqlTableModel::isDirty() const

如果模型包含被修改的值并且没有提交到数据库,返回true

QSqlIndex QSqlTableModel::primaryKey() const

返回当前表的主键

3QSqlTableModel使用

#include 
#include 
#include 
#include 
#include 
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(":memory:");
    if(!db.open())
        return false;
    QSqlQuery query;
    query.exec("create table student (id int primary key, name vchar)");
    query.exec("insert into student values (0,'scorpio')");
    query.exec("insert into student values (1,'alex')");
    query.exec("insert into student values (2,'alexander')");
 
    QSqlTableModel *model = new QSqlTableModel;
    model->setTable("student");
    model->setEditStrategy(QSqlTableModel::OnManualSubmit);
    model->select(); //选取整个表的所有行
 
    QTableView *view = new QTableView;
    view->setModel(model);
    view->show();
 
    return a.exec();
}

    A、修改操作

提交修改

//开始事务操作
model->database().transaction();
if (model->submitAll())
{
    model->database().commit(); //提交
}
else
{
    model->database().rollback(); //回滚
    QMessageBox::warning(this, tr("tableModel"),
                 tr("数据库错误: %1").arg(model->lastError().text()));
}

撤销修改

model->revertAll();

    B、查询操作

QString name = lineedit->text();

//根据姓名进行筛选

model->setFilter(QString("name = '%1'").arg(name));

//显示结果

model->select();

 

model->setTable("student"); //重新关联表

model->select();//选择整个表格

    C、排序操作

升序:

model->setSort(0, Qt::AscendingOrder); //id属性即第0列,升序排列

model->select();

降序:

model->setSort(0, Qt::DescendingOrder);

model->select();

    D、删除操作

//获取选中的行

int curRow = view->currentIndex().row();

//删除该行

model->removeRow(curRow);

int ok = QMessageBox::warning(this,tr("删除当前行!"),tr("你确定删除当前行吗?"),QMessageBox::Yes,QMessageBox::No);

if(ok == QMessageBox::No)

{

    model->revertAll(); //如果不删除,则撤销

}

else

    model->submitAll(); //否则提交,在数据库中删除该行

E、插入操作

int rowNum = model->rowCount(); //获得表的行数

int id = 10;

model->insertRow(rowNum); //添加一行

model->setData(model->index(rowNum,0),id);

三、QSqlRelationalTableModel

1QSqlRelationalTableModel简介

    QSqlRelationalTableModel为了单个数据库表格提供了可编辑的数据模型。

QSqlRelationalTableModel继承自QSqlTableModel,并对其进行了扩展,提供了对外键的支持。外键就是一个表中的一个属性和其他表中的主键属性之间的一对一的映射。

2、QSqlRelationalTableModel成员函数

[virtual] void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation)

当前表中第column列为QSqlRelation(tableName,indexColumn,displayColumn)的外键

QVariant QSqlRelationalTableModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const

返回index的数据

[virtual protected] bool QSqlRelationalTableModel::insertRowIntoTable(const QSqlRecord &values)

向表中插入一条记录values

QSqlRelation QSqlRelationalTableModel::relation(int column) const

返回表格中第column列的关系

[virtual] QSqlTableModel *QSqlRelationalTableModel::relationModel(int column) const

返回外键column要访问的表的QSqlTableModel对象

[virtual] bool QSqlRelationalTableModel::setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)

设置index数据项的值为value,数据项角色为role

[virtual] void QSqlRelationalTableModel::setTable(const QString &table)

设置模型的表格为table

3、使用外键

#include
#include 
#include 
#include 
#include 
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(":memory:");
    if(!db.open())
        return false;
    QSqlQuery query;
    query.exec("create table student (id int primary key, name vchar, course int)");
    query.exec("insert into student values(1,'scorpio',1)");
    query.exec("insert into student values(2,'alex',1)");
    query.exec("insert into student values(3,'alexandar',3)");
 
    query.exec("create table course (id int primarykey, name vchar, teacher vchar)");
    query.exec("insert into course values(1,'Math','kim')");
    query.exec("insert into course values(2,'English','kim')");
    query.exec("insert into course values(3,'Computer','kim')");
 
    QSqlRelationalTableModel *model = new QSqlRelationalTableModel;
    //属性变化时写入数据库
    model->setEditStrategy(QSqlTableModel::OnFieldChange);
    model->setTable("student");
    //将student表的第2个属性设为course表的id属性的外键,
    //并将其显示为course表的name属性的值
    model->setRelation(2,QSqlRelation("course","id","name"));
    model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
    model->setHeaderData(1, Qt::Horizontal, QObject::tr("Name"));
    model->setHeaderData(2, Qt::Horizontal, QObject::tr("Course"));
    model->select();
    QTableView *view = new QTableView;
    view->setModel(model);
    view->show();
 
    return a.exec();
}

4、使用委托

为了控制表中某些数据项的数据的可选类型或是数据内容,可以使用委托。QT提供了QSqlRelationalDelegate委托类,可以为QSqlRelationalTableModel显示和编辑数据。不同于默认的委托,QSqlRelationalDelegate为外键提供了字段的下拉框。

view->setItemDelegate(new QSqlRelationalDelegate(view));