(11)功能实现:Qt实战项目之新建db文件

上节课我们完成了新建项目对话框的搭建,本节课将聚焦其核心功能逻辑的实现。此前我们已在对话框中配置了保存路径设置、项目名称定义等基础交互项,接下来的核心目标是:当用户点击 "确定" 按钮时,系统将自动为该项目创建专属的 db 文件,用于统一存储项目全量数据。这也是本节课实战的核心任务。

我们会在本节课引入SQLite数据库,SQLite 是一款轻量级、嵌入式关系型数据库,核心特点是 无需独立服务器、零配置、文件级存储,堪称 "数据库界的 U 盘"------ 一个完整数据库就是单个文件(后缀通常为 .db),无需安装服务、无需账号密码,复制文件即可迁移,适配 Windows、Linux、macOS 等全平台。

Qt 5 及以上版本已内置 SQLite 支持,可直接调用相关接口使用,无需额外配置 ------ 既不用单独安装数据库服务,也无需进行复杂的环境部署与管理。更便捷的是,一个完整的 SQLite 数据库会以单个文件的形式存储,且该文件具备跨平台兼容性,使用起来十分灵活。

创建DBOperator项目

创建一个空项目

我们在VS的解决方案下创建一个新的空项目,主要用来处理数据库操作的逻辑,项目类型"空项目",项目名称"DBOperator"

Debug和Release下配置属性→常规:

  • 配置类型改为:动态库(.dll)
  • C++语言标准改为:ISO C++17 标准 (/std:c++17)

创建数据库管理类

添加数据库连接、初始化的类DBManager。

++注意:VS 不自动生成构造函数和析构函数是有意为之,符合 C++ 的 "零开销原则" 和现代编程实践,编译器会给生成默认版本。实际根据自己的需要来决定是否手动添加。++

手动添加构造函数、析构函数,如下:

cpp 复制代码
class DBManager
{
public:
	DBManager();
	~DBManager();
};

创建一个头文件DBOperator.h,并声明DLL导出的函数

cpp 复制代码
# if defined(DBOPERATOR_LIB)
#  define DBOPERATOR_API __declspec(dllexport)
# else
#  define DBOPERATOR_API __declspec(dllimport)
# endif

这段代码是 Windows 平台下 DLL 项目中常用的宏定义技巧,用于控制函数和类的导入 / 导出行为。

++注意:当前项目是作为导出函数,我们我们在 DLL 项目的属性页(配置属性 → C/C++ → 预处理器 → 预处理器定义)中添加DBOPERATOR_LIB。++

回到数据库管理类,引用DBOperator.h,并将该类设置为导出类。

cpp 复制代码
#include "DBOperator.h"
class DBOPERATOR_API DBManager
{
public:
	DBManager();
	~DBManager();
};

数据库初始化

利用SQLite3创建数据库的过程如下图,①创建数据库文件;②创建表结构;③关闭数据库连接; 其中数据库文件的路径是我们在创建项目的对话框中设置的路径。

定义initDatabase()函数

我们在DBManager类中,添加一个函数initDatabase()函数用来创建数据库文件、创建表结构,在DBManager类的析构函数中关闭数据库连接;

定义initDatabase函数,引入#include <QString>

cpp 复制代码
void initDatabase(QString strFilePath);

注意:此处会提示找不到QString。

原因是:我们建立的空项目未引入Qt库。

解决方案:以下内容Debug/Release都配置
配置属性 → C/C++ → 常规 →附加包含目录:

$(QTDIR)\include

$(QTDIR)\include\QtCore

$(QTDIR)\include\QtWidgets

$(QTDIR)\include\QtSql
配置属性→链接器→常规→附加库目录:

$(QTDIR)\lib #表示 Qt 安装目录下的 lib 文件夹,用于引用Qt 官方库文件(如 Qt5Core.lib等)

$(OutDir) #表示当前项目的输出目录(通常是 Debug 或 Release 文件夹)
配置属性→链接器→输入→附加依赖项:(debug下带d后缀)

Qt6Cored.lib

Qt6Widgetsd.lib

Qt6Sqld.lib

其中:顶层目录(即$(QTDIR)\include):这里存放着一些通用的头文件和模块索引文件。
如果编译报错:"Qt requires a C++17 compiler, and a suitable value for __cplusplus. On MSVC, you must pass the /Zc:__cplusplus option to the compiler."

