a
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
# CRUD操作实现
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [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)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
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)
|
||||
Reference in New Issue
Block a user