Qt操作数据库

王柳  论坛元老 | 2024-8-28 08:52:58 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1008|帖子 1008|积分 3024

Qt 框架中对数据库操作提供了很好的支持,我们可以通过 Qt 提供的类非常方便地和当地或者远程数据库举行毗连。同时Qt 提供了 Qt SQL 模块,它支持与多种数据库管理系统(DBMS)举行交互,包括 SQLite、MySQL、PostgreSQL 等。众所周知,数据库是 C-S(client-server)布局的,我们要毗连的数据库属于服务器端,通过 Qt 编写的应用程序属于客户端。
一. 环境设置

在开始之前,确保你已经安装了 Qt 开发环境和相应的数据库驱动。
步骤:

  • 下载并安装 Qt。
  • 安装你须要的数据库系统,例如 MySQL 或 PostgreSQL。
  • 在 Qt 项目中添加 SQL 模块。可以在 .pro 文件中添加以下内容:
    1. QT += sql
    复制代码
    如果用的是CMakeLists,那么可以在里面加上这么些东西:
    1. find_package(xxxxxxxxxxx Sql)
    2. target_link_libraries(xxxxxxxxxxxx Qt::Sql)
    3. //xx表示省略前面的内容
    复制代码
  • 支持的驱动
    QSqlDatabase类提供了一个通过毗连访问数据库的接口。 QSqlDatabase的一个实例表现毗连。 该毗连通过一个受支持的数据库驱动程序提供对数据库的访问,该驱动程序派生自QSqlDriver。
    可以通过静态成员drivers()查看支持的全部驱动。
    1. qInfo() << QSqlDatabase::drivers();
    复制代码
    注意,此时你是看不到MySql的!!!默认是不支持MySql驱动的,须要自己编译,可以参考这篇文章。
二. 创建数据库毗连

要使用数据库,首先须要创建一个毗连。通过调用一个静态addDatabase()函数创建一个毗连,在该函数中指定要使用的驱动程序或驱动程序范例(取决于数据库的范例)和一个毗连名称。 毗连可以通过它自己的名称而不是它所毗连的数据库的名称得知。 可以有一个或多个数据库的毗连。 QSqlDatabase还支持默认毗连的概念,即未定名毗连。 要创建默认毗连,在调用addDatabase()时不要转达毗连名称参数。 随后,如果您调用任何静态成员函数而没有指定毗连名,则将假定默认毗连。 以下是毗连到 SQLite 数据库的示例:
  1. #include <QCoreApplication>
  2. #include <QtSql/QSqlDatabase>
  3. #include <QtSql/QSqlError>
  4. #include <QtSql/QSqlQuery>
  5. #include <QDebug>
  6. bool createConnection() {
  7.     QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
  8.     db.setHostName("localHost");
  9.     db.setDatabaseName("testdb");
  10.     db.setUserName("root");
  11.     db.setPassword("123456");
  12.     if (!db.open()) {
  13.         qDebug() << "Error: Unable to connect to database!";
  14.         return false;
  15.     }
  16.     qDebug() << "Database connection established.";
  17.     return true;
  18. }
  19. int main(int argc, char *argv[]) {
  20.     QCoreApplication a(argc, argv);
  21.     if (!createConnection()) {
  22.         return -1;
  23.     }
  24.     return a.exec();
  25. }
  26.    
复制代码
创建QSqlDatabase对象之后,使用setDatabaseName()、setUserName()、setPassword()、setHostName()、setPort()和setConnectOptions()设置毗连参数。 然后调用open()来激活到数据库的物理毗连(当然Sqlite只须要设置一个数据库名即可,其他的也能设置,但是无效)。 在您打开毗连之前,该毗连不可用。
定名毗连

如果创建多个数据库毗连,在调用addDatabase()时为每个毗连指定唯一的毗连名。 使用带有毗连名的database()来获取该毗连。 使用removeDatabase()和毗连名一起删除毗连。 如果您试图删除由其他QSqlDatabase对象引用的毗连,QSqlDatabase将输出一个告诫。 使用contains()查看给定的毗连名是否在毗连列表中。
  1. //命名连接
  2. auto db = QSqlDatabase::addDatabase("QSQLITE","sql_mycon_1");
  3. //根据连接名获取连接
  4. auto db = QSqlDatabase::database("sql_mycon_1");
复制代码
实用函数