原因是:MSVC 编译器默认不会正确定义__cplusplus宏,需要手动添加/Zc:__cplusplus选项

解决方案:配置属性→c/c++→命令行→其他选项框里添加 /Zc:__cplusplus

编译通过后,我们来实现initDatabase函数,首先需要导入头文件,导入SQL操作涉及的头文件:

#include <QSqlDatabase>

#include <QSqlQuery>

#include <QSqlQueryModel>

#include <QSqlError>

定义全局变量QSqlDatabase sql_db :负责数据库连接的建立和管理。定义全局变量智能指针sql_query:负责执行 SQL 语句和处理结果集。智能指针自动管理内存,防止内存泄漏。之所以定义成指针的形式,是因为sql_query对象在后续SQL的处理时会频繁使用。

cpp 复制代码
private:
	QSqlDatabase sql_db;
	std::unique_ptr<QSqlQuery> sql_query; 

数据库连接管理

首先定义连接名称。在 Qt 里,默认连接名为 "qt_sql_default_connection",我们此处定义为cfgConnection。 对名称为cfgC的数据库连接是否存在进行检查,若存在则将其移除。原因是每个连接名必须是独一无二的,要是多次运行添加连接,就会因为重复添加同名连接而产生错误。先移除已有的连接,能够保证后续添加连接时不会出现冲突。

cpp 复制代码
 // 定义连接名称
 const QString connectionName = "cfgConnection";
    // 移除已存在的连接
 if (QSqlDatabase::contains(connectionName)) 
    {
        QSqlDatabase::removeDatabase(connectionName);
    }

创建数据库连接

通过addDatabase函数新建数据库连接,第一个参数"QSQLITE" 表明使用 SQLite 嵌入式数据库,第二个参数如果不指定连接名,就会使用默认连接名,也可以指定自定义的连接名称。

通过setDatabaseName函数设置数据库文件的路径:

cpp 复制代码
// 创建数据库连接
sql_db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
sql_db.setDatabaseName(strFilePath);

打开数据库连接

尝试打开数据库文件。如果文件不存在,则会自动创建该文件。

cpp 复制代码
    // 打开数据库
    if (!sql_db.open()) 
    {
        qDebug() << "连接数据库失败:" << sql_db.lastError().text();
        return;
    }
    qDebug() << "连接数据库成功";

启用外键约束

在 SQLite 中,外键约束默认是关闭的,在实际项目中,外键约束的使用存在明显的 "场景分化" ------ 并非 "一刀切" 地使用或禁用,而是取决于项目的架构设计、数据一致性要求、性能需求、团队协作模式等核心因素。可通过如下代码开启外键约束:

cpp 复制代码
QSqlQuery query(sql_db);
if (!query.exec("PRAGMA foreign_keys=ON")) 
{
   qDebug() << "启用外键约束失败:" << query.lastError().text();
   return;
}

查询对象动态构造

创建 QSqlQuery 智能指针对象,QSqlQuery 对象的作用是执行 SQL 语句。sql_db 作为参数被传入,意味着这个查询对象会使用 sql_db的连接来执行 SQL 语句。

cpp 复制代码
// 使用栈上对象替代new操作
sql_query = std::make_unique<QSqlQuery>(sql_db);

关闭数据库

在该类的析构函数中关闭数据库。

cpp 复制代码
DBManager::~DBManager()
{
	if (sql_db.isOpen())
	{
		sql_db.close();
	}
}

创建数据库表结构

我们定义个新的方法,专门用来创建表结构,后续有表的增加,也统一在这个方法中维护。我们以创建一个设备表为例,首先定义创建表的SQL语句,然后利用我们定义的智能指针sql_query来执行这个sql语句:

cpp 复制代码
void DBManager::createTables()
{
	//设备表:ID、设备名称、类型、型号ID、间隔ID
	QString strProjectDevice = QString("CREATE TABLE project_device(\
                                       device_id              VARCHAR(32) PRIMARY KEY NOT NULL,\
                                       device_name            VARCHAR(32) NOT NULL,\
                                       device_type              VARCHAR(32),\
                                       model_id         VARCHAR(32),\
                                       bay_id         VARCHAR(32),\
                                       create_time        TIMESTAMP DEFAULT(DATETIME('now','localtime')))");
	if (!sql_query->exec(strProjectDevice))
	{
		qDebug() << "Error:Fail to create table project_device." << sql_query->lastError();
	}
	else
	{
		qDebug() << "table project_device created!";
	}
}

