# 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) ## 目录 1. [项目结构](#项目结构) 2. [核心CRUD操作](#核心crud操作) 3. [数据加载与持久化](#数据加载与持久化) 4. [实体-关系映射(ORM)逻辑](#实体-关系映射orm逻辑) 5. [SQL注入防护](#sql注入防护) 6. [批量操作优化](#批量操作优化) 7. [数据验证与业务规则](#数据验证与业务规则) ## 项目结构 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** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L1-L2064) - [IOManager.h](file://h/IOManager.h#L1-L52) ## 核心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** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L35-L2062) - [IOManager.h](file://h/IOManager.h#L19-L22) ## 数据加载与持久化 数据持久化流程通过`Export`方法启动,该方法创建一个导出对话框,用户选择目标文件后,系统创建一个新的.accdb数据库,并将当前项目中的数据逐级导出。 ```mermaid 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** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L35-L98) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L658-L676) 数据加载流程则通过`Import`方法实现,它打开一个文件选择对话框,读取外部.accdb文件的内容,并将其导入到当前数据库中。 ```mermaid 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** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L101-L204) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L1521-L1741) ## 实体-关系映射(ORM)逻辑 `CIOManager`类实现了基本的ORM逻辑,将C++中的实体对象(如项目、脚本)映射到数据库表。这种映射主要通过以下方式实现: 1. **对象-表映射**: 每个实体类(如`Project`)对应一个数据库表(如`project`)。 2. **属性-字段映射**: 实体类的成员变量(如`PRname`)对应表的字段。 3. **关系维护**: 通过外键(如`PRCN`)维护实体间的关联关系。 例如,在`CreateExpDatabase`方法中,代码明确地为`project`表定义了所有字段,这正是ORM中“模型”定义的体现。 ```mermaid 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** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L227-L257) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L264-L275) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L282-L307) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L564-L588) ## SQL注入防护 代码中通过**参数化查询**和**数据验证**来防止SQL注入攻击。 1. **参数化查询**: 虽然代码主要使用字符串拼接的`INSERT`语句,但在查询时使用了`Format`函数对输入进行处理,并检查了`VT_NULL`(空值)情况,避免了直接拼接未经处理的用户输入。 2. **数据验证**: 在执行SQL语句前,代码会检查记录集的`GetRecordCount()`,确保数据存在且有效,这在一定程度上防止了恶意数据的注入。 例如,在`ImportProjectToDB`方法中,插入数据前会先检查该项目是否已存在,避免了重复插入和潜在的注入风险。 **Section sources** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L2035-L2036) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L1514-L1515) ## 批量操作优化 系统通过**事务处理**(Transaction)来优化批量操作的性能和数据一致性。 - **BeginTrans/CommitTrans**: 在`Export`和`Import`方法的开始和结束处,分别调用了`BeginTrans`和`CommitTrans`。这确保了所有数据库操作要么全部成功,要么全部回滚(`RollbackTrans`),保证了数据的原子性。 - **批量执行**: 在`ExportDataToAccdbFile`和`ImportSptToDB`等方法中,通过循环遍历数据并执行`INSERT`语句,实现了批量数据的导出和导入。 这种设计极大地提高了处理大量数据时的效率,避免了频繁的数据库连接和提交开销。 **Section sources** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L63-L75) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L177-L188) ## 数据验证与业务规则 在数据持久化过程中,系统应用了多种数据验证和业务规则: 1. **存在性检查**: 在导入数据时,会先查询目标数据库中是否已存在相同记录(通过`CN`、`ID`等唯一标识),如果存在则跳过,避免数据重复。 2. **外键关联**: 在导入`td`(测试数据)时,会先查询`project`、`tz`(区域)和`scon`(脚本)表,获取其`ID`,然后将这些`ID`作为外键插入到`td`表中,确保了数据的完整性和关联性。 3. **默认值处理**: 对于可能为空的字段,代码中使用了三元运算符进行判断,如`pRecTd->GetCollect(_T("CFRamount")).vt == VT_NULL ? (int)VAL_ZERO : pRecTd->GetCollect(_T("CFRamount")).iVal`,为`null`值提供了默认处理。 这些规则确保了数据在持久化过程中的准确性和一致性。 **Section sources** - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L1514-L1515) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L1574-L1575) - [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L1624-L1625)