一旦毗连乐成,就可以操作数据库了,可以使用QSqlDatabase的一些函数操作。
获取全部表

如果你想要查看当前数据库的全部表,可以使用QStringList tables() const函数获取。
  1. qInfo() << db.tables()
复制代码
获取表字段

知道了当前数据库有哪些表之后,就可以使用QSqlRecord record(const QString &tablename) const查看指定表的字段信息。
  1. qInfo() << db.record("emp");
复制代码
注意

QSqlDatabase是一个值类。 通过一个QSqlDatabase实例对数据库毗连所做的更改将影响表现同一连接的其他QSqlDatabase实例。 使用cloneDatabase()在现有数据库毗连的底子上创建一个独立的数据库毗连。
   告诫:强烈建议不要将QSqlDatabase的副本作为类的成员生存,因为这将阻止在关闭时正确清理实例。 如果须要访问现有的QSqlDatabase,应该使用database()来访问它。 如果你选择有一个QSqlDatabase成员变量,它须要在QCoreApplication实例被删除之前被删除,否则它可能会导致未界说的举动。
  三. 实行 SQL 语句

一旦毗连建立,可以使用 QSqlQuery 对象来实行 SQL 语句。
实行SQL语句

QSqlQuery封装了从在QSqlDatabase上实行的SQL查询中创建、导航和检索数据所涉及的功能。 它可以用于实行DML(数据操作语言)语句,如SELECT、INSERT、UPDATE和DELETE,也可以用于实行DDL(数据界说语言)语句,如CREATE TABLE。 它也可以用于实行非尺度SQL的特定数据库下令。
乐成实行的SQL语句将查询的状态设置为活动,以便isActive()返回true。 否则,查询的状态将设置为不活动。 在这两种环境下,当实行新的SQL语句时,查询将定位在无效的记录上。 在检索值之前,活动查询必须导航到有效记录(以便isValid()返回true)。
导航记录有以下功能:
  1. bool next()                                        //检索结果中的下一条记录(如果可用),并在检索的记录上定位查询。
  2. bool previous()                                //检索结果中的前一条记录(如果可用),并将查询定位于检索的记录上。  
  3. bool first()                                //检索结果中的第一条记录(如果可用),并在检索的记录上定位查询。  
  4. bool last()                                        //检索结果中的最后一条记录(如果可用),并在检索的记录上定位查询  
  5. bool seek()                                  //在位置索引处检索记录(如果可用),并在检索的记录上定位查询。 第一条记录位于位置0。
  6. int at() const                                //返回查询的当前内部位置。 如果位置无效,函数返回特殊的负值QSql::BeforeFirstRow或QSql::AfterLastRow。
复制代码
这些函数允许程序员在查询返回的记录中向前、向后移动或任意移动。 如果您只须要移动效果(例如,使用next()),您可以使用setForwardOnly(),这将节流大量的内存开销,并提高某些数据库的性能。 一旦活动查询定位在有效记录上,就可以使用value()检索数据。 全部数据都使用QVariant从SQL后端传输。
例如:
  1. QSqlQuery query("SELECT ename FROM emp",QSqlDatabase::database("如果有你指定了命名连接,则需要在这指定"));
  2. while (query.next())
  3. {
  4.       QString ename = query.value(0).toString();
  5.       QString ename = query.value("ename").toString();
  6.       qDebug()<< ename;
  7. }
复制代码
要访问查询返回的数据,请使用value(int i)。 通过从0开始转达字段在语句中的位置来访问SELECT语句返回的数据中的每个字段。 这使得使用SELECT *查询是不可取的,因为返回的字段的顺序是不确定的。
下面代码片段可以获取指定表的全部记录:
  1. QSqlQuery query("SELECT * FROM emp");
  2. auto recordCnt = query.record().count();
  3. while (query.next())
  4. {
  5.      for (int i = 0;i < recordCnt;i++)
  6.      {
  7.          QString ename = query.value(i).toString();
  8.          std::cout<< ename.toStdString()<<std::endl;
  9.      }
  10.      std::cout<<std::endl;
  11. }
复制代码
获取SQL语句查询到的记录条数(不是全部数据都会陈诉查询记录数目)
  1. int QSqlQuery::size() const
复制代码
获取SQL语句影响的行数(插入、更新、删除语句才返回影响的行数)
  1. int QSqlQuery::numRowsAffected() const
