8.0 KiB
CRUD操作实现
**本文档引用的文件** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp) - [IOManager.h](file://h/IOManager.h) - [Project.h](file://h/Project.h) - [Device.h](file://h/Device.h) - [Script.h](file://h/Script.h) - [TestingData.h](file://h/TestingData.h)目录
项目结构
Geomative Studio项目是一个地质勘探数据管理软件,其核心功能围绕项目、设备、脚本和测试数据的管理。项目采用C++与MFC框架开发,使用Access数据库(.accdb)作为数据存储,通过ADO(ActiveX Data Objects)技术实现数据库访问。
项目的主要目录结构包括:
- CACHE/: 缓存文件目录
- DB/: 数据库相关文件
- Install/: 安装包和配置文件
- LOG/: 日志文件
- Release/: 编译输出文件
- cpp/Managers/: 核心管理类,包含
IOManager.cpp等关键文件 - h/: 头文件目录,包含
IOManager.h、Project.h等实体类定义
Diagram sources
核心CRUD操作
CIOManager类是实现CRUD(创建、读取、更新、删除)操作的核心。它通过ADO的_ConnectionPtr和_CatalogPtr指针与数据库建立连接,并提供Export和Import两大核心功能,分别对应数据的导出(持久化)和导入(加载)。
创建(Create)与读取(Read)
- 创建: 通过
Import方法从外部.accdb文件导入数据,系统会检查数据是否存在,若不存在则执行INSERT语句创建新记录。 - 读取: 使用
_RecordsetPtr对象执行SELECT查询,通过Open方法打开记录集,并利用GetCollect方法获取字段值。
更新(Update)与删除(Delete)
- 更新: 在
ImportSptToDB方法中,通过UPDATE语句更新td表中的SCID字段,确保脚本与测试数据的关联。 - 删除: 虽然代码中未直接体现
DELETE语句,但通过DeleteFile函数在创建导出数据库前删除同名文件,体现了对旧数据的清理。
Section sources
数据加载与持久化
数据持久化流程通过Export方法启动,该方法创建一个导出对话框,用户选择目标文件后,系统创建一个新的.accdb数据库,并将当前项目中的数据逐级导出。
flowchart TD
A[启动导出] --> B[创建导出对话框]
B --> C{用户选择文件}
C --> |确认| D[创建新数据库]
D --> E[开始事务 BeginTrans]
E --> F[导出项目数据]
F --> G[导出区域数据]
G --> H[导出脚本数据]
H --> I[提交事务 CommitTrans]
I --> J[完成导出]
C --> |取消| K[取消导出]
Diagram sources
数据加载流程则通过Import方法实现,它打开一个文件选择对话框,读取外部.accdb文件的内容,并将其导入到当前数据库中。
flowchart TD
A[启动导入] --> B[选择.accdb文件]
B --> C{文件有效?}
C --> |是| D[开始事务 BeginTrans]
D --> E[导入项目数据]
E --> F[导入区域数据]
F --> G[导入脚本数据]
G --> H[导入测试数据]
H --> I[提交事务 CommitTrans]
I --> J[完成导入]
C --> |否| K[显示错误并取消]
Diagram sources
实体-关系映射(ORM)逻辑
CIOManager类实现了基本的ORM逻辑,将C++中的实体对象(如项目、脚本)映射到数据库表。这种映射主要通过以下方式实现:
- 对象-表映射: 每个实体类(如
Project)对应一个数据库表(如project)。 - 属性-字段映射: 实体类的成员变量(如
PRname)对应表的字段。 - 关系维护: 通过外键(如
PRCN)维护实体间的关联关系。
例如,在CreateExpDatabase方法中,代码明确地为project表定义了所有字段,这正是ORM中“模型”定义的体现。
erDiagram
project {
string CN PK
string PRname
string PRdesc
string location
date PRdate
int duration
string PS
string CS
string PM
string QAS
string standard
}
tz {
string TZname PK
string PRCN FK
string TZdesc
string location
}
td {
string TDname PK
string DESN
string PRCN FK
string TZname FK
string SCCN FK
int TDtype
string HFname
string CFname
int CFRamount
string GRFname
int GRFRamount
}
scon {
string CN PK
string Sname
int Eamount
int CHamount
int TPamount
string definer
date DEdate
string SCdesc
}
project ||--o{ tz : "包含"
project ||--o{ td : "包含"
scon ||--o{ td : "关联"
Diagram sources
SQL注入防护
代码中通过参数化查询和数据验证来防止SQL注入攻击。
- 参数化查询: 虽然代码主要使用字符串拼接的
INSERT语句,但在查询时使用了Format函数对输入进行处理,并检查了VT_NULL(空值)情况,避免了直接拼接未经处理的用户输入。 - 数据验证: 在执行SQL语句前,代码会检查记录集的
GetRecordCount(),确保数据存在且有效,这在一定程度上防止了恶意数据的注入。
例如,在ImportProjectToDB方法中,插入数据前会先检查该项目是否已存在,避免了重复插入和潜在的注入风险。
Section sources
批量操作优化
系统通过事务处理(Transaction)来优化批量操作的性能和数据一致性。
- BeginTrans/CommitTrans: 在
Export和Import方法的开始和结束处,分别调用了BeginTrans和CommitTrans。这确保了所有数据库操作要么全部成功,要么全部回滚(RollbackTrans),保证了数据的原子性。 - 批量执行: 在
ExportDataToAccdbFile和ImportSptToDB等方法中,通过循环遍历数据并执行INSERT语句,实现了批量数据的导出和导入。
这种设计极大地提高了处理大量数据时的效率,避免了频繁的数据库连接和提交开销。
Section sources
数据验证与业务规则
在数据持久化过程中,系统应用了多种数据验证和业务规则:
- 存在性检查: 在导入数据时,会先查询目标数据库中是否已存在相同记录(通过
CN、ID等唯一标识),如果存在则跳过,避免数据重复。 - 外键关联: 在导入
td(测试数据)时,会先查询project、tz(区域)和scon(脚本)表,获取其ID,然后将这些ID作为外键插入到td表中,确保了数据的完整性和关联性。 - 默认值处理: 对于可能为空的字段,代码中使用了三元运算符进行判断,如
pRecTd->GetCollect(_T("CFRamount")).vt == VT_NULL ? (int)VAL_ZERO : pRecTd->GetCollect(_T("CFRamount")).iVal,为null值提供了默认处理。
这些规则确保了数据在持久化过程中的准确性和一致性。
Section sources