From 6bec3f246b6128e0d166bd3aed4509ef881cc848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=AE=E7=94=9F=E8=8B=A5=E6=A2=A6?= <1070753498@qq.com> Date: Thu, 2 Nov 2023 18:45:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9ESQLITE=20WAL=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E5=A4=9A=E7=BA=BF=E7=A8=8B=E5=86=99=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E7=A8=8B=E5=BA=8F=EF=BC=9B=20=E5=88=A0=E9=99=A4sqltab?= =?UTF-8?q?leview=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-tidy | 81 +++++++------------ CMakeLists.txt | 2 +- Qt-Examples.pro | 2 +- README.md | 83 +++++++++++-------- SqlTabview/CMakeLists.txt | 10 --- SqlTabview/SqlTabview.pro | 30 ------- SqlTabview/main.cpp | 11 --- SqlTabview/mainwindow.cpp | 166 -------------------------------------- SqlTabview/mainwindow.h | 31 ------- SqlTabview/sql.cpp | 86 -------------------- SqlTabview/sql.h | 21 ----- SqliteWAL/CMakeLists.txt | 5 ++ SqliteWAL/SqliteWAL.pro | 19 +++++ SqliteWAL/main.cc | 31 +++++++ SqliteWAL/sqlutils.cc | 100 +++++++++++++++++++++++ SqliteWAL/sqlutils.hpp | 19 +++++ 16 files changed, 255 insertions(+), 442 deletions(-) delete mode 100644 SqlTabview/CMakeLists.txt delete mode 100644 SqlTabview/SqlTabview.pro delete mode 100644 SqlTabview/main.cpp delete mode 100644 SqlTabview/mainwindow.cpp delete mode 100644 SqlTabview/mainwindow.h delete mode 100644 SqlTabview/sql.cpp delete mode 100644 SqlTabview/sql.h create mode 100644 SqliteWAL/CMakeLists.txt create mode 100644 SqliteWAL/SqliteWAL.pro create mode 100644 SqliteWAL/main.cc create mode 100644 SqliteWAL/sqlutils.cc create mode 100644 SqliteWAL/sqlutils.hpp diff --git a/.clang-tidy b/.clang-tidy index f95ff5f..9a74f3b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -6,55 +6,32 @@ AnalyzeTemporaryDtors: false FormatStyle: file User: user CheckOptions: - - key: readability-identifier-naming.AbstractClassCase - value: CamelCase - - key: readability-identifier-naming.ClassCase - value: CamelCase - - key: readability-identifier-naming.ClassConstantCase - value: UPPER_CASE - - key: readability-identifier-naming.ClassConstantPrefix - value: 'k' - - key: readability-identifier-naming.ClassMemberCase - value: lower_case - - key: readability-identifier-naming.ClassMemberPrefix - value: 'm_' - - key: readability-identifier-naming.ClassMethodCase - value: camelBack - - key: readability-identifier-naming.ConstexprVariableCase - value: UPPER_CASE - - key: readability-identifier-naming.ConstexprVariablePrefix - value: 'k' - - key: readability-identifier-naming.EnumCase - value: CamelCase - - key: readability-identifier-naming.EnumConstantCase - value: UPPER_CASE - - key: readability-identifier-naming.EnumConstantPrefix - value: '' - - key: readability-identifier-naming.FunctionCase - value: camelBack - - key: readability-identifier-naming.FunctionParameterCase - value: lower_case - - key: readability-identifier-naming.GlobalConstantCase - value: UPPER_CASE - - key: readability-identifier-naming.GlobalConstantPrefix - value: 'k' - - key: readability-identifier-naming.GlobalFunctionCase - value: camelBack - - key: readability-identifier-naming.GlobalVariableCase - value: lower_case - - key: readability-identifier-naming.GlobalVariablePrefix - value: 'g_' - - key: readability-identifier-naming.InlineNamespaceCase - value: lower_case - - key: readability-identifier-naming.LocalConstantCase - value: UPPER_CASE - - key: readability-identifier-naming.LocalConstantPrefix - value: 'k' - - key: readability-identifier-naming.LocalVariableCase - value: lower_case - - key: readability-identifier-naming.MacroDefinitionCase - value: UPPER_CASE - - key: readability-identifier-naming.NamespaceCase - value: lower_case - - key: bugprone-narrowing-conversions.IgnoreFloatingPointPrecisionLoss - value: 'false' + # 类名改为大驼峰 + +- key: readability-identifier-naming.ClassCase + value: CamelCase + +# 函数名改为小驼峰 + +- key: readability-identifier-naming.FunctionCase + value: camelBack + +# 变量名改为小驼峰 + +- key: readability-identifier-naming.VariableCase + value: camelBack + +# 关闭一些过于严苛或者不适合的检查 + +- key: readability-braces-around-statements.ShortStatementLines + value: '0' +- key: readability-magic-numbers.IgnorePowersOf2IntegerLiterals + value: '1' +- key: modernize-use-trailing-return-type.UseEastWestConst + value: '1' +- key: readability-identifier-naming.ClassMemberPrefix + value: 'm_' +- key: readability-identifier-naming.ConstexprVariablePrefix + value: 'k' +- key: readability-identifier-naming.GlobalVariablePrefix + value: 'g_' diff --git a/CMakeLists.txt b/CMakeLists.txt index 6706132..ba1f508 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ add_subdirectory(ProgressArc) add_subdirectory(ProgressBar) add_subdirectory(ReactorServer) add_subdirectory(SlipButton) -add_subdirectory(SqlTabview) +add_subdirectory(SqliteWAL) add_subdirectory(TableViewModel) add_subdirectory(Thread) add_subdirectory(TreeViewModel) diff --git a/Qt-Examples.pro b/Qt-Examples.pro index bd0a6a0..57e7231 100644 --- a/Qt-Examples.pro +++ b/Qt-Examples.pro @@ -24,7 +24,7 @@ SUBDIRS += \ ReactorServer \ SimpleUdp \ SlipButton \ - SqlTabview \ + SqliteWAL \ TableViewModel \ Thread \ TreeViewModel \ diff --git a/README.md b/README.md index f353557..02bd20d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## [QT实用小技巧(想到就更新) | 自由意志 (realchuan.github.io)](https://realchuan.github.io/2021/10/12/QT%E5%AE%9E%E7%94%A8%E5%B0%8F%E6%8A%80%E5%B7%A7%EF%BC%88%E6%83%B3%E5%88%B0%E5%B0%B1%E6%9B%B4%E6%96%B0%EF%BC%89/) -## [Battery](Battery/)——电池; +## [Battery](/Battery/)——电池 @@ -14,13 +14,15 @@
-## [Bootstarp](Bootstarp/)--程序开机自启动设置和检测; +## [Bootstarp](/Bootstarp/)--程序开机自启动设置和检测 1. Windows下读写注册表实现开机自启动,有两个位置可以写入; + ```powershell HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run //对于所有用户 HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run // 对于当前用户 ``` + 2. MacOS下拷贝plist文件到~/Library/LaunchAgents/,使用launchctl load/unload命令实现开机自启动; 3. Ubuntu下有两种方式: 1. 使用systemctl --user enable/disable命令实现开机自启动; @@ -28,44 +30,44 @@ 2. systemctl命令用于.timer文件,.timer文件用于定时执行.service文件,防止图形界面启动后,出现qxcbconnection: could not connect to display错误; 2. 把/usr/share/Application/下的.desktop文件拷贝到~/.config/autostart/下,实现开机自启动(未验证); -## [BubbleWindow](BubbleWindow/)——气泡式对话框,也可作工具提示(ToolTip); +## [BubbleWindow](/BubbleWindow/)——气泡式对话框,也可作工具提示(ToolTip)
-## [Chart](Chart/)——可视化图表绘制,参考[ 使用 QChart 显示实时动态曲线 ](https://qtdebug.com/qtbook-paint-realtime-curve-qchart/ "qtdebug/公孙二狗") 和QChart相关示例; +## [Chart](Chart/)——可视化图表绘制,参考[使用 QChart 显示实时动态曲线](https://qtdebug.com/qtbook-paint-realtime-curve-qchart/ "qtdebug/公孙二狗") 和QChart相关示例
图一二是动态曲线
图一二是动态曲线, 图二坐标轴也会动态变化
-## [CheckBoxStandardItem](/CheckBoxStandardItem)——可以勾选的StandardItem,而且根据勾选状态自动更新父节点状态或者子节点状态; +## [CheckBoxStandardItem](/CheckBoxStandardItem/)——可以勾选的StandardItem,而且根据勾选状态自动更新父节点状态或者子节点状态
-## [Clock](/Clock)——时钟; +## [Clock](/Clock/)——时钟
-## [DashBoard](/DashBoard)——仪表盘; +## [DashBoard](/DashBoard/)——仪表盘
-## [FlowLayout](FlowLayout/)——流式布局,来自QT示例Flow Layout Example; +## [FlowLayout](/FlowLayout/)——流式布局,来自QT示例Flow Layout Example
-## [DragDrop](DragDrop/)——简单控件拖拽,参考QT示例Drag and Drop Puzzle Example; +## [DragDrop](/DragDrop/)——简单控件拖拽,参考QT示例Drag and Drop Puzzle Example -## [HttpClient ](/HttpClient)——http客户端; +## [HttpClient](/HttpClient/)——http客户端 -## [ImageCarousel](ImageCarousel/)——简易图片轮播; +## [ImageCarousel](/ImageCarousel/)——简易图片轮播
-## [GridViewModel ](/GridViewModel)——基于QListView的自适应宫图; +## [GridViewModel](/GridViewModel/)——基于QListView的自适应宫图
-## [LogAsynchronous](LogAsynchronous/)——异步日志,开辟一个线程专门往文件里写日志,前后端分离。 +## [LogAsynchronous](/LogAsynchronous/)——异步日志,开辟一个线程专门往文件里写日志,前后端分离 1. 日志文件名:应用程序名(appname).时间(time,精确到秒).主机hostname.进程ID(Tid).log(.count),假如一天内写的单个日志大约接近1G,会自动加后缀(.1,.2.3...,以此类推)新建新的日志文件去写,每天0点依然会rollFile; 1. 正常文件名:LogAsynchronous.2020-04-26-20-29-03.Youth.11828.log; @@ -73,15 +75,15 @@ 2. 日志格式:时间(time,精确到毫秒).线程ID(Pid).日志级别(debug).打印信息(msg).文件(File).行号(Line)。 1. 比如:2020-04-26 20:38:55.818 2052 [Debug] 123456789qwertyuioplkjhgfdsa 8412789-File:(..\logAsynchronous\main.cpp) Line:(19); -## [MulClient](MulClient/)——多线程客户端,一个线程一个客户端(怎么可以绕开系统限制,模拟百万个客户端); +## [MulClient](/MulClient/)——多线程客户端,一个线程一个客户端(怎么可以绕开系统限制,模拟百万个客户端) -## [MulServer](MulServer/)——多线程服务端,一个线程一个客户端处理(处理实时性很高的TCP通讯); +## [MulServer](/MulServer/)——多线程服务端,一个线程一个客户端处理(处理实时性很高的TCP通讯) -## [NavigationProgressBar](/NavigationProgressBar)——导航进度栏; +## [NavigationProgressBar](/NavigationProgressBar/)——导航进度栏
-## [PasswordLineEdit](PasswordLineEdit/)——密码输入框; +## [PasswordLineEdit](/PasswordLineEdit/)——密码输入框 @@ -90,23 +92,23 @@
-## [ProgressArc](ProgressArc/)——圆弧进度条; +## [ProgressArc](/ProgressArc/)——圆弧进度条
-## [ProgressBar](ProgressBar)——QProgressBar圆角替代方案; +## [ProgressBar](/ProgressBar/)——QProgressBar圆角替代方案
-## [ReactorServer](ReactorServer/)——多线程服务端,Reactor模式(Echo); +## [ReactorServer](/ReactorServer/)——多线程服务端,Reactor模式(Echo) -## [SimpleUdp](SimpleUdp/)——简单UDP例子,广播和接收; +## [SimpleUdp](/SimpleUdp/)——简单UDP例子,广播和接收 -## [ShowInMyComputer](ShowInMyComputer/)——在我的电脑中显示当前应用程序; +## [ShowInMyComputer](/ShowInMyComputer/)——在我的电脑中显示当前应用程序 防火墙白名单。 -## [SlipButton](SlipButton/)——滑动按钮; +## [SlipButton](/SlipButton/)——滑动按钮 另:更简单的实现:[有动画效果的 CheckBox](http://qtdebug.com/qtbook-animated-checkbox/); @@ -117,9 +119,21 @@ -## [SqlTabview](SqlTabview/)——SQLite数据库调用,模型方法; +## [SqliteWAL](/SqliteWAL/)——Sqlite WAL 模式下多线程并发写入数据库程序 + +### WAL模式的优点 + +1. 提高了并发性:WAL模式允许多个读取器和一个写入器同时访问数据库,可以提高并发性能和性能; +2. 崩溃恢复:WAL模式在发生崩溃时确保数据库保持一致,通过在提交事务之前将所有更改刷新到日志文件来实现; +3. 改进了写入性能:WAL模式允许并发写入,可以比默认的回滚模式更好地改进写入性能; + +### WAL模式的注意事项 -## [TableViewModel](TableViewModel/)——表格视图; +1. WAL模式仅适用于SQLite 3.35.5+版本; +2. 增加了磁盘使用量:与回滚模式相比,WAL模式需要更多的磁盘空间,因为它在提交更改之前将所有更改都写入日志文件; +3. 读取性能较慢:在WAL模式下,读取操作不会被写入操作阻塞,如果同时进行读取和写入操作,可能导致数据不一致。 + +## [TableViewModel](/TableViewModel/)——表格视图 1. 各种自定义代理 1. [ButtonDelegate](./TableViewModel/buttondelegate.h); @@ -131,24 +145,27 @@
-## [Thread](Thread/)——多线程例子,6种写法; +## [Thread](/Thread/)——多线程例子,6种写法 -## [TreeViewModel](TreeViewModel/)——树形视图(MVC),QtCreator源码; +## [TreeViewModel](/TreeViewModel/)——树形视图(MVC),QtCreator源码
-## [Validator](Validator/)——加强版IntValidator(QIntValidator)和DoubleValidator(QDoubleValidator) +## [Validator](/Validator/)——加强版IntValidator(QIntValidator)和DoubleValidator(QDoubleValidator) + +## [packaging](/packaging/)——打包脚本 + +1. [macos](/packaging/macos/)——macos qmake编译、打包dmg包脚本(`python`/`appdmg`); +2. [ubuntu](/packaging/ubuntu/)——ubuntu qmake编译、打包AppImage/deb包脚本(`linuxdeployqt-continuous-x86_64.AppImage`/`dpkg-deb`); + 1. [使用root权限打开应用程序的一种方法](/packaging/ubuntu/opt/MyApp/MyApp.sh): -## [packaging](packaging/)——打包脚本; -1. [macos](packaging/macos/)——macos qmake编译、打包dmg包脚本(`python`/`appdmg`); -2. [ubuntu](packaging/ubuntu/)——ubuntu qmake编译、打包AppImage/deb包脚本(`linuxdeployqt-continuous-x86_64.AppImage`/`dpkg-deb`); - 1. [使用root权限打开应用程序的一种方法](packaging/ubuntu/opt/MyApp/MyApp.sh): ```shell #!/bin/sh pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY /opt/MyApp/MyApp ``` -3. [windows](packaging/windows/)——windows qmake编译、打包安装脚本(`Innosetup`); + +3. [windows](/packaging/windows/)——windows qmake编译、打包安装脚本(`Innosetup`); 1. `Innosetup` `signtool` ``` diff --git a/SqlTabview/CMakeLists.txt b/SqlTabview/CMakeLists.txt deleted file mode 100644 index 8a6806f..0000000 --- a/SqlTabview/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(PROJECT_SOURCES - main.cpp - mainwindow.cpp - mainwindow.h - sql.h - sql.cpp) - -qt_add_executable(SqlTabview MANUAL_FINALIZATION ${PROJECT_SOURCES}) -target_link_libraries(SqlTabview PRIVATE Qt6::Widgets Qt6::Sql) -qt_finalize_executable(SqlTabview) diff --git a/SqlTabview/SqlTabview.pro b/SqlTabview/SqlTabview.pro deleted file mode 100644 index 1ea4bc7..0000000 --- a/SqlTabview/SqlTabview.pro +++ /dev/null @@ -1,30 +0,0 @@ -QT += core gui sql - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -CONFIG += c++17 - -# The following define makes your compiler emit warnings if you use -# any Qt feature that has been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. -DEFINES += QT_DEPRECATED_WARNINGS - -# You can also make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -# You can also select to disable deprecated APIs only up to a certain version of Qt. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -SOURCES += \ - main.cpp \ - mainwindow.cpp \ - sql.cpp - -HEADERS += \ - mainwindow.h \ - sql.h - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target diff --git a/SqlTabview/main.cpp b/SqlTabview/main.cpp deleted file mode 100644 index 24ba3b6..0000000 --- a/SqlTabview/main.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "mainwindow.h" - -#include - -auto main(int argc, char *argv[]) -> int -{ - QApplication a(argc, argv); - MainWindow w; - w.show(); - return a.exec(); -} diff --git a/SqlTabview/mainwindow.cpp b/SqlTabview/mainwindow.cpp deleted file mode 100644 index 2da831c..0000000 --- a/SqlTabview/mainwindow.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "mainwindow.h" -#include "sql.h" - -#include -#include -#include - -class MainWindow::MainWindowPrivate -{ -public: - explicit MainWindowPrivate(QWidget *parent) - : d_ptr(parent) - { - sqlTabView = new QTableView(d_ptr); - sqlTabView->setAlternatingRowColors(true); - sqlTabView->horizontalHeader()->setStretchLastSection(true); - lineEdie = new QLineEdit(d_ptr); - } - - QWidget *d_ptr; - - Sql *sqlite; - QSqlTableModel *sqlModel; - QTableView *sqlTabView; - QLineEdit *lineEdie; -}; - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , d_ptr(new MainWindowPrivate(this)) -{ - if (!Sql::searchSQLite()) { - return; - } - d_ptr->sqlite = new Sql(this); - QSqlError error = d_ptr->sqlite->createOrOpenSqlite(); - if (error.type() != QSqlError::NoError) { - showError(error); - return; - } - setupUI(); - setupModel(); - - resize(1000, 618); -} - -MainWindow::~MainWindow() {} - -void MainWindow::onAdd() -{ - // 获得表的行数 - int rowNum = d_ptr->sqlModel->rowCount(); - QSqlQuery query; - int id = query.lastInsertId().toInt(); - // 添加一行 - d_ptr->sqlModel->insertRow(rowNum); - d_ptr->sqlModel->setData(d_ptr->sqlModel->index(rowNum, 0), id + 1); -} - -void MainWindow::onDelete() -{ - int curRow = d_ptr->sqlTabView->currentIndex().row(); - d_ptr->sqlModel->removeRow(curRow); - int ok = QMessageBox::warning(this, - tr("Delete current row!"), - tr("Are you sure you want to delete the current line?"), - QMessageBox::Yes, - QMessageBox::No); - if (ok == QMessageBox::No) { - d_ptr->sqlModel->revertAll(); - } // 如果不删除, 则撤销 - else { - d_ptr->sqlModel->submitAll(); // 否则提交, 在数据库中删除该行 - } -} -void MainWindow::onUpdate() -{ - // 开始事务操作 - d_ptr->sqlModel->database().transaction(); - if (d_ptr->sqlModel->submitAll()) { - d_ptr->sqlModel->database().commit(); //提交 - } else { - d_ptr->sqlModel->database().rollback(); //回滚 - QMessageBox::warning(this, - tr("tableModel"), - tr("Database error: %1").arg(d_ptr->sqlModel->lastError().text())); - } -} - -void MainWindow::onSelect() -{ - QString name = d_ptr->lineEdie->text().trimmed(); - if (name.isEmpty()) { - QMessageBox::warning(this, tr("Warning"), tr("Name cannot be empty!")); - } else { - //根据姓名进行筛选, 一定要使用单引号 - d_ptr->sqlModel->setFilter(QString("Name = '%1'").arg(name)); - d_ptr->sqlModel->select(); - } -} - -void MainWindow::onRevert() -{ - d_ptr->sqlModel->revertAll(); -} - -void MainWindow::onShow() -{ - d_ptr->sqlModel->setTable("students"); - d_ptr->sqlModel->select(); -} - -void MainWindow::setupUI() -{ - auto addButton = new QPushButton(tr("Add"), this); - auto deleteButtom = new QPushButton(tr("Delete"), this); - auto updateButton = new QPushButton(tr("Update"), this); - auto selectButton = new QPushButton(tr("Select"), this); - auto revertButton = new QPushButton(tr("Revert"), this); - auto showAllButton = new QPushButton(tr("Show All"), this); - - connect(addButton, &QPushButton::clicked, this, &MainWindow::onAdd); - connect(deleteButtom, &QPushButton::clicked, this, &MainWindow::onDelete); - connect(updateButton, &QPushButton::clicked, this, &MainWindow::onUpdate); - connect(selectButton, &QPushButton::clicked, this, &MainWindow::onSelect); - connect(revertButton, &QPushButton::clicked, this, &MainWindow::onSelect); - connect(showAllButton, &QPushButton::clicked, this, &MainWindow::onShow); - - auto buttonLayout = new QVBoxLayout; - buttonLayout->setSpacing(0); - buttonLayout->addWidget(addButton); - buttonLayout->addWidget(deleteButtom); - buttonLayout->addWidget(updateButton); - buttonLayout->addWidget(revertButton); - buttonLayout->addWidget(showAllButton); - - auto bottomLayout = new QHBoxLayout; - bottomLayout->addWidget(new QLabel(tr("Name: "), this)); - bottomLayout->addWidget(d_ptr->lineEdie); - - auto widget = new QWidget(this); - QGridLayout *layout = new QGridLayout(widget); - layout->addWidget(d_ptr->sqlTabView, 0, 0); - layout->addLayout(buttonLayout, 0, 1); - layout->addLayout(bottomLayout, 1, 0); - layout->addWidget(selectButton, 1, 1); - - setCentralWidget(widget); -} - -void MainWindow::setupModel() -{ - d_ptr->sqlModel = new QSqlTableModel(this); - d_ptr->sqlModel->setTable("Students"); - d_ptr->sqlModel->select(); - // 设置编辑策略 - d_ptr->sqlModel->setEditStrategy(QSqlTableModel::OnManualSubmit); - d_ptr->sqlTabView->setModel(d_ptr->sqlModel); -} - -void MainWindow::showError(const QSqlError &err) -{ - QMessageBox::critical(this, - tr("Unable to initialize Database"), - tr("Error initializing database: ") + err.text()); -} diff --git a/SqlTabview/mainwindow.h b/SqlTabview/mainwindow.h deleted file mode 100644 index d005a97..0000000 --- a/SqlTabview/mainwindow.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include - -class QSqlError; -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = nullptr); - ~MainWindow() override; - -private slots: - void onAdd(); - void onDelete(); - void onUpdate(); - void onSelect(); - void onRevert(); - void onShow(); - -private: - void setupUI(); - void setupModel(); - void showError(const QSqlError &err); - - class MainWindowPrivate; - QScopedPointer d_ptr; -}; -#endif // MAINWINDOW_H diff --git a/SqlTabview/sql.cpp b/SqlTabview/sql.cpp deleted file mode 100644 index eba2474..0000000 --- a/SqlTabview/sql.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "sql.h" - -#include -#include - -static const auto STUDENTS_SQL = QLatin1String(R"( - CREATE TABLE "Students" ( - "ID" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "Name" text, - "Age" integer, - "Gender" text, - "Achievment" TEXT - ) - )"); - -static const auto INSERT_STUDENTS_SQL = QLatin1String(R"( - INSERT INTO "Students"(Name, Age, Gender, Achievment) VALUES (?, ?, ?, ?) - )"); - -static const auto CATALOG_INDEX_STUDENTS_SQL = QLatin1String(R"( - CREATE UNIQUE INDEX "Name" - ON "Students" ( - "Name" COLLATE BINARY ASC - ) - )"); - -Sql::Sql(QObject *parent) - : QObject(parent) -{} - -bool Sql::searchSQLite(QWidget *parent) -{ - if (QSqlDatabase::drivers().contains("QSQLITE")) { - return true; - } - QMessageBox::critical(parent, - tr("Unable to load database"), - tr("This demo needs the SQLITE driver")); - return false; -} - -QSqlError Sql::createOrOpenSqlite() -{ - QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); - db.setDatabaseName("tabview.db"); - - if (!db.open()) { - return db.lastError(); - } - QStringList tables = db.tables(); - if (tables.contains("Students", Qt::CaseInsensitive)) { - return QSqlError(); - } - - QSqlQuery q; - if (!q.exec(STUDENTS_SQL)) { - return q.lastError(); - } - if (!q.prepare(INSERT_STUDENTS_SQL)) { - return q.lastError(); - } - - addStudent(q, QLatin1String("Jason"), 15, QLatin1String("MALE"), 66); - addStudent(q, QLatin1String("Lily"), 13, QLatin1String("FEMALE"), 85); - addStudent(q, QLatin1String("Odin"), 16, QLatin1String("MALE"), 68); - addStudent(q, QLatin1String("Nieo"), 14, QLatin1String("MALE"), 77); - addStudent(q, QLatin1String("Willion"), 12, QLatin1String("MALE"), 89); - - if (!q.exec(CATALOG_INDEX_STUDENTS_SQL)) { - return q.lastError(); - } - - db.close(); - - return QSqlError(); -} - -void Sql::addStudent( - QSqlQuery &q, const QString &name, int age, const QString &gender, int achievment) -{ - q.addBindValue(name); - q.addBindValue(age); - q.addBindValue(gender); - q.addBindValue(achievment); - q.exec(); -} diff --git a/SqlTabview/sql.h b/SqlTabview/sql.h deleted file mode 100644 index e794295..0000000 --- a/SqlTabview/sql.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef SQL_H -#define SQL_H - -#include -#include - -class QSqlQuery; -class Sql : public QObject -{ - Q_OBJECT -public: - explicit Sql(QObject *parent = nullptr); - - static auto searchSQLite(QWidget *parent = nullptr) -> bool; - auto createOrOpenSqlite() -> QSqlError; - -private: - void addStudent(QSqlQuery &, const QString &, int, const QString &, int); -}; - -#endif // SQL_H diff --git a/SqliteWAL/CMakeLists.txt b/SqliteWAL/CMakeLists.txt new file mode 100644 index 0000000..63539a2 --- /dev/null +++ b/SqliteWAL/CMakeLists.txt @@ -0,0 +1,5 @@ +set(PROJECT_SOURCES main.cc sqlutils.cc sqlutils.hpp) + +qt_add_executable(SqliteWAL MANUAL_FINALIZATION ${PROJECT_SOURCES}) +target_link_libraries(SqliteWAL PRIVATE Qt6::Core Qt6::Sql) +qt_finalize_executable(SqliteWAL) diff --git a/SqliteWAL/SqliteWAL.pro b/SqliteWAL/SqliteWAL.pro new file mode 100644 index 0000000..495ec3b --- /dev/null +++ b/SqliteWAL/SqliteWAL.pro @@ -0,0 +1,19 @@ +QT += core sql + +CONFIG += c++17 cmdline + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cc \ + sqlutils.cc + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +HEADERS += \ + sqlutils.hpp diff --git a/SqliteWAL/main.cc b/SqliteWAL/main.cc new file mode 100644 index 0000000..303ad15 --- /dev/null +++ b/SqliteWAL/main.cc @@ -0,0 +1,31 @@ +#include "sqlutils.hpp" + +#include + +#include + +void insertThread(const QString &brand) +{ + SqlUtils sqlUtils; + for (int i = 0; i < 500; i++) { + sqlUtils.insert(brand, i); + } +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + SqlUtils sqlUtils; + + const QStringList brands{"Apple", "Samsung", "Xiaomi", "Huawei", "Oppo", "Vivo", "Realme"}; + std::vector threads; + for (const auto &brand : brands) { + threads.emplace_back(insertThread, brand); + } + for (auto &thread : threads) { + thread.join(); + } + + return 0; +} diff --git a/SqliteWAL/sqlutils.cc b/SqliteWAL/sqlutils.cc new file mode 100644 index 0000000..5b635d5 --- /dev/null +++ b/SqliteWAL/sqlutils.cc @@ -0,0 +1,100 @@ +#include "sqlutils.hpp" + +#include +#include +#include +#include +#include + +class SqlUtils::SqlUtilsPrivate +{ +public: + SqlUtilsPrivate(SqlUtils *q) + : q_ptr(q) + { + if (checkSQLITEDriver()) { + openSqliteDatabase(); + } + } + + ~SqlUtilsPrivate() { QSqlDatabase::removeDatabase(connectionName); } + + bool checkSQLITEDriver() + { + if (QSqlDatabase::drivers().contains("QSQLITE")) { + return true; + } + + qWarning() << "This demo needs the SQLITE driver"; + QMetaObject::invokeMethod(qApp, &QCoreApplication::quit, Qt::QueuedConnection); + return false; + } + + void openSqliteDatabase() + { + s_instanceCount.ref(); + connectionName = QString("connection%1").arg(s_instanceCount.loadRelaxed()); + // qDebug() << connectionName; + auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName); + db.setDatabaseName(databaseName); + if (!db.open()) { + qCritical() << db.lastError().text(); + return; + } + // Enable WAL mode + QSqlQuery walQuery(db); + walQuery.exec("PRAGMA journal_mode = WAL;"); + + auto tables = db.tables(); + if (tables.contains(tableName, Qt::CaseInsensitive)) { + return; + } + const auto createTableSql = QString("CREATE TABLE IF NOT EXISTS %1 (" + "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, " + "brand TEXT, " + "num INTEGER) ") + .arg(tableName); + QSqlQuery query(db); + if (!query.exec(createTableSql)) { + qCritical() << query.lastError().text(); + db.close(); + } + } + + void insert(const QString &brand, int num) + { + auto db = QSqlDatabase::database(connectionName); + if (!db.isValid() || !db.isOpen()) { + return; + } + QSqlQuery query(db); + query.prepare(QString("INSERT INTO %1 (brand, num) VALUES (:brand, :num)").arg(tableName)); + query.bindValue(":brand", brand); + query.bindValue(":num", num); + if (!query.exec()) { + qCritical() << query.lastError().text(); + } + } + + SqlUtils *q_ptr; + + QString connectionName; + const QString databaseName = "example.db"; + const QString tableName = "phone"; + + static QAtomicInt s_instanceCount; +}; + +QAtomicInt SqlUtils::SqlUtilsPrivate::s_instanceCount = 0; + +SqlUtils::SqlUtils(QObject *parent) + : QObject(parent) + , d_ptr(new SqlUtilsPrivate(this)) +{} + +SqlUtils::~SqlUtils() {} + +void SqlUtils::insert(const QString &brand, int num) +{ + d_ptr->insert(brand, num); +} diff --git a/SqliteWAL/sqlutils.hpp b/SqliteWAL/sqlutils.hpp new file mode 100644 index 0000000..f8eb6a2 --- /dev/null +++ b/SqliteWAL/sqlutils.hpp @@ -0,0 +1,19 @@ +#ifndef SQLUTILS_HPP +#define SQLUTILS_HPP + +#include + +class SqlUtils : public QObject +{ +public: + SqlUtils(QObject *parent = nullptr); + ~SqlUtils(); + + void insert(const QString &brand, int num); + +private: + class SqlUtilsPrivate; + QScopedPointer d_ptr; +}; + +#endif // SQLUTILS_HPP