复制代码
size()用来获取SELECT语句查询到的记录条数。如果大小无法确定或数据库不支持陈诉有关查询大小的信息,则返回-1。 注意,对于非select语句(isSelect()返回false), size()将返回-1。 如果查询不是活动的(isActive()返回false),则返回-1。
numRowsAffected()返回受效果的SQL语句影响的行数,如果不能确定,则返回-1。 注意,对于SELECT语句,该值是未界说的; 使用大小()。 如果查询未激活,则返回-1。
创建表:
  1. bool createTable() {
  2.     QSqlQuery query;
  3.     QString createTableQuery = "CREATE TABLE IF NOT EXISTS people ("
  4.                                "id INTEGER PRIMARY KEY AUTOINCREMENT, "
  5.                                "name TEXT, "
  6.                                "age INTEGER)";
  7.     if (!query.exec(createTableQuery)) {
  8.         qDebug() << "Error: Failed to create table -" << query.lastError();
  9.         return false;
  10.     }
  11.     return true;
  12. }
复制代码
SQL准备语句

   经过测试SQLite不支持,Mysql支持
  QSqlQuery支持预先准备的查询实行和将参数值绑定到占位符。 有些数据库不支持这些特性,所以对于这些特性,Qt会模仿所需的功能。


  • Oracle数据库使用冒号语法识别占位符,例如:name。
  • ODBC仅仅使用?字符。
Qt支持这两种语法,只是不能将它们混合在同一个查询中。
下面我们将展示使用四种差别将参数绑定到准备语句的方法。
定名占位符


  • 使用定名占位符的定名绑定:
    1. bool insertData(const QString& name, int age) {
    2.     QSqlQuery query;
    3.     query.prepare("INSERT INTO people (name, age) VALUES (:name, :age)");
    4.     query.bindValue(":name", name);
    5.     query.bindValue(":age", age);
    6.     if (!query.exec()) {
    7.         qDebug() << "Error: Failed to insert data -" << query.lastError();
    8.         return false;
    9.     }
    10.     return true;
    11. }
    复制代码
  • 使用定名占位符的位置绑定:
    1. bool insertData(const QString& name, int age) {
    2.     QSqlQuery query;
    3.     query.prepare("INSERT INTO people (name, age) VALUES (:name, :age)");
    4.     query.bindValue(0, name);
    5.     query.bindValue(1, age);
    6.     if (!query.exec()) {
    7.         qDebug() << "Error: Failed to insert data -" << query.lastError();
    8.         return false;
    9.     }
    10.     return true;
    11. }
    复制代码

位置占位符


  • 使用位置占位符绑定值(版本1):

    1. bool insertData(const QString& name, int age) {
    2.     QSqlQuery query;
    3.     query.prepare("INSERT INTO people (name, age) VALUES (?, ?)");
    4.     query.bindValue(0, name);
    5.     query.bindValue(1, age);
    6.     if (!query.exec()) {
    7.         qDebug() << "Error: Failed to insert data -" << query.lastError();
    8.         return false;
    9.     }
    10.     return true;
    11. }
    复制代码

  • 使用位置占位符绑定值(版本2):
    1. bool insertData(const QString& name, int age) {
    2.     QSqlQuery query;
    3.     query.prepare("INSERT INTO people (name, age) VALUES (?, ?)");
    4.     query.bindValue(name);
    5.     query.bindValue(age);
    6.     if (!query.exec()) {
    7.         qDebug() << "Error: Failed to insert data -" << query.lastError();
    8.         return false;
    9.     }
    10.     return true;
    11. }
    复制代码
    别的,未绑定的参数将导致操作失败。
查询数据:

  1. void fetchData() {
  2.     QSqlQuery query("SELECT id, name, age FROM people");
  3.     while (query.next()) {
  4.         int id = query.value(0).toInt();
  5.         QString name = query.value(1).toString();
  6.         int age = query.value(2).toInt();
  7.         qDebug() << "ID:" << id << "Name:" << name << "Age:" << age;
  8.     }
  9. }
复制代码
错误处理

在数据库操作中,错误处理是至关紧张的。Qt 提供了 QSqlError 类来获取详细的错误信息。
  1. QSqlError err = db.lastError();
  2. qDebug() << "Database error:" << err.text();
复制代码
关闭数据库毗连

完成数据库操作后,应关闭毗连以释放资源。
  1. db.close();
复制代码
QSqlRecord

