• Qt中的数据库使用


    引入

    Qt中使用数据库我们要在项目文件中声明加入SQL模块,然后Qt项目就会连接到SQL模块的一些头文件
    . p r o .pro .pro文件中添加代码QT += sql

    整个 . p r o .pro .pro文件就会像下面这种状态,以后自己有一些外部连接库或者添加一些Qt的内部模块等等都是在这里面引入:

    在这里插入图片描述
    Qtsql模块由不同的Qt类支撑,类可以分为三个层次:驱动层、sql接口层、用户层

    层次描述
    驱动层实现了特定数据库与sql接口的底层桥接,包含的支持类有 Q S q l D r i v e r 、 Q S q l D r i v e r C r e a t o r < T > 、 Q S q l D r i v e r C r e a t o r B a s e 、 Q S q l D r i v e r P l u g i n 和 Q S q l R e s u l t 等 QSqlDriver、QSqlDriverCreator<T>、QSqlDriverCreatorBase、QSqlDriverPlugin和QSqlResult等 QSqlDriverQSqlDriverCreator<T>QSqlDriverCreatorBaseQSqlDriverPluginQSqlResult
    SQL接口层提供了对数据库的访问,其中QSqlDatabase类用来创建连接,QSqlQuery类可以使用SQL语句来实现与数据库的交互
    用户接口层实现了将数据库中的数据链接到窗口部件上(使用模型/视图框架实现)

    不同的数据库驱动

    Qt Sql模块使用数据库驱动插件来和不同的数据库接口进行通信,Qt默认支持的一些驱动版本:

    驱动名称数据库版本
    QSQLITE2SQLite2版本
    QSQLITESQLite3版本
    QMYSQLMysql
    QODBCsql service
    QPSQLPostgreSQL(>=7.3版本)

    Qt提供的SQLite数据库

    不同的数据库驱动是用来连接不同的数据库的,而Qt提供了一种进程内数据库SQLite,它小巧灵活,无须额外安装配置且支持大部分ANSI SQL92的标准SQL语句,是一个轻量化的数据库
    主要优点:

    • SQLite的设计目的是实现嵌入式SQL数据库引擎,基于纯C语言代码,运行速度很顶
    • SQLite在需要持久存储的时候可以直接读写硬盘的数据,不需要持久存储的时候也可以将整个数据库放到内存中,两种方式都不需要额外的服务器进程(SQLite无须独立运行的数据库引擎,相当于你打开了Qt应用程序就打开了数据库)
    • 开放源代码,整套代码少于3万行,有良好的注释和90%以上的测试覆盖率(应用很广泛不用担心它突然崩了没人修)
    • 少于250KB的内存占用容量(gcc编译)
    • 支持视图、触发器和事务,支持嵌套SQL功能
    • 提供虚拟机用于处理SQL语句
    • 不需要配置,不需要安装、不需要管理员
    • 支持大部分ANSI SQL92标准的语句
    • 大部分应用的速度比面前常见的数据库快
    • 编程接口简单

    接下来我们看看如何编写代码,为了方便我就直接在main函数中处理数据库连接了
    (一般可以单独写个类管理数据库的操作)

    #include <QCoreApplication>
    #include <QTextCodec>
    #include <QSqlDatabase>
    #include <QSqlQuery>
    #include <QTime>
    #include <QSqlError>
    #include <QtDebug>
    #include <QSqlDriver>
    #include <QSqlRecord>
    int main(int argc,char * argv[])
    {
        QCoreApplication a(argc, argv);
        QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());//设置中文显示
        
        QSqlDatabase db =QSqlDatabase::addDatabase("QSQLITE");//以“QSQLITE”为数据库类型,在本进程地址空间内创建一个SQLite数据库。
        db.setHostName("easybook-3313b0");     	//设置数据库主机名
        db.setDatabaseName("qtDB.db");         	//以上创建的数据库以“qtDB.db”为数据库名。它是SQLite在建立内存数据库时唯一可用的名字。 
        db.setUserName("wang");            	//设置数据库用户名
        db.setPassword("123456");               	//设置数据库密码
        db.open();                             		//打开连接
    
    	//创建QSqlQuery对象。
    	//QtSql模块中的QSqlQuery类提供了一个执行SQL语句的接口,并且可以遍历执行的返回结果集。
    	//除QSqlQuery类外,Qt还提供了三种用于访问数据库的高层类,
    	//即QSqlQueryModel、QSqlTableModel和QSqlRelationTableModel。
    	//它们无须使用SQL语句就可以进行数据库操作,而且可以很容易地将结果在表格中表示出来
        QSqlQuery query;
        //创建数据库表
        bool success=query.exec("create table automobile
                            (id int primary key,
                            attribute varchar,
                            type varchar,
                            kind varchar,
                            nation int,
                            carnumber int,
                            elevaltor int,
                            distance int,
                            oil int,
                            temperature int)");		//创建数据库表“automobil”,该表具有10个字段。在执行exec()函数调用后,就可以操作返回的结果了。 
        if(success)
            qDebug()<<QObject::tr("数据库表创建成功!\n");
        else
            qDebug()<<QObject::tr("数据库表创建失败!\n");
        //查询
        query.exec("select * from automobil");
        QSqlRecord rec = query.record();
        qDebug() << QObject::tr("automobil表字段数:" )<< rec.count();
        //插入记录
        QTime t;
        t.start();								//启动一个计时器,统计操作耗时
        query.prepare("insert into automobil values(?,?,?,?,?,?,?,?,?,?)");															//如果要插入多条记录,或者避免将值转换为字符串(即正确地转义),则可以首先调用prepare()函数指定一个包含占位符的query,然后绑定要插入的值。Qt对所有数据库均可以支持Qracle类型的占位符和ODBC类型的占位符。此处使用了ODBC类型的定位占位符
        long records=100;								//向表中插入任意的100条记录
        for(int i=0;i<records;i++)
        {
            query.bindValue(0,i);						//调用bindValue()或addBindValue()函数绑定要插入的值。 
            query.bindValue(1,"四轮");
            query.bindValue(2,"轿车");
            query.bindValue(3,"富康");
            query.bindValue(4,rand()%100);
            query.bindValue(5,rand()%10000);
            query.bindValue(6,rand()%300);
            query.bindValue(7,rand()%200000);
            query.bindValue(8,rand()%52);
            query.bindValue(9,rand()%100);
            success=query.exec();						//调用exec()函数在query中插入对应的值,之后,可以继续调用bindValue() 或addBindValue()函数绑定新值,然后再次调用exec()函数在query中插入新值。 
            if(!success)
            {
                QSqlError lastError=query.lastError();
                qDebug()<<lastError.driverText()<<QString(QObject::tr("插入失败"));
            }
        }
        qDebug()<<QObject::tr("插入 %1 条记录,耗时:%2 ms").arg(records). arg(t.elapsed());														//向表中插入任意的100条记录,操作成功后输出操作消耗的时间。
        //排序
        t.restart();								//重启计时器
        success=query.exec("select * from automobil order by id desc");
    													//按id字段的降序将查询表中刚刚插入的100条记录进行排序。 
        if(success)
            qDebug()<<QObject::tr("排序 %1 条记录,耗时:%2 ms").arg(records).arg(t. elapsed());												//输出操作耗时
        else
            qDebug()<<QObject::tr("排序失败!");
        //更新记录
        t.restart();								//重启计时器
        for(int i=0;i<records;i++)
        {
           query.clear();
           query.prepare(QString("update automobil set attribute=?,type=?,"
                                 "kind=?,nation=?,"
                                 "carnumber=?,elevaltor=?,"
                                 "distance=?,oil=?,"
                                 "temperature=? where id=%1").arg(i));
    													//更新操作与插入操作类似,只是使用的SQL语句不同。 
           query.bindValue(0,"四轮");
           query.bindValue(1,"轿车");
           query.bindValue(2,"富康");
           query.bindValue(3,rand()%100);
           query.bindValue(4,rand()%10000);
           query.bindValue(5,rand()%300);
           query.bindValue(6,rand()%200000);
           query.bindValue(7,rand()%52);
           query.bindValue(8,rand()%100);
           success=query.exec();
           if(!success)
           {
               QSqlError lastError=query.lastError();
               qDebug()<<lastError.driverText()<<QString(QObject::tr("更新失败"));
           }
        }
        qDebug()<<QObject::tr("更新 %1 条记录,耗时:%2 ms").arg(records).arg(t.elapsed());
        //删除
        t.restart();											//重启计时器
        query.exec("delete from automobil where id=15");	//执行删除id为15的记录的操作。 
    	//输出操作耗时
        qDebug()<<QObject::tr("删除一条记录,耗时:%1 ms").arg(t.elapsed());
        return 0;
        //return a.exec();
    } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117

    建立数据库的连接

    连接QSQLite数据库

    上面的代码是整个操作,我们来具体讲一下不同数据库的连接方式,在进行数据库操作之前,我们先建立与数据库的连接
    在这里插入图片描述
    使用SQLite驱动连接数据库的时候其实就在进程中单独开辟了一个空间用来存放数据库(),如果不另外调用写函数,这个数据库就会放在内存中暂时不与硬盘数据文件交互

    连接Mysql数据库

    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");//添加名叫QMYSQL的驱动连接
    db.setHostName("127.0.0.1");
    db.setDatabaseName("book");
    db.setUserName("root");
    db.setPassword("123456");
    if(!db.open())//安全性的问题,如果没有连接成功会打印连接失败
    {
    	qDebug() << "连接mysql失败" << db.lastError().text();
    }
    //我们创建两个带名字的连接来看看区别
    QSqlDatabase firstDB = QSqlDatabase::addDatabase("QMYSQL", "first");
    QSqlDatabase secondDB = QSqlDatabase::addDatabase("QMYSQL", "second");
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    创建完连接后可以在任何地方使用QSqlDatabase::database()静态函数通过连接名称获取指向数据库连接的指针,如果调用该函数没有指明连接名称则会返回默认连接

    QSqlDatabase defaultDB = QSqlDatabase::database();
    QSqlDatabase firstDB = QSqlDatabase::database("first");
    QSqlDatabase secondDB = QSqlDatabase::database("second");
    
    • 1
    • 2
    • 3

    要移除一个数据库连接,需要先使用QSqlDatabase::close()关闭数据库,然后使用静态函数QSqlDatabase::removeDatabase()移除连接

    执行sql语句

    查询数据

    在这里插入图片描述
    QSqlQuery类是我们用来操作数据库的接口类之一,我上面就写了这个类接口函数的部分使用方式。
    当你使用完query.exec("select * from student");进行查询后,它的内部指针会指向返回结果前面的位置。

    我们需要调用QSqlQuery::next()来完成遍历,下面是遍历打印的方式,我们用qDebug将结果打印到控制台上:

    while(query.next())
    {  
    	qDebug() << query.value(0).toInt() << query.value(1).toString();  
    }
    
    • 1
    • 2
    • 3
    • 4

    在QSqlQuery类中还有很多函数来实现在结果集中的定位:

    • next()定位到下一条记录
    • previous()定位到前一条记录
    • first()定位到第一条记录
    • last() 定位到最后一条记录
    • seek(n)定位到第n条记录
    • 当前行的索引可以通过at()返回
    • record()函数返回当前指向的记录

    插入数据

    插入一条记录:

    query.exec("insert into student (id, name) values (1, 'albert')");
    
    • 1

    同时插入多条记录可以使用占位符来完成

    1. 名称绑定占位符
    query.prepare("insert into student (id, name) values (:id, :name)");
    int idValue = 1;
    QString nameValue = "albert";
    query.bindValue(":id", idValue);
    query.bindValue(":name", nameValue);
    query.exec();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    占位之后id和idValue这个变量绑定、name和nameValue这个变量绑定,通过for循环赋值exec就可以实现插入多条记录

    调用QSqlQuery::prepare()一次,然后使用多次bindValue()或者addBindValue()函数来绑定需要的数据,最后调用一次exec()函数就可以了。

    我这里用输入流的写法表示:

    query.prepare("insert into student (id, name) values (?, ?)");
    QVariantList ids;
    ids << 1 << 2 << 3;
    query.addBindValue(ids);
    QVariantList names;
    names << "albert" << "su" << "OS";
    query.addBindValue(names);
    if(!query.execBatch()) 
    	qDebug() << query.lastError();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    事务的处理

    如果底层的数据库驱动支持事务,QSqlDriver::hasFeature(QSqlDriver::Transactions)会返回true。可以使用QSqlDatabase::transaction()来启动一个事务,然后编写希望在事务中执行的SQL语句,最后调用QSqlDatabase::commit()提交或者QSqlDatabase::rollback()回滚。使用事务必须在创建查询以前就开始事务

    QSqlDatabase::database().transaction();//启动一个事务
    QSqlQuery query;//新建一个操作对象
    query.exec("SELECT id FROM student WHERE name = 'albert'");
    if (query.next())
    {
    	int id = query.value(0).toInt();
        query.exec("INSERT INTO project (id, name, ownerid) "
                   "VALUES (201, 'MProject', "
                   + QString::number(id) + ')');
    }
    QSqlDatabase::database().commit();//提交事务
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Qt提供的sql模型类

    如果学习过其他语言的sql连接,就知道有的语言提供了更高级的用户接口类,让你在语言框架中就可以建立sql模型。
    特别是最近流行的MVC框架,将模型直接放到框架类中,不需要在单独地维护数据库,Qt也提供了这种更高级的用户接口。

    Qt提供了3个更高层的类来访问数据库,分别是
    QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel。
    这3个类都是从QAbstractTableModel派生来的,可以很容易地实现将数据库中的数据在QListView和QTableView等项视图类中进行显示。使用这些类的另一个好处是,这样可以使编写的代码很容易的适应其他的数据源。例如,如果开始使用了QSqlTableModel,而后来要改为使用XML文件来存储数据,这样需要做的仅是更换一个数据模型

    QSqlQueryModel模型

    QSqlQueryModel提供了一个基于SQL查询的只读模型

    QSqlQueryModel *model = new QSqlQueryModel(this);
    model->setQuery("select * from student");
    model->setHeaderData(0, Qt::Horizontal, tr("学号"));
    model->setHeaderData(1, Qt::Horizontal, tr("姓名"));
    model->setHeaderData(2, Qt::Horizontal, tr("课程"));
    ui->tableView->setModel(model);
    ui->tableView->verticalHeader()->setHidden(true);
    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    QSqlTableModel模型

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

    QSqlTableModel *model = new QSqlTableModel(this);
    model->setTable("student");
    model->select();
    // 设置编辑策略
    model->setEditStrategy(QSqlTableModel::OnManualSubmit);
    ui->tableView->setModel(model);
    ui->tableView->verticalHeader()->setHidden(true);
    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    QSqlRelationalTableModel模型

    QSqlRelationalTableModel继承自QSqlTableModel,并且对其进行了扩展,提供了对外键的支持。

    QSqlRelationalTableModel *model = new QSqlRelationalTableModel(this);
    model->setTable("student");
    model->setRelation(2, QSqlRelation("course", "id", "name"));
    model->select();
    ui->tableView->setModel(model);
    ui->tableView->verticalHeader()->setHidden(true);
    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    Flutter 第三方 flutter_screenutil(屏幕适配)
    大三实习生,字节跳动面经分享,已拿Offer
    虽然菜但是喜欢
    react中的props的使用
    分布式框架Apache Dubbo 快速入门-不古出品
    【云原生】SQL(及存储过程)跑得太慢怎么办?
    【重学Java四】Object通用方法、继承
    phpStorm Xdebug调试 加FireFox浏览器
    Nginx监控模块
    QT 自定义QGraphicsItem 缩放后旋转 图形出现漂移问题
  • 原文地址:https://blog.csdn.net/Albert_weiku/article/details/125524488