一、Qt SQL模块概述
1、Qt SQL模块提供数据库编程的支持。
2、Qt支持多种常见的数据库,提供这些数据库的驱动。
驱动名 数据库
QDB2 IBM DB2 (version 7.1 and above)
QMYSQL MySQL
QOCI Oracle Call Interface Driver (version 12.1 and above)
QODBC Open Database Connectivity (ODBC) - Microsoft SQL Server and other ODBC-compliant databases
QPSQL PostgreSQL (versions 7.3 and above)
QSQLITE SQLite version 3
3、Qt SQL模块包括多个类,分为不同的层:
(1)、驱动层
主要包括QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorBase、QSqlDriverPlugin、QSqlResult等,这一层提供了特定数据库和SQL API层之间的底层桥梁。
(2)、SQL API层
主要提供了数据库的访问相关类,QSqlDatabase类进行连接,QSqlQuery类与数据库交互,还有QSqlError、QSqlField、QSqlIndex、QSqlRecord类等。
(3)、用户接口层
实现将数据库中的数据链接到窗口部件上,这些类使用模型Model/视图View框架,主要包括QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel类等。
4、在项目中进行数据库编程的流程:
(1)、在项目配置文件.pro中增加语句 Qt += sql。
(2)、在头文件或源文件中增加包含语句 #include
此句会将Qt SQL模块中的所有类都包含进去; 若只使用其中某些类,为避免冗余可以单独包含某个类。
(3)、查看Qt对数据库类型的支持。
QStringList drivers = QSqlDatabase::drivers();
foreach (QString driver, drivers)
{
qDebug() << driver;
}
(4)、连接与打开数据库。
//该类对象就相当于一个数据库,所以在一个工程中,通常只使用一个对象即可。
QSqlDatabase db;
//加载SQLite数据库驱动
db = QSqlDatabase::addDatabase("QSQLITE");
//以.db为后缀,设置数据库文件。 运行时会在当前工作目录下生成mydatabase.db。
db.setDatabaseName("mydatabase.db");
//如果想要进一步操作数据库,那就必须进行数据库连接打开操作
if(db.open())
{
qDebug() << "Database open successed!";
}
else
{
qDebug() << "Database open failed: " << db.lastError() ;
}
(5)、访问数据库。
(6)、关闭数据库。
5、数据库有网络型数据库和单机型数据库。
(1)、网络型数据库包括: MySQL、Oracle、Microsoft SQL Server等。
(2)、单机型数据库包括: SQLite、MySQL、Microsoft Access等。
二、Qt SQL模块的主要类和它们的主要函数
1、Qt SQL模块包含的主要类的功能:
类名称 功能描述
QSqlDatabase 用于建立与数据库的连接(重要!!!)
QSqlDriver 用于访问具体的SQL数据库的底层抽象类
QSqlDriverCreator 为某个具体的数据库驱动提供SQL驱动的模板类
QSqlDriverCreatorBase 所有SQL驱动器的基类
QSqlDriverPlugin 用于定制QSqlDriver插件的抽象基类
QSqlError SQL数据库错误信息,可以用于访问上一次出错的信息
QSqlField 操作数据表或视图的字段的类
QSqlIndex 操作数据库的索引的类
QSqlQuery 执行各种SQL语句的类(重要!!!)
QSqlQueryModel SQL查询结果数据的只读数据模型,用于SELECT查询结果数据记录的只读显示
QSqlRecord 封装了数据记录操作的类
QSqlRelation 用于存储SQL外键信息的类,用于QSqlRelationalTableModel数据源中设置代码字段与关联数据表的关系
QSqlRelationalTableModel 用于一个数据表的可编辑的数据模型,支持代码字段的外键
QSqlResult 访问SQL数据库的抽象接口
QSqlTableModel 编辑一个单一数据表的数据模型类
QDataWidgetMapper 用于界面组件与字段之间实现映射,实现字段内容自动显示的类
2、QSqlDatabase类的主要函数接口
QSqlDatabase用于建立与数据库的连接。
对于一个工程而言,该类对象就相当于一个数据库,所以使用一个对象即可。
一般先加载需要的数据库驱动,然后如果是网络型数据库(如Oracle、MS SQL Server),需设置数据库的登录参数,如主机地址、用户名、登录密码等; 如果是单机型数据库(如SQLite),只需设置数据库文件即可。
(1)、QSqlDatabase addDatabase(const QString & type, const QString & connectionName = QLatin1String(defaultConnection))
静态函数,根据数据库驱动名type加载数据库驱动,获取数据库对象。
(2)、void setDatabaseName(const QString & name)
将连接的数据库名称设置为name,即设置数据库文件(数据库文件后缀为.db)。 要生效,必须在打开数据库之前设置名称。 (用于单机型数据库)
(3)、void setHostName(const QString & host)
将连接的主机名设置为host。 要生效,必须在打开数据库之前设置,无默认值。 (用于网络型数据库)
(4)、void setPassword(const QString & password)
将连接的密码设置为password。 要生效,必须在打开数据库之前设置,无默认值。 (用于网络型数据库)
(5)、void setUserName(const QString & name)
将连接的用户名设置为name。 要生效,必须在打开数据库之前设置,无默认值。 (用于网络型数据库)
(6)、bool open()
使用当前连接值打开数据库连接。 成功返回true,失败返回false。 可以使用lastError()函数检索错误信息。
bool open(const QString & user, const QString & password)
用指定的用户名和密码打开数据库连接。 成功返回true,失败返回false。 可以使用lastError()函数检索错误信息。
3、QSqlQuery类的主要函数接口
QSqlQuery可以执行各种SQL语句。
在打开成功的数据库中,通过QSqlQuery的函数接口对数据库内容进行访问。
访问数据库的操作主要包括: 创建表,向数据库表中插入数据、删除数据、更新数据、查询数据。
注意,表只创建一次,其它增删改查操作可以做无数次。
(1)、QSqlQuery(const QString & query = QString(), const QSqlDatabase & db = QSqlDatabase())
得到访问数据库的QSqlQuery对象。
如果query不是空字符串,则会执行query语句。
如果未指定db,则使用默认数据库,系统会自动完成跟数据库的关联。
(2)、bool exec(const QString & query)
执行SQL语句访问数据库。
如果执行成功则返回true并将查询状态设为active,否则返回false。
注意,执行查询后,查询将定位在无效记录上,必须导航到有效记录才能检索数据值(如使用next())。
(3)、int at() const
得到当前记录查询位置,第一条记录的为0
(4)、bool first()
检索结果中的第一条记录
(5)、bool last()
检索结果中的最后一条记录
(6)、bool next()
检索结果中的下一条记录。
如果无法检索记录,则结果将位于最后一条记录之后,并返回false。 如果成功检索到记录,则返回true。
(7)、QVariant value(const QString & name) const
返回当前记录中名为name的字段的值。
(8)、bool prepare(const QString & query)
提前准备好SQL语句以供执行。
查询语句可能包含绑定值的占位符,如":名称"或者"?",但在同一查询中二者不可混用。
(9)、void addBindValue(const QVariant & val, QSql::ParamType paramType = QSql::In);
使用位置值绑定,将值val添加到值列表中。
addBindValue()调用顺序决定值val绑定到哪个占位符。
(10)、void bindValue(const QString & placeholder, const QVariant & val, QSql::ParamType paramType = QSql::In)
在准备好的SQL语句中将占位符placeholder设置为值val。
4、数据获取和界面显示
(1)、Qt采用Model/View结构进行数据库内容的界面显示和编辑。
(2)、QTableView是常用的数据库内容显示视图组件。
(3)、QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel是常用的数据库操作的数据模型类。
(4)、数据库相关数据模型类的继承关系:
QAbstractTableModel
|
QSqlQueryModel
|
QSqlTableModel
|
QSqlRelationalTableModel
(5)、QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel三者区别:
a、QSqlQueryModel通过设置SQL语句查询获取数据库的内容,但是数据是只读的,不能进行编辑。
b、QSqlTableModel直接设置一个数据表的名称,可以获取数据表的全部记录,且结果是可编辑的,只要设置为QTableView的模型后就可显示和编辑数据。
c、QSqlRelationalTableModel编辑一个数据表,并且可以将代码字段通过关系与代码表关联,将代码字段的编辑转换为直观的内容选择编辑,即支持外键。
(6)、根据官方手册提示,通过重写QSqlQueryModel的setData()和flags()函数,可实现数据的编辑,见"七代码示例"。
a、bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
b、Qt::ItemFlags flags(const QModelIndex & index) const;
三、SQLite数据库简介
0、本文及后续文章,采用SQLite数据库作为实例,来研究Qt SQL数据库编程功能。
1、SQLite是一种无需服务器、无需进行任何配置的数据库。
2、所有的数据表、索引等数据库元素全都存储在一个文件里,在应用程序里使用SQLite数据库可以当作一个文件来使用。
3、SQLite是可以跨平台使用的数据库,在不同平台之间可以随意复制数据库。
4、SQLite的驱动库文件很小,包含完整功能的驱动可以小到只有500KB。
5、SQLite是一种开源免费使用的数据库。
6、SQLite Expert是SQLite数据库可视化管理工具,可以从官网下载,自带SQLite数据库驱动。
四、SQL语法:
1、创建表:
(1)、举例:
/*
* 员工表: staff
* 字段名: id name age address salary
*/
(2)、代码:
//创建表只运行一次,之后注释掉
QString sqlCreateTable("create table staff(id integer primary key autoincrement, "
"name varchar(20), "
"age int, "
"address varchar(50), "
"salary int); ");
if(!query.exec(sqlCreateTable))
{
qDebug() << "create table error" << m_db.lastError();
}
(3)、说明:
因为id是自动增长的,所以插入数据时,可以不用插入id。
QString sqlInsert("insert into staff(name, age, address, salary) values('张三', 20, '广州市天河区', 12000);");
if(!query.exec(sqlInsert))
{
qDebug() << "insert into error" << m_db.lastError();
}
2、插入操作:
(1)、语法:
INSERT INTO TABLE_NAME(column1, column2, column3, ... , columnN) VALUES(value1, value2, value3, ... , valueN);
插入记录
(2)、说明:
INSERT INTO 是关键字
TABLE_NAME 是表名
VALUES 是关键字
3、删除操作:
(1)、语法:
DELETE FROM TABLE_NAME WHERE [condition];
删除记录
(2)、说明:
DELETE FROM 是关键字
TABLE_NAME 是表名
WHERE 是条件的关键字
[condition] 是条件表达式
(3)、举例:
QString sqlDelete = QString("delete from staff where id = %1;").arg(id);
(4)、注意:
经测试,某字段若定义为自动增长,那删除某条记录后,该字段的值就会被永久删除。(如id定义为自动增长,删除id=3的记录,则以后再添加记录,id = 3不会再出现)
4、更新操作:
(1)、语法:
UPDATE TABLE_NAME SET column1 = value1, column2 = value2, ... , columnN = valueN WHERE [condition]
更新某记录的某字段值
(2)、说明:
UPDATE 是关键字
TABLE_NAME 是表名
SET 是关键字
column1 = value1 是要修改的字段名和所对应的值
WHERE 是条件的关键字
[condition] 是条件表达式
5、查询操作:
(1)、语法:
SELECT column1, column2, ... , columnN FROM TABLE_NAME;
从表中查询出指定字段对应的值
(2)、说明:
SELECT 是关键字
column1, column2, ... , columnN 是列的字段名
FROM 是关键字
TABLE_NAME 是表名
(3)、语法:
SELECT * FROM TABLE_NAME;
从表中查询出所有字段对应的值
五、代码示例QSqlQuery + Widget:
//itemfrom.h
#ifndef ITEMFROM_H
#define ITEMFROM_H
#include
namespace Ui { class itemFrom;}
class itemFrom : public QMainWindow
{
Q_OBJECT
public:
explicit itemFrom(QWidget * parent = nullptr);
~itemFrom();
void setStaffInfo(int id, QString name, int age, QString addr, int salary);
private:
Ui::itemFrom * ui = nullptr;
};
#endif // ITEMFROM_H
//itemfrom.cpp
#include "itemfrom.h"
#include "ui_itemfrom.h"
itemFrom::itemFrom(QWidget * parent) : QMainWindow(parent)
{
ui = new Ui::itemFrom();
ui->setupUi(this);
}
itemFrom::~itemFrom()
{
delete ui;
}
void itemFrom::setStaffInfo(int id, QString name, int age, QString addr, int salary)
{
ui->idLabel->setText(QString::number(id));
ui->nameLabel->setText(name);
ui->ageLabel->setText(QString::number(age));
ui->addrLabel->setText(addr);
ui->salaryLabel->setText(QString::number(salary));
}
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget * parent = nullptr);
~MainWindow();
private slots:
void on_addBtn_clicked();
void on_updateBtn_clicked();
void on_deleteBtn_clicked();
void on_selectBtn_clicked();
private:
Ui::MainWindow * ui = nullptr;
QSqlDatabase m_db;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "itemfrom.h"
#include
MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent)
{
ui = new Ui::MainWindow();
ui->setupUi(this);
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName("company.db");
if(!m_db.open())
{
qDebug() << "open database error" << m_db.lastError();
}
//创建表只运行一次,下次运行要注释掉!!!
//QSqlQuery构造不传参,会使用默认数据库sqlite
//员工表: staff
//字段名: id name age address salary
// QSqlQuery query;
// QString sqlCreateTable("create table staff(id integer primary key autoincrement, "
// "name varchar(20),"
// "age int,"
// "address varchar(50),"
// "salary int);");
// if(!query.exec(sqlCreateTable))
// {
// qDebug() << "create table error" << m_db.lastError();
// }
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_addBtn_clicked()
{
QString name = ui->nameLineEdit->text();
int age = ui->ageLineEdit->text().toInt();
QString addr = ui->addrLineEdit->text();
int salary = ui->salaryLineEdit->text().toInt();
//ID定义时是自动增长,不用从UI获取,不用插入到数据库
QSqlQuery query;
QString sqlInsert = QString("insert into staff(name, age, address, salary) values('%1', %2, '%3', %4);").arg(name).arg(age).arg(addr).arg(salary);
if(!query.exec(sqlInsert))
{
qDebug() << "insert into error" << m_db.lastError();
}
on_selectBtn_clicked();
}
void MainWindow::on_updateBtn_clicked()
{
//只更新地址
int id = ui->idLineEdit->text().toInt();
QString addr = ui->addrLineEdit->text();
QSqlQuery query;
QString sqlUpdate = QString("update staff set address = '%1' where id = %2;").arg(addr).arg(id);
if(!query.exec(sqlUpdate))
{
qDebug() << "update error" << m_db.lastError();
}
on_selectBtn_clicked();
}
void MainWindow::on_deleteBtn_clicked()
{
int id = ui->idLineEdit->text().toInt();
QSqlQuery query;
QString sqlDelete = QString("delete from staff where id = %1;").arg(id);
if(!query.exec(sqlDelete))
{
qDebug() << "delete error" << m_db.lastError();
}
on_selectBtn_clicked();
}
void MainWindow::on_selectBtn_clicked()
{
ui->listWidget->clear();
QSqlQuery query;
QString sqlSelect("select * from staff;");
if(!query.exec(sqlSelect))
{
qDebug() << "select error" << m_db.lastError();
}
while(query.next())
{
int id = query.value("id").toInt();
QString name = query.value("name").toString();
int age = query.value("age").toInt();
QString addr = query.value("address").toString();
int salary = query.value("salary").toInt();
itemFrom * staffItem = new itemFrom(ui->listWidget);
staffItem->setStaffInfo(id, name, age, addr, salary);
QListWidgetItem * item = new QListWidgetItem(ui->listWidget);
item->setSizeHint(QSize(531, 34));
ui->listWidget->addItem(item);
ui->listWidget->setItemWidget(item, staffItem);
}
}
//main.cpp
#include "mainwindow.h"
#include
int main(int argc, char * argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
QStringList drivers = QSqlDatabase::drivers();
foreach (QString driver, drivers)
{
qDebug() << driver;
}
return a.exec();
}
六、代码示例QSqlQueryModel + QTableView:
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget * parent = nullptr);
~MainWindow();
private:
Ui::MainWindow * ui = nullptr;
QSqlDatabase m_db;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent)
{
ui = new Ui::MainWindow();
ui->setupUi(this);
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName("company.db");
if(!m_db.open())
{
qDebug() << "open database error" << m_db.lastError();
}
//只创建表一次,然后注释掉
// QSqlQuery query;
// QString sqlCreateTable = QString("create table staff(id integer primary key autoincrement,"
// "name varchar(20),"
// "age integer,"
// "address varchar(50),"
// "salary integer);");
// if(!query.exec(sqlCreateTable))
// {
// qDebug() << "create table error" << m_db.lastError();
// }
//插入数据后也需要注释掉
// QString sqlInsert = QString("insert into staff(name, age, address, salary) values('zx', 8, '工业园区', 55000);");
// if(!query.exec(sqlInsert))
// {
// qDebug() << "insert error" << m_db.lastError();
// }
//创建QSqlQueryModel对象
QSqlQueryModel * model = new QSqlQueryModel;
//执行SQL语句, 将查询出来的结果转换为model对象
model->setQuery("select name, age, address, salary from staff;");
//根据需求设置表头信息
model->setHeaderData(0, Qt::Horizontal, "Name");
model->setHeaderData(1, Qt::Horizontal, "Age");
model->setHeaderData(2, Qt::Horizontal, "Address");
model->setHeaderData(3, Qt::Horizontal, "Salary");
//给UI控件设置一个模型
QTableView * view = new QTableView(ui->centralwidget);
view->setFixedSize(QSize(this->width(), this->height()));
view->setModel(model);
view->show();
}
MainWindow::~MainWindow()
{
delete ui;
}
七、代码示例自定义可编辑QSqlQueryModel + QTableView:
//editquerymodel.h
#ifndef EDITQUERYMODEL_H
#define EDITQUERYMODEL_H
#include
class EditQueryModel : public QSqlQueryModel
{
public:
explicit EditQueryModel(QObject * parent = nullptr);
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex & index) const override;
private:
//更新数据,相当于将数据库的数据查询出来,转换成model
void refresh();
//根据需求来定义修改表中内容的接口
bool setName(int useId, const QString & name);
};
#endif // EDITQUERYMODEL_H
//editquerymodel.cpp
#include "editquerymodel.h"
#include
EditQueryModel::EditQueryModel(QObject * parent) : QSqlQueryModel{parent}
{
}
bool EditQueryModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
//判断数据的合法性: 是否为有效的列
if(index.column() < 1 || index.column() > 4)
{
return false;
}
//获取列所对应的id
QModelIndex primaryIndex = QSqlQueryModel::index(index.row(), 0);
int id = this->data(primaryIndex).toInt();
//在修改前将model数据清除
this->clear();
bool bOk = false;
//根据需求,来修改对应列
if(index.column() == 1)
{
bOk = setName(id, value.toString());
}
//刷新数据
refresh();
return bOk;
}
Qt::ItemFlags EditQueryModel::flags(const QModelIndex & index) const
{
//获取单元格的编辑状态
Qt::ItemFlags flag = QSqlQueryModel::flags(index);
//给name列增加一个可编辑的标志
if(index.column() == 1)
{
flag = flag | Qt::ItemIsEditable;
}
return flag;
}
void EditQueryModel::refresh()
{
this->setQuery("select * from staff;");
//如果不重新设置名字,会默认都是数据库表的表头
this->setHeaderData(0, Qt::Horizontal, "ID");
this->setHeaderData(1, Qt::Horizontal, "Name");
this->setHeaderData(2, Qt::Horizontal, "Age");
this->setHeaderData(3, Qt::Horizontal, "Address");
this->setHeaderData(4, Qt::Horizontal, "Salary");
}
bool EditQueryModel::setName(int useId, const QString & name)
{
QSqlQuery query;
//"?"是占位符
query.prepare("update staff set name = ? where id = ?;");
//调用顺序决定绑定哪个占位符
query.addBindValue(name);
query.addBindValue(useId);
return query.exec();
}
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget * parent = nullptr);
~MainWindow();
private:
Ui::MainWindow * ui = nullptr;
QSqlDatabase m_db;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "editquerymodel.h"
MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent)
{
ui = new Ui::MainWindow();
ui->setupUi(this);
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName("company.db");
if(!m_db.open())
{
qDebug() << "open database error" << m_db.lastError();
}
//QSqlQuery query;
// QString sqlCreateTable = QString("create table staff(id integer primary key autoincrement,"
// "name varchar(20),"
// "age integer,"
// "address varchar(50),"
// "salary integer);");
// if(!query.exec(sqlCreateTable))
// {
// qDebug() << "create table error" << m_db.lastError();
// }
// QString sqlInsert = QString("insert into staff(name, age, address, salary) values('李四', 25, '广州', 15000);");
// if(!query.exec(sqlInsert))
// {
// qDebug() << "insert error" << m_db.lastError();
// }
//创建模型对象
EditQueryModel * model = new EditQueryModel();
model->setQuery("select id, name, age, address, salary from staff;");
model->setHeaderData(0, Qt::Horizontal, "ID");
model->setHeaderData(1, Qt::Horizontal, "Name");
model->setHeaderData(2, Qt::Horizontal, "Age");
model->setHeaderData(3, Qt::Horizontal, "Address");
model->setHeaderData(4, Qt::Horizontal, "Salary");
//创建表格控件
QTableView * view = new QTableView(ui->centralwidget);
view->setModel(model);
view->setFixedSize(QSize(this->width(), this->height()));
view->show();
}
MainWindow::~MainWindow()
{
delete ui;
}