QSqlRecord类封装了数据库记录(通常是数据库中的表或视图中的一行)的功能和特征。 QSqlRecord支持添加和删除字段,以及设置和检索字段值。
使用setValue()可以通过名称或位置设置记录字段的值; 如果你想设置一个字段值为空,使用setNull()。 要按名称查找字段的位置,请使用indexOf(),要在特定位置查找字段的名称,请使用fieldName()。 使用field()检索给定字段的QSqlField对象。 使用contains()查看记录是否包含特定的字段名。
当生成要在数据库上实行的查询时,生成的SQL中只包含isGenerated()为true的字段。
一条记录可以有通过append()或insert()添加的字段,通过replace()替换的字段,以及通过remove()删除的字段。 可以使用clear()删除全部字段。 字段的数目由count()给出; 全部它们的值都可以使用clearValues()清除(为空)。
对SqlRecord记录字段操作不会影响到原来的表。
下面的代码片段展示了如何获取指定表的全部字段,只不过值是无效的。
  1. QSqlRecord rec = db.record("emp");
  2. for (int i=0;i<rec.count();i++)
  3. {
  4.      qDebug()<<rec.fieldName(i)<<rec.value(i);
  5. }
复制代码
完整示例代码如下:
  1. #include <QCoreApplication>#include <QtSql/QSqlDatabase>#include <QtSql/QSqlError>#include <QtSql/QSqlQuery>#include <QDebug>bool createConnection() {    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");    db.setDatabaseName("test.db");    if (!db.open()) {        qDebug() << "Error: Unable to connect to database!" << db.lastError();        return false;    }    qDebug() << "Database connection established.";    return true;}bool createTable() {
  2.     QSqlQuery query;
  3.     QString createTableQuery = "CREATE TABLE IF NOT EXISTS people ("
  4.                                "id INTEGER PRIMARY KEY AUTOINCREMENT, "
  5.                                "name TEXT, "
  6.                                "age INTEGER)";
  7.     if (!query.exec(createTableQuery)) {
  8.         qDebug() << "Error: Failed to create table -" << query.lastError();
  9.         return false;
  10.     }
  11.     return true;
  12. }
  13. bool insertData(const QString& name, int age) {
  14.     QSqlQuery query;
  15.     query.prepare("INSERT INTO people (name, age) VALUES (:name, :age)");
  16.     query.bindValue(":name", name);
  17.     query.bindValue(":age", age);
  18.     if (!query.exec()) {
  19.         qDebug() << "Error: Failed to insert data -" << query.lastError();
  20.         return false;
  21.     }
  22.     return true;
  23. }
  24. void fetchData() {
  25.     QSqlQuery query("SELECT id, name, age FROM people");
  26.     while (query.next()) {
  27.         int id = query.value(0).toInt();
  28.         QString name = query.value(1).toString();
  29.         int age = query.value(2).toInt();
  30.         qDebug() << "ID:" << id << "Name:" << name << "Age:" << age;
  31.     }
  32. }
  33. int main(int argc, char *argv[]) {    QCoreApplication a(argc, argv);    if (!createConnection()) {        return -1;    }    if (!createTable()) {        return -1;    }    if (!insertData("Alice", 30)) {        return -1;    }    if (!insertData("Bob", 25)) {        return -1;    }    fetchData();    QSqlDatabase::database().close();    return a.exec();}
复制代码
数据库模型

QSqlQueryModel

QSqlQueryModel类为SQL效果集提供了只读数据模型
QSqlQueryModel是实行SQL语句和遍历效果集的高级接口。 它构建在较低级别的QSqlQuery之上,可用于向视图类(如QTableView)提供数据。
  1. QSqlQueryModel *sqlQueryModel = new QSqlQueryModel(this);
  2. //sqlQueryModel->setQuery(query);
  3. sqlQueryModel->setQuery("select * from user",database);
  4. QTableView * view = new QTableView;
  5. view->setModel(sqlQueryModel);
  6. view->show();
复制代码
我们设置了模型的查询,然后设置了显示在视图头中的标签。
QSqlQueryModel也可以用于通过编程方式访问数据库,而无需将其绑定到视图:
  1. QSqlQueryModel model;
  2. model.setQuery("SELECT username,nickname FROM user");
  3. QString nickname = model.record(4).value("nickname").toInt();
