Qt操作SQLite数据库的教程详解
作者:音视频开发老舅 发布时间:2024-01-16 14:10:04
0.前言
SQLite是一款开源、轻量级、跨平台的数据库,无需server,无需安装和管理配置。它的设计目标是嵌入式的,所以很适合小型应用,也是Qt应用开发种常用的一种数据库。
1.驱动
Qt SQL模块使用驱动程序插件(plugins)与不同的数据库API进行通信。由于Qt的SQL模块API与数据库无关,因此所有特定于数据库的代码都包含在这些驱动程序中。Qt提供了几个驱动程序,也可以添加其他驱动程序。提供驱动程序源代码,可用作编写自己的驱动程序的模型。
QtCreator在*.pro中引入sql模块(QT+=sql),或是VS中在Qt VS Tool里勾选上sql模块,就可以使用该模块的接口了。
可以使用QSqlDatabase::drivers()获取驱动程序列表并打印,Qt5.9.7输出如下:
其中,SQLite是一个进程内数据库,这意味着没有必要拥有数据库服务器。SQLite在单个文件上运行,在打开连接时必须将其设置为数据库名称。如果该文件不存在,SQLite将尝试创建它。。
2.初相遇
/*
* ... ...
* 我喜欢那样的梦
* 在梦里 一切都可以重新开始
* 一切都可以慢慢解释
* 心里甚至还能感觉到所有被浪费的时光
* 竟然都能重回时的狂喜和感激
* ... ...
*
* ----席慕容《初相遇》
*/
#include <QSqlDatabase>
#include <QDebug>
void initDb()
{
//qDebug()<<QSqlDatabase::drivers();//打印驱动列表
QSqlDatabase db;
//检测已连接的方式 - 默认连接名
//QSqlDatabase::contains(QSqlDatabase::defaultConnection)
if(QSqlDatabase::contains("qt_sql_default_connection"))
db = QSqlDatabase::database("qt_sql_default_connection");
else
db = QSqlDatabase::addDatabase("QSQLITE");
//检测已连接的方式 - 自定义连接名
/*if(QSqlDatabase::contains("mysql_connection"))
db = QSqlDatabase::database("mysql_connection");
else
db = QSqlDatabase::addDatabase("QSQLITE","mysql_connection");*/
//设置数据库路径,不存在则创建
db.setDatabaseName("sqltest.db");
//db.setUserName("gongjianbo"); //SQLite不需要用户名和密码
//db.setPassword("qq654344883");
//打开数据库
if(db.open()){
qDebug()<<"open success";
//关闭数据库
db.close();
}
}
上面的代码中,先是创建了一个QSqlDatabase对象,该类用于处理数据库的连接。contains方法用于查看给定的连接名称是否在连接列表中,database方法获取数据库连接,前提是已使用addDatabase添加数据库连接。addDatabase声明如下:
QSqlDatabase QSqlDatabase::addDatabase(const QString &type, const QString &connectionName = QLatin1String(defaultConnection)) [static]
第一个参数对应驱动名,第二个参数为连接名称 ,如果不使用默认连接名称“qt_sql_default_connection”的话需要填写该参数。
设置了驱动及连接名称后,就是设置数据库文件的名称/路径,因为SQLite不需要用户名和密码,接下来直接就可以通过open和close函数来打开关闭该数据库了。
此外,如果需要在内存中创建数据库,而不是指定一个文件,可以setDatabaseName(“:memory:”);
db.setDatabaseName(":memory:");
3.创建表
SQL语句执行需要用到QSqlQuery类,文档有云:
QSqlQuery封装了在QSqlDatabase上执行的SQL查询中创建,导航和检索数据所涉及的功能。它可以被用来执行DML(数据操纵语言)语句,例如select、insert、update、delete,以及DDL(数据定义语言)语句,如create table,还可以用于执行非标准SQL的特定于数据库的命令。
成功执行的SQL语句将查询的状态设置为活动状态,以便isActive()返回true。否则,查询的状态将设置为非活动状态。在任何一种情况下,执行新的SQL语句时,查询都位于无效记录上。必须先将活动查询导航到有效记录(以便isActive()返回true),然后才能检索值。
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
void createTable()
{
//sql语句不熟悉的推荐《sql必知必会》,轻松入门
//如果不存在则创建my_table表
//id自增,name唯一
const QString sql=R"(
CREATE TABLE IF NOT EXISTS my_table (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name CHAR (50) UNIQUE NOT NULL,
age INTEGER
);)";
//QSqlQuery构造前,需要db已打开并连接
//未指定db或者db无效时使用默认连接进行操作
QSqlQuery query;
if(query.exec(sql)){
qDebug()<<"init table success";
}else{
//打印sql语句错误信息
qDebug()<<"init table error"<<query.lastError();
}
}
代码通过QSqlQuery的exec方法执行SQL语句,创建了一个简单的数据表。exec执行SQL语句成功返回true,否则返回false。
QSqlQuery的构造函数可以指定QDatabase参数,如果未指定db,或者db无效将使用默认连接。
QSqlQuery::QSqlQuery(QSqlDatabase db);
要注意的是,对于SQLite,exec方法一次只能执行一个语句。
4.执行增删改查
执行了上面的操作且数据库已open,就能使用QSqlQuery的exec方法来完成增删改查了。
//插入数据
void insertRecord(const QString &name, int age)
{
QSqlQuery query;
//方式一,直接执行SQL语句
query.exec(QString(R"(INSERT INTO my_table(name,age) VALUES('%1',%2);)")
.arg(name).arg(age));
//方式二,绑定值,待定变量默认用问号占位,注意字符串也没有引号
/*query.prepare(R"(INSERT INTO my_table(name,age) VALUES(?,?);)");
query.addBindValue(name);
query.addBindValue(age);
query.exec();*/
}
//删除数据
void deleteRecord(const QString &name)
{
QSqlQuery query;
//方式一,直接执行SQL语句
query.exec(QString(R"(DELETE FROM my_table WHERE name='%1';)")
.arg(name));
//方式二,绑定值,待定变量默认用问号占位
/*query.prepare(R"(DELETE FROM my_table WHERE name=?;)");
query.addBindValue(name);
query.exec();*/
}
//更新数据
void updateRecord(const QString &name, int age)
{
QSqlQuery query;
//方式一,直接执行SQL语句
query.exec(QString(R"(UPDATE my_table SET age=%2 WHERE name='%1';)")
.arg(name).arg(age));
//方式二,绑定值,待定变量默认问号,可自定义
/*query.prepare(R"(UPDATE my_table SET age=:age WHERE name=:name;)");
query.bindValue(":name",name);//通过自定义的别名来替代
query.bindValue(":age",age);
query.exec();*/
}
//查询数据
int searchRecord(const QString &name)
{
QSqlQuery query;
query.exec(QString(R"(SELECT age FROM my_table WHERE name='%1';)")
.arg(name));
//获取查询结果的第0个值,
//如果结果是多行数据,可用while(query.next()){}遍历每一行
int ageValue=-1;
if(query.next()){
ageValue=query.value(0).toInt();
}
qDebug()<<ageValue;
return ageValue;
}
可以看到,如果熟悉SQL语句的话,很容易就实现了增删改查功能。
对于 BLOB 类型,查询后可以 toByteArray,修改时可以 bindValue QByteArray。
5.进阶
有时候会遇到大量数据操作的情况,这时候用普通的insert之类的语句循环操作可能会很慢。
技巧一:开启事务
SQLite通过执行”BEGIN;“或是”BEGIN TRANSACTION;“开启事务,执行”ROLLBACK;“进行回滚,执行”COMMIT;“或是”END TRANSACTION;“提交事务。QSqlDatabase也提供了对应的transaction、rollback、commit三个函数来执行对应操作。
技巧二:关闭写同步(synchrnous)
在SQLite中,数据库配置的参数都由编译指示(pragma)来实现的,而其中synchronous选项有三种可选状态,分别是full、normal、off。简要说来,full写入速度最慢,但保证数据是安全的,不受断电、系统崩溃等影响,而off可以加速数据库的一些操作,但如果系统崩溃或断电,则数据库可能会损毁。通过执行”PRAGMA synchronous = OFF;“语句,可以提升效率,不过若不是临时数据库不建议此操作。
SQLite 默认是文件锁, Qt 中 SQLite 默认是以多线程读写模式打开,如果同时写入就会出现写入错误:
可以将写操作上锁,但是实测线程中循环写入时,只读打开去查询也是会阻塞很久,毫秒到几秒不等,这时候就得把超时设置长一点。
所以还是得读写都加锁,但这也只能解决单个进程的并发访问。
来源:https://blog.csdn.net/m0_60259116/article/details/128226378
猜你喜欢
- 共享状态是比较容易理解和使用的,但是可能产生隐晦以至于很难追踪的 bugs。尤其是在我们的数据结构只有部分是通过引用传递的。切片就是这么一个
- 一、算术运算符运算符+-*/%**(幂)求次方//(取整除,向下取整)如:9//2 =4二、比较运算符运算符==!=<>(不等于
- 通过 Regsvr 32 .exe, 然后注册下列 DLL : C:\Program files\Common Files\System\A
- 本文实例讲述了Python中Django框架利用url来控制登录的方法。分享给大家供大家参考。具体如下:from django.conf.u
- 1.在pycharm下安装scrapy函数库2.将安装好scrapy函数库下的路径配置到系统path的环境变量中3.打开cmd终端输入:sc
- 今天在写一个算法的过程中,得到了一个类似下面的字典:{'user1':0.456,'user2':0.999
- asp抓取网页。偶要实现实实更新天气预报。利用了XMLHTTP组件,抓取网页的指定部分。很多小偷查询都是使用这个方法来实现的。需要分件htm
- windows下vue-cli及webpack 构建网站(一)环境安装 windows下vue-cli及webpack 构建网站(
- golang扩容规则举个例子来演示下package mainimport ("fmt")func main() {arr
- 一、CSRF:保护机制Django预防CSRF攻击的方法是在用户提交的表单中加入一个csrftoken的隐含值,这个值和服务器中保存的csr
- 登录百度AL开发平台在控制台选择语音合成创建应用填写应用信息在应用列表获取(Appid、API Key、Secret Key)6. 安装py
- 问题你想使用一个Python字典存储数据,并将它转换成XML格式。解决方案尽管 xml.etree.ElementTree 库通常用来做解析
- 插入或更新null空值一、在SQL语句中直接插入null或空字符串“”int? item = nul
- 前述VARCHAR和CHAR是两种最主要的字符串类型。不幸的是,很难精确地解释这些值是怎么存储在磁盘和内存中的,因为这跟存储引擎的具体实现有
- 如下所示:from numpy import *import numpy as npimport matplotlib.pyplot as
- 关于使用CTE(公用表表达式)的递归查询----SQL Server 2005及以上版本公用表表达式 (CTE) 具有一个重要的优点,那就是
- 第一种方式: $(document).ready(function(){ $("#clickme").click(fun
- 这里需要用到一个Django插件:django-pagination安装打开控制台 输入pip install dj-pagination实
- 前言去年暑假参加了一个比赛,比赛内容中需要确定目标的位置 本来想全用图像完成的,最后发现不是很符合要求。比完赛之后,就忙别的事了。直到现在突
- 定义:select语句中嵌套select语句,被嵌套的select语句是子查询。子查询可以出现在:select ....(select)..