244 lines
13 KiB
Markdown
244 lines
13 KiB
Markdown
|
|
对象缓存机制是SunvStation地理编辑系统的核心组件,用于在批量创建和修改地理对象时提供高效的内存管理。该机制通过在内存中暂存对象,避免频繁的数据库I/O操作,显著提升大批量数据处理的性能。缓存机制区分了新建对象和修改对象两种场景,分别采用不同的缓存策略,并通过统一的事务性接口完成数据库持久化。
|
|||
|
|
|
|||
|
|
Sources: [PySSProcess.py](PySSProcess.py#L26-L46)
|
|||
|
|
|
|||
|
|
## 缓存架构概述
|
|||
|
|
|
|||
|
|
对象缓存机制采用多层次的缓存架构,通过SSProcessManager类的成员变量管理不同类型的缓存。缓存系统分为三大类别:新建对象缓存、修改对象缓存和删除对象缓存,每种类别又进一步区分为地物对象和注记对象。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
classDiagram
|
|||
|
|
class SSProcessManager {
|
|||
|
|
+GeoBaseList bufferObjList
|
|||
|
|
+GeoBaseList bufferNoteList
|
|||
|
|
+list newBufferObjList
|
|||
|
|
+list newBufferNoteList
|
|||
|
|
+tuple curNewObj
|
|||
|
|
+GeoBaseList delBufferGeoList
|
|||
|
|
+createNewObjByCode(code)
|
|||
|
|
+setNewObjValue(field, value)
|
|||
|
|
+addNewObjPoint(x, y, z, type, name)
|
|||
|
|
+addNewObjToSaveObjList()
|
|||
|
|
+saveBufferObjToDatabase()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class GeoEditMixin {
|
|||
|
|
<<Mixin>>
|
|||
|
|
+transMemoDataToExtendAttr(geoList)
|
|||
|
|
+persistTempGeoList(geoVec)
|
|||
|
|
+persistTempNoteList(noteVec)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class GeoBaseList {
|
|||
|
|
+append(geo)
|
|||
|
|
+empty()
|
|||
|
|
+clear()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class GeoBase {
|
|||
|
|
+getObjectType()
|
|||
|
|
+getCode()
|
|||
|
|
+setMemoData(fields, values)
|
|||
|
|
+getDatasetName()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
SSProcessManager *-- GeoBaseList : 管理缓存列表
|
|||
|
|
SSProcessManager *-- GeoBase : 封装几何对象
|
|||
|
|
SSProcessManager --> GeoEditMixin : 继承缓存操作
|
|||
|
|
GeoBaseList o-- GeoBase : 包含对象
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
该架构体现了**关注点分离**的设计原则:SSProcessManager负责缓存容器管理,GeoEditMixin提供缓存操作的具体实现,GeoBase和GeoBaseList作为数据载体。缓存对象以pair形式存储,其中第一个元素是GeoObject实例(包含数据集引用和唯一ID),第二个元素是具体的几何对象(PointObject、LineObject、AreaObject或MarkNote)。
|
|||
|
|
|
|||
|
|
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L28-L54)
|
|||
|
|
|
|||
|
|
## 缓存类型与分类
|
|||
|
|
|
|||
|
|
缓存机制根据对象状态和类型进行明确分类,这种设计使得系统能够对不同场景采用优化的处理策略。
|
|||
|
|
|
|||
|
|
### 按操作状态分类
|
|||
|
|
|
|||
|
|
| 缓存类型 | 存储容器 | 对象来源 | 持久化方式 | 典型应用场景 |
|
|||
|
|
|---------|---------|---------|-----------|------------|
|
|||
|
|
| 新建对象缓存 | `newBufferObjList` / `newBufferNoteList` | 脚本创建的新对象 | `addGeoObject()` 添加到地图 | 批量导入数据、程序化生成地物 |
|
|||
|
|
| 修改对象缓存 | `bufferObjList` / `bufferNoteList` | 从地图加载并修改的对象 | `saveDatabase()` 更新数据库 | 属性批量更新、几何形状编辑 |
|
|||
|
|
| 删除对象缓存 | `delBufferGeoList` | 标记为删除的对象 | `delGeoBaseComponent()` 执行删除 | 清理过期数据、对象合并操作 |
|
|||
|
|
| 当前新建对象 | `curNewObj` | 正在创建过程中的临时对象 | 转移至其他缓存或丢弃 | 交互式创建、参数化建模 |
|
|||
|
|
|
|||
|
|
### 按对象类型分类
|
|||
|
|
|
|||
|
|
地物对象和注记对象的物理存储位置不同,系统通过`getObjectType()`方法进行类型识别(e_Point_Obj=0、e_Line_Obj=1、e_Area_Obj=2、e_Note_Obj=3),并自动路由到相应的缓存容器中。
|
|||
|
|
|
|||
|
|
Sources: [PySSProcess.py](PySSProcess.py#L32-L40)
|
|||
|
|
|
|||
|
|
## 缓存生命周期管理
|
|||
|
|
|
|||
|
|
对象的生命周期在缓存系统中遵循明确的阶段划分,从对象创建开始,经过属性设置和几何编辑,最终通过事务性接口持久化到数据库。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
A[开始创建对象] --> B{创建方式}
|
|||
|
|
B -->|通过编码创建| C[createNewObjByCode]
|
|||
|
|
B -->|通过注记类创建| D[createNewObjByClass]
|
|||
|
|
B -->|默认类型创建| E[createDefaultGeoBase]
|
|||
|
|
|
|||
|
|
C --> F[curNewObj 临时缓存]
|
|||
|
|
D --> F
|
|||
|
|
E --> F
|
|||
|
|
|
|||
|
|
F --> G[setNewObjValue 设置属性]
|
|||
|
|
G --> H[addNewObjPoint 添加坐标点]
|
|||
|
|
H --> I{下一步操作}
|
|||
|
|
|
|||
|
|
I -->|加入选择集| J[addNewObjToSelObjList]
|
|||
|
|
I -->|加入保存列表| K[addNewObjToSaveObjList]
|
|||
|
|
I -->|放弃创建| L[deleteNewObj]
|
|||
|
|
|
|||
|
|
K --> M[newBufferObjList/newBufferNoteList]
|
|||
|
|
J --> N[selGeoList/selNoteList]
|
|||
|
|
|
|||
|
|
M --> O[saveBufferObjToDatabase 批量保存]
|
|||
|
|
N --> O
|
|||
|
|
|
|||
|
|
O --> P[transMemoDataToExtendAttr 属性转换]
|
|||
|
|
P --> Q[map.addGeoObject 添加到地图]
|
|||
|
|
Q --> R[map.saveDatabase 写入数据库]
|
|||
|
|
R --> S[清除缓存列表]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
该生命周期体现了**延迟加载**和**批量处理**的优化策略:对象在内存中完成所有编辑操作,仅在最后阶段才与数据库交互,显著减少了网络往返和数据锁争用。
|
|||
|
|
|
|||
|
|
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L120-L210)
|
|||
|
|
|
|||
|
|
## 核心API详解
|
|||
|
|
|
|||
|
|
### 对象创建API
|
|||
|
|
|
|||
|
|
`createNewObjByCode()` 是新建对象的主要入口,通过编码获取地物定义并创建相应类型的几何对象。该方法会自动从当前数据源获取特征定义,包括对象类型、颜色、线型、线宽等属性,并应用到新创建的对象上。
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 通过编码创建新地物对象
|
|||
|
|
obj, geo = SSProcess.createNewObjByCode(3103013)
|
|||
|
|
if obj is not None:
|
|||
|
|
# 设置对象属性
|
|||
|
|
SSProcess.setNewObjValue("SSObj_Name", "新建道路")
|
|||
|
|
SSProcess.setNewObjValue("[宽度]", "15.5")
|
|||
|
|
|
|||
|
|
# 添加坐标点(线对象)
|
|||
|
|
SSProcess.addNewObjPoint(100.0, 200.0, 0.0, 0, "")
|
|||
|
|
SSProcess.addNewObjPoint(150.0, 250.0, 0.0, 0, "")
|
|||
|
|
|
|||
|
|
# 将对象加入保存列表
|
|||
|
|
SSProcess.addNewObjToSaveObjList()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L133-L163)
|
|||
|
|
|
|||
|
|
### 属性设置API
|
|||
|
|
|
|||
|
|
`setNewObjValue()` 方法支持基本属性和扩展属性的统一设置。基本属性以`SSObj_`前缀标识,扩展属性使用方括号`[]`包裹。该方法还支持批量设置多个扩展属性,通过逗号分隔属性名和值。
|
|||
|
|
|
|||
|
|
**属性设置对比表:**
|
|||
|
|
|
|||
|
|
| 属性类型 | 语法格式 | 存储位置 | 转换逻辑 |
|
|||
|
|
|---------|---------|---------|---------|
|
|||
|
|
| 基本属性 | `SSObj_Name` | 对象内置字段 | 直接映射到GeoBase的getter/setter |
|
|||
|
|
| 单个扩展属性 | `[自定义字段名]` | MemoData字段 | 通过StringArray键值对存储 |
|
|||
|
|
| 批量扩展属性 | `[字段1],[字段2]` | MemoData字段 | 解析后分别设置键值对 |
|
|||
|
|
|
|||
|
|
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L193-L260)
|
|||
|
|
|
|||
|
|
### 批量持久化API
|
|||
|
|
|
|||
|
|
`saveBufferObjToDatabase()` 是缓存机制的核心事务接口,负责将所有缓存中的对象持久化到数据库。该方法按数据集分组处理对象,先调用`addGeoObject()`添加新对象,再通过`transMemoDataToExtendAttr()`将MemoData转换为扩展属性,最后调用`saveDatabase()`提交修改。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant API as saveBufferObjToDatabase()
|
|||
|
|
participant Cache as 缓存列表
|
|||
|
|
participant Map as ScaleMap
|
|||
|
|
participant DB as 数据库
|
|||
|
|
|
|||
|
|
API->>Cache: 读取newBufferObjList/newBufferNoteList
|
|||
|
|
API->>API: 按数据集分组
|
|||
|
|
loop 每个数据集分组
|
|||
|
|
API->>Map: addGeoObject(geoList)
|
|||
|
|
API->>Map: setExtentAttr(geo, fields, values)
|
|||
|
|
end
|
|||
|
|
API->>Cache: 清空newBufferObjList/newBufferNoteList
|
|||
|
|
|
|||
|
|
API->>Cache: 读取bufferObjList/bufferNoteList
|
|||
|
|
API->>Map: setExtentAttr(geo, fields, values)
|
|||
|
|
API->>Map: saveDatabase(geoList)
|
|||
|
|
API->>Cache: 清空bufferObjList/bufferNoteList
|
|||
|
|
|
|||
|
|
API->>Map: delGeoBaseComponent(delBufferGeoList)
|
|||
|
|
API->>Cache: 清空delBufferGeoList
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
该方法实现了**原子性**的事务特性:所有缓存对象的保存操作作为一个整体提交,成功则全部生效,失败则回滚至保存前状态。同时,通过数据集分组优化了数据库锁粒度,提高了并发性能。
|
|||
|
|
|
|||
|
|
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L475-L508)
|
|||
|
|
|
|||
|
|
### 备忘数据转换API
|
|||
|
|
|
|||
|
|
`transMemoDataToExtendAttr()` 方法负责将对象的备忘数据(MemoData)转换为扩展属性。这是必要的中间步骤,因为MemoData是Python层面的临时存储格式,而扩展属性是数据库层面的持久化格式。
|
|||
|
|
|
|||
|
|
转换过程在事务边界内进行(`beginSetExtentAttr()` / `endSetExtentAttr()`),确保扩展属性设置的原子性。对于具有大量扩展属性的对象,该操作可能产生显著的性能开销,建议批量处理时控制对象数量。
|
|||
|
|
|
|||
|
|
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L464-L473)
|
|||
|
|
|
|||
|
|
## 缓存与选择集的关系
|
|||
|
|
|
|||
|
|
缓存列表与选择集是两个独立但相关的容器系统。选择集(`selGeoList`、`selNoteList`)用于对象的逻辑分组和查询,而缓存列表(`bufferObjList`、`newBufferObjList`等)用于对象的事务性持久化。
|
|||
|
|
|
|||
|
|
**关系对比表:**
|
|||
|
|
|
|||
|
|
| 容器类型 | 主要用途 | 数据来源 | 是否持久化 | 清除时机 |
|
|||
|
|
|---------|---------|---------|-----------|---------|
|
|||
|
|
| 选择集 | 逻辑分组、批量查询 | 从地图加载或新建对象添加 | 否 | clearSelection()手动清除 |
|
|||
|
|
| 新建对象缓存 | 事务性保存新对象 | 通过createNewObjByCode创建 | 是 | saveBufferObjToDatabase()自动清除 |
|
|||
|
|
| 修改对象缓存 | 事务性更新现有对象 | 从选择集加载并修改 | 是 | saveBufferObjToDatabase()自动清除 |
|
|||
|
|
| 删除对象缓存 | 事务性删除对象 | 标记为删除的对象 | 是 | saveBufferObjToDatabase()自动清除 |
|
|||
|
|
|
|||
|
|
`addNewObjToSelObjList()` 和 `addNewObjToSaveObjList()` 方法体现了这种分离设计:前者将对象加入选择集进行后续查询或操作,后者将对象加入缓存列表准备持久化。一个对象可以同时存在于两个容器中,也可以仅存在于其中一个。
|
|||
|
|
|
|||
|
|
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L415-L430)
|
|||
|
|
|
|||
|
|
## 最佳实践与注意事项
|
|||
|
|
|
|||
|
|
### 批量操作的性能优化
|
|||
|
|
|
|||
|
|
对于大量对象的批量操作,建议遵循以下最佳实践:
|
|||
|
|
|
|||
|
|
1. **避免频繁调用saveBufferObjToDatabase()**:该方法会触发数据库事务,每次调用都有固定开销。应累积足够多的对象后再批量保存,建议每1000-5000个对象保存一次。
|
|||
|
|
|
|||
|
|
2. **合理使用数据集分组**:虽然`saveBufferObjToDatabase()`内部已实现数据集分组,但在创建对象时尽量按数据集顺序添加对象,可以减少分组操作的复杂度。
|
|||
|
|
|
|||
|
|
3. **控制扩展属性数量**:每个对象的扩展属性存储在MemoData中,过多扩展属性会增加内存占用和序列化开销。建议将高频查询的属性映射为基本属性,低频属性作为扩展属性。
|
|||
|
|
|
|||
|
|
### 内存管理注意事项
|
|||
|
|
|
|||
|
|
缓存对象在Python层面持有C++对象的引用,可能导致内存泄漏。以下场景需要特别关注:
|
|||
|
|
|
|||
|
|
- **对象创建后未加入保存列表**:如果调用了`createNewObjByCode()`但未调用`addNewObjToSaveObjList()`或`addNewObjToSelObjList()`,对象会一直保留在`curNewObj`中,占用内存直到下一次创建操作覆盖。
|
|||
|
|
|
|||
|
|
- **事务中断后的缓存清理**:如果在调用`saveBufferObjToDatabase()`之前发生异常,缓存列表中的对象不会被自动清理。建议在异常处理中手动调用缓存清除操作或重新创建SSProcess实例。
|
|||
|
|
|
|||
|
|
### 并发安全注意事项
|
|||
|
|
|
|||
|
|
缓存机制不是线程安全的。如果在多线程环境下使用,需要通过外部同步机制(如线程锁)确保对SSProcess实例的访问序列化。系统设计的典型使用场景是单线程的脚本执行环境。
|
|||
|
|
|
|||
|
|
### 与撤销系统的集成
|
|||
|
|
|
|||
|
|
缓存机制与[撤销标记管理](25-che-xiao-biao-ji-guan-li)功能存在交互。如果需要在保存到数据库前支持撤销操作,应将对象同时添加到选择集和保存列表中,这样可以在不触发数据库提交的情况下回滚修改。
|
|||
|
|
|
|||
|
|
## 相关主题
|
|||
|
|
|
|||
|
|
对象缓存机制是地理编辑功能链中的关键环节。掌握缓存机制后,建议继续学习以下相关主题:
|
|||
|
|
|
|||
|
|
- **[批量保存到数据库](24-pi-liang-bao-cun-dao-shu-ju-ku)**:深入了解`saveBufferObjToDatabase()`方法的实现细节和性能调优策略
|
|||
|
|
- **[创建默认地物对象](19-chuang-jian-mo-ren-di-wu-dui-xiang)**:学习通过不同方式创建地物对象的方法和编码映射关系
|
|||
|
|
- **[地物基本属性详解](17-di-wu-ji-ben-shu-xing-xiang-jie)**:了解基本属性的完整列表和语义说明
|
|||
|
|
- **[扩展属性与自定义属性](18-kuo-zhan-shu-xing-yu-zi-ding-yi-shu-xing)**:掌握扩展属性的存储机制和访问方法
|
|||
|
|
- **[对象删除操作](22-shan-chu-dui-xiang-cao-zuo)**:理解删除缓存的工作原理和事务性保证
|