复制代码
默认环境下,模型是只读的。 要使它可读可写,必须子类化它并重新实现setData()和flags()。 另一种选择是使用QSqlTableModel,它提供了基于单个数据库表的读写模型。
如果数据库不返回查询中选择的行数,模型将以递增的方式获取行。
  1. //清除模型并释放所有获得的资源。
  2. virtual void clear()
  3. //返回关于数据库上发生的最后一个错误的信息      
  4. QSqlError lastError() const
  5. //返回与此模型关联的QSqlQuery。      
  6. QSqlQuery query() const
  7. //返回包含有关当前查询字段信息的记录。 如果row是有效行的索引,则记录将使用来自该行的值填充。      
  8. QSqlRecord record(int row) const
  9. QSqlRecord record() const
  10. //执行给定数据库连接db的查询查询。 如果没有指定数据库(或无效的数据库),则使用默认连接。
  11. void setQuery(const QSqlQuery &query)
  12. void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase())
复制代码
QSqlTableModel

QSqlTableModel类为单个数据库表提供了一个可编辑的数据模型
QSqlTableModel是一个高级接口,用于从单个表中读写数据库记录。可用于向视图类(如QTableView)提供数据。 例如:
  1. //构造时指定数据库,如果使用默认连接则不需要指定
  2. QSqlTableModel* sqltableModel = new QSqlTableModel(this,database);
  3. //设置需要查询的表名
  4. sqltableModel->setTable("freecustomers");       
  5. //设置在视图中编辑值时选择的策略。  OnManualSubmit手动提交
  6. sqltableModel->setEditStrategy(QSqlTableModel::EditStrategy::OnManualSubmit);
  7. //使用指定的过滤器和排序条件,用setTable()设置的表中的数据填充模型,如果成功则返回true; 否则返回false。  
  8. sqltableModel->select();
  9. //设置表头数据
  10. sqltableModel->setHeaderData(0,Qt::Horizontal,"ID");
  11. sqltableModel->setHeaderData(1,Qt::Horizontal,"QQ");
  12. sqltableModel->setHeaderData(2,Qt::Horizontal,"电话");
  13. QTableView * tableView = new QTableView;
  14. tableView->setModel(sqltableModel);
  15. tableView->hideColumn(0);        //隐藏ID
  16. tableView->show();
复制代码
QSqlTableModel不提供对外键的直接支持。 如果要解析外键,请使用QSqlRelationalTableModel和QSqlRelationalDelegate。
其他函数

  1. //还原指定行的更改
  2. virtual void revertRow(int row)
  3. 设置在视图中编辑值时选择的策略。   
  4. virtual void setEditStrategy(QSqlTableModel::EditStrategy strategy)
  5. //将当前筛选器设置为筛选器。这个过滤器是一个没有关键字WHERE的SQL WHERE子句(例如,name='Josephine')。      
  6. virtual void setFilter(const QString &filter)
  7. //设置一条记录到指定行,记录顺序可以随意,会自动映射   
  8. bool setRecord(int row, const QSqlRecord &values)
  9. bool insertRecord(int row, const QSqlRecord &record)   
  10. //将列的排序顺序设置为order。 这不会影响当前数据,要使用新的排序顺序刷新数据,调用select()。(在查询之前设置即可)     
  11. virtual void setSort(int column, Qt::SortOrder order)
  12. //将模型操作的数据库表设置为tableName。 如果设置之后不调用select,那么将获取表的字段信息,要获取数据,必须调用select      
  13. virtual void setTable(const QString &tableName)
  14. //获取表名   
  15. QString tableName() const
  16. //获取一条空记录,只有字段名。 此函数可用于检索记录的字段名。   
  17. QSqlRecord record() const
  18. //获取指定行的记录,如果row是有效行的索引。  如果模型没有初始化,将返回一个空记录。
  19. QSqlRecord record(int row) const   
  20. //返回当前表的主键,如果表没有设置或没有主键,则返回空QSqlIndex。  
  21. QSqlIndex primaryKey() const   
  22. //如果模型包含未提交到数据库的修改值,则返回true,否则返回false。  
  23. bool isDirty(const QModelIndex &index) const
  24. bool isDirty() const   
复制代码
总结: 

Qt 提供了强盛且易用的 SQL 模块,使得与数据库的交互变得简单和高效。通过合理使用 Qt 提供的各种类和方法,我们可以方便地在应用程序中实现复杂的数据库操作。记着:遇事不决查文档

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王柳

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表