定义全局访问

因为这个DBManager类后续会经常使用,所以我们使用单例模式构建一个该类的一个实例,以便提供一个全局访问点来获取这个实例。

先在头文件定义一个该类的静态对象:

cpp 复制代码
private:
   static DBManager* instance;

然后在cpp文件中进行对象的初始化:

cpp 复制代码
DBManager* DBManager::instance = nullptr;

再定义个获取实例的方法:

cpp 复制代码
static DBManager* getInstance();

通过该方法返回instance对象,如果为空则重新创建,如果不为空直接返回已经创建的实例:

cpp 复制代码
DBManager* DBManager::getInstance()
{
	if (instance == nullptr)
	{
		instance = new DBManager;
	}
	return instance;
}

创建工程的逻辑

我们回到上节课的新建项目对话框,补全逻辑。新建项目的时候,设置了项目名称和项目位置,点击确定之后,我们获取到db文件的完整路径,所以在对话框的类中增加以下逻辑:

cpp 复制代码
void NewProjectDlg::accept()
{
	// 获取db文件的完整路径
	QString strProFilePath = lineEdtProDirPath->text() + lineEdtProName->text() + ".db";
	DBManager::getInstance()->initDatabase(strProFilePath);
	DBManager::getInstance()->createTables();

	QDialog::accept();
}

然后就可以调用DBManager类的静态函数getInstance,需要引入头文件:#include "DBManager.h",并需要在配置属性中添加上引用。

配置属性→VC++目录→包含目录:

../DBOperator 注:../ 代表上一级目录

配置属性→链接器→常规→附加库目录:

$(QTDIR)\lib #表示 Qt 安装目录下的 lib 文件夹,用于引用Qt 官方库文件(如 Qt5Core.lib等)

$(OutDir) #表示当前项目的输出目录(通常是 Debug 或 Release 文件夹)

配置属性→链接器→输入→附加依赖项:

DBOperator.lib

编译程序,发现报错。

提示错误:

E1696 无法包括源文件 "QSqlDatabase"

E1696 无法.......

解决方案:配置主项目SubCfgTool的信息

配置属性 → C/C++ → 常规 →附加包含目录:

$(QTDIR)\include

$(QTDIR)\include\QtCore

$(QTDIR)\include\QtWidgets

$(QTDIR)\include\QtSql

配置属性→链接器→输入→附加依赖项:(debug下带d后缀)

Qt6Cored.lib

Qt6Widgetsd.lib

Qt6Sqld.lib

重新编译成功。测试创建效果,可以通过菜单栏创建一个项目,并生成一个db文件,用Navicat或DBeaver打开即可看到新建的表。

至此新建项目的核心逻辑就算完成了,接下来后续的开发中产生的业务数据都将存放在该db文件中,且随着业务的不断复杂,表结构会越来越丰富。

相关推荐
e***282937 分钟前
mybatisPlus打印sql配置
数据库·sql
+VX:Fegn089540 分钟前
计算机毕业设计|基于springboot + vue二手交易管理系统(源码+数据库+文档)
数据库·vue.js·spring boot
V***u45343 分钟前
sql实战解析-sum()over(partition by xx order by xx)
数据库·sql
范纹杉想快点毕业44 分钟前
《STM32深度100问:AI助教工程师的实战问答录》从入门到精通适用入门嵌入式软件初级工程师,筑牢基础,技术积累
arm开发·数据库·驱动开发·mongodb·fpga开发
修己xj1 小时前
使用Docker Compose轻松部署达梦数据库
数据库
合方圆~小文1 小时前
智能变焦球机:全方位监控升级新标杆
数据库·人工智能·前端框架
二宝1521 小时前
黑马商城day10-Redis面试篇
数据库·redis·面试
xiegwei1 小时前
spring security oauth2 集成异常处理
数据库·spring·spring security
siriuuus1 小时前
带你了解 Redis —— 基础知识总结
数据库·redis·缓存