# 数据访问层
**本文档引用的文件**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [IOManager.h](file://h/IOManager.h)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp)
- [database_modify.xml](file://database_modify.xml)
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
Geomative Studio 是一个用于地球物理数据采集和分析的软件系统。本架构文档详细描述了其数据访问层的实现机制,重点分析基于 ADO(ActiveX Data Objects)的数据库访问实现。文档涵盖了 CIOManager 类如何封装数据库连接、查询执行、事务管理和错误处理,解释了数据持久化模式、对象-关系映射策略以及关键数据库操作(如项目导入导出、设备信息同步、测量数据存储)的实现细节。同时,文档还提供了性能优化策略和异常处理机制。
## 项目结构
Geomative Studio 项目采用典型的分层架构,将业务逻辑、数据访问和用户界面分离。数据访问层主要由 `Managers` 和 `Operator` 目录下的类构成,它们通过 ADO 技术与 Microsoft Access 数据库(`.accdb`)进行交互。`Managers` 目录包含核心管理类(如 `CIOManager`, `CProManager`),负责高层次的业务逻辑和数据协调。`Operator` 目录则包含具体的操作类(如 `CDataOperator`, `CSynOperator`),直接执行数据库的 CRUD(创建、读取、更新、删除)操作。
```mermaid
graph TB
subgraph "用户界面"
UI[视图/对话框]
end
subgraph "业务逻辑层"
Managers[Managers目录
CIOManager等]
Operators[Operator目录
DataOperator等]
end
subgraph "数据访问层"
ADO[ADO组件
_ConnectionPtr
_RecordsetPtr]
end
subgraph "数据存储"
DB[(GeoMativeDB.accdb)]
end
UI --> Managers
Managers --> Operators
Operators --> ADO
ADO --> DB
```
**图表来源**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L233-L283)
- [IOManager.h](file://h/IOManager.h#L15-L49)
**本节来源**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L233-L283)
- [IOManager.h](file://h/IOManager.h#L15-L49)
## 核心组件
数据访问层的核心是 `CIOManager` 类,它作为整个系统与数据库交互的中心枢纽。该类在应用程序启动时被初始化,并接收一个全局的 `_ConnectionPtr` 对象,确保所有数据库操作都通过同一个连接进行,从而提高效率并保证事务的一致性。`CIOManager` 封装了数据库的导入、导出功能,并通过 `ADOX::_CatalogPtr` 对象在运行时动态创建和管理数据库结构。
**本节来源**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L430)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L22-L28)
## 架构概述
系统的数据访问架构基于 ADO 技术,利用 COM 接口实现对 Access 数据库的访问。`CIOManager` 类在 `GeoMativeApp` 的 `InitInstance` 方法中被创建,并在整个应用程序生命周期内持有数据库连接。其他管理器(如 `CTdManager`, `CProManager`)在初始化时也会接收同一个 `_ConnectionPtr`,形成一个共享连接的架构。这种设计减少了连接开销,但也要求开发者在多线程环境下谨慎处理连接的并发访问。
```mermaid
sequenceDiagram
participant App as "GeoMativeApp"
participant IO as "CIOManager"
participant Pro as "CProManager"
participant Td as "CTdManager"
participant DB as "Access数据库"
App->>App : 启动程序
App->>App : 创建_ConnectionPtr
App->>IO : 创建CIOManager(连接, 目录)
App->>Pro : 创建CProManager(连接)
App->>Td : 创建CTdManager(连接)
IO->>DB : 执行导入/导出操作
Pro->>DB : 管理项目数据
Td->>DB : 管理测量任务
```
**图表来源**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L233-L283)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L22-L28)
## 详细组件分析
### CIOManager 分析
`CIOManager` 类是数据访问层的核心,负责数据库的连接管理、事务控制和高级数据操作。
#### 类图
```mermaid
classDiagram
class CIOManager {
+_ConnectionPtr m_pConnection
+ADOX : : _CatalogPtr m_pCatalog
+CHandleProcessor m_handleProcessor
+CStateProcessor m_stateProcessor
+CIOManager(_ConnectionPtr&, ADOX : : _CatalogPtr)
+~CIOManager()
+UINT Export()
+UINT Import()
+_ConnectionPtr CreateExpDatabase(CString)
+void ExportDataToAccdbFile(_ConnectionPtr, CTreeCtrl&)
+void ImportProjectToDB(_ConnectionPtr)
}
CIOManager --> _ConnectionPtr : "使用"
CIOManager --> ADOX._CatalogPtr : "使用"
CIOManager --> CHandleProcessor : "包含"
CIOManager --> CStateProcessor : "包含"
```
**图表来源**
- [IOManager.h](file://h/IOManager.h#L15-L49)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L22-L28)
#### 导入导出流程
`CIOManager` 提供了完整的项目导入导出功能。导出时,它首先通过 `CreateExpDatabase` 方法创建一个新的 `.accdb` 文件,并使用 `ADOX::_CatalogPtr` 定义所有必要的表结构(如 `project`, `tz`, `td` 等),然后将当前数据库中的数据逐批写入新文件。导入时,它会验证源文件的版本,然后在一个事务中将数据从源数据库读取并插入到主数据库中,确保数据的原子性和一致性。
```mermaid
flowchart TD
Start([开始导入]) --> CheckFile["验证文件格式和版本"]
CheckFile --> |有效| BeginTrans["开始数据库事务"]
CheckFile --> |无效| ReturnError["返回错误"]
BeginTrans --> ReadData["从源数据库读取数据"]
ReadData --> InsertData["向主数据库插入数据"]
InsertData --> Commit["提交事务"]
InsertData --> Rollback["回滚事务"]
Commit --> End([导入成功])
Rollback --> EndError([导入失败])
style Commit stroke:#0f0,stroke-width:2px
style Rollback stroke:#f00,stroke-width:2px
```
**图表来源**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L100-L204)
**本节来源**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L100-L204)
- [IOManager.h](file://h/IOManager.h#L19-L20)
### 数据持久化模式
系统采用了一种混合的持久化模式。对于复杂的业务对象(如 `Project`, `TestingData`),系统在内存中维护对象模型,并通过 `CDataOperator` 等操作类将对象的状态映射到数据库表的行和列上。这种映射关系是隐式的,通过 SQL 查询和记录集(`_RecordsetPtr`)来实现,而不是一个成熟的 ORM(对象关系映射)框架。
#### 对象-关系映射策略
数据加载和保存流程通常遵循以下模式:
1. **加载**:构造一个 SQL 查询语句,打开一个 `_RecordsetPtr`,然后遍历记录集,为每一行创建相应的 C++ 对象,并将数据库字段的值赋给对象的属性。
2. **保存**:当对象状态改变时,生成相应的 `INSERT`, `UPDATE`, 或 `DELETE` SQL 语句,并通过 `_CommandPtr` 执行。
例如,在 `CDataOperator::CreateProjectInDB` 中,通过 `m_pConnection->BeginTrans()` 开启事务,调用业务方法创建项目,然后通过 SQL 查询获取新创建的项目信息并更新 UI 树形控件,最后提交事务。
**本节来源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp#L618-L668)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L97-L200)
### 关键数据库操作实现
#### 项目导入导出
如上所述,`CIOManager` 类的 `Export` 和 `Import` 方法实现了完整的项目级数据迁移。导出过程会创建一个包含所有必要表结构的新数据库文件,并将选中的项目、测区、测量任务等数据复制过去。导入过程则会检查源文件的兼容性,并安全地将数据合并到当前数据库中。
#### 设备信息同步
设备信息同步由 `CSynOperator` 类处理。该类在构造时接收数据库连接,其 `Syn` 方法会创建一个同步对话框(`COpSynDlg`),允许用户选择设备进行同步。同步逻辑在对话框内部实现,通过查询 `device` 表来获取设备列表,并与现场设备进行数据交换。
**本节来源**
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp#L21-L55)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L100-L204)
#### 测量数据存储
测量数据的存储是一个关键操作,由 `CTaskDataOper` 类负责。当创建一个新的测量任务(如一维任务)时,`Create1DTask` 方法会:
1. 查询脚本信息(`scon` 表)以获取电极数量等参数。
2. 获取默认测区 ID。
3. 在 `td` 表中插入一条新的任务记录。
4. 在 `tdchannel` 表中插入通道信息。
5. 批量将脚本中的测量点(`script1d` 表)复制到任务的测量点表(`td1dcon`)中。
整个过程在一个数据库事务中完成,确保了数据的完整性。
**本节来源**
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L97-L200)
## 依赖分析
数据访问层严重依赖于 ADO 和 ADOX 库,这些库通过 `msado15.tlh` 和 `msadox.tlh` 头文件在编译时引入。`CIOManager` 依赖于 `CHandleProcessor` 和 `CStateProcessor` 来处理句柄和状态转换。业务逻辑层的各个管理器(`CProManager`, `CTdManager` 等)都直接依赖于 `CIOManager` 建立的数据库连接。
```mermaid
graph LR
IOManager --> ADO
IOManager --> ADOX
IOManager --> HandleProcessor
IOManager --> StateProcessor
ProManager --> IOManager
TdManager --> IOManager
DevManager --> IOManager
ExecManager --> IOManager
```
**图表来源**
- [IOManager.h](file://h/IOManager.h#L8-L9)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L425-L430)
**本节来源**
- [IOManager.h](file://h/IOManager.h#L8-L9)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L425-L430)
## 性能考虑
系统在性能方面采取了以下策略:
1. **连接复用**:全局使用单个 `_ConnectionPtr`,避免了频繁建立和断开数据库连接的开销。
2. **事务管理**:对于批量操作(如项目导入、任务创建),使用 `BeginTrans`/`CommitTrans` 将多个 SQL 操作包装在一个事务中,显著提高了写入性能。
3. **批量操作**:在数据迁移时,使用 `INSERT INTO ... SELECT` 语句进行批量数据复制,而不是逐条插入。
4. **连接参数优化**:在连接字符串中设置了 `Jet OLEDB:Max Locks Per File`,以避免在高并发操作时出现锁冲突。
**本节来源**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L282)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L63-L75)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L148-L197)
## 故障排除指南
数据访问层的异常处理主要通过捕获 `_com_error` 异常来实现。当 ADO 操作失败时,会立即回滚当前事务(`RollbackTrans`),关闭连接,并向用户显示错误信息。例如,在数据库连接失败或 SQL 执行出错时,系统会弹出消息框提示用户。此外,`UpdateDataBase` 类在数据库升级时也会进行详细的日志记录(`update_db.log`),便于排查版本更新问题。
**本节来源**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L293-L309)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L80-L86)
- [UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp#L26)
## 结论
Geomative Studio 的数据访问层基于 ADO 技术构建,设计清晰,职责明确。`CIOManager` 作为核心组件,有效地封装了数据库连接和事务管理,为上层业务逻辑提供了稳定的数据访问接口。系统通过共享连接、事务和批量操作等策略保证了数据操作的性能和一致性。尽管其对象-关系映射较为基础,但对于当前的应用场景是足够且高效的。未来的改进方向可以考虑引入更现代的 ORM 框架或迁移到更强大的数据库系统以支持更大的数据量和更复杂的查询。