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/)——电池
-## [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/)——密码输入框
-## [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