烹饪披萨机
110.73M · 2026-03-10
定义: 与数据库表结构一一对应的 Java 对象
特点:
用途:
示例:
// 数据库表: battery
// 列: project_id, ts, value, device, iter
@Data
public class Battery { // PO 类
private String projectId; // 对应 project_id
private Long ts; // 对应 ts
private BigDecimal value; // 对应 value
private String device; // 对应 device
private Integer iter; // 对应 iter
}
定义: 用于前后端数据传输的 Java 对象
特点:
用途:
示例:
// API 返回给前端的数据结构
@Data
public class ItersPeakRssVO { // VO 类
private Integer iter; // 迭代次数
private String device; // 设备名称
private Long rss; // RSS 内存值(KB)
}
数据库 → PO → Service 转换 → VO → Controller → 前端
↑ ↓
└──────────────── 写入数据 ←──────────────────┘
// 数据库表可能有很多字段
@Data
public class MemoryDetailPO {
private String projectId;
private Long timestamp;
private String deviceId;
private Integer iterationNumber;
private Long rssValue;
private String internalFlag; // 内部使用
private Timestamp createdAt; // 创建时间
private Timestamp updatedAt; // 更新时间
}
// 但前端只需要部分字段
@Data
public class MemoryDetailVO {
private Integer iter; // 重命名更友好
private String device; // 简化字段名
private Long rss; // 只返回需要的数据
}
// 可能需要从多个 PO 聚合数据
public MemorySummaryVO buildSummary(List<MemoryPO> memories, List<DevicePO> devices) {
MemorySummaryVO vo = new MemorySummaryVO();
vo.setTotalRss(memories.stream().mapToLong(MemoryPO::getRss).sum());
vo.setDeviceCount(devices.size());
vo.setAvgRss(vo.getTotalRss() / vo.getDeviceCount());
return vo;
}
// PO 可能包含敏感信息
@Data
public class UserPO {
private String username;
private String password; // 敏感信息
private String internalId; // 内部ID
}
// VO 只返回安全的信息
@Data
public class UserVO {
private String username;
// 不包含密码和内部ID
}
数据库表: memory_peak_rss
CREATE TABLE memory_peak_rss (
project_id VARCHAR(255),
iter INT,
device VARCHAR(255),
rss BIGINT
);
PO 类:
@Data
public class MemoryPeakRssPO {
private String projectId;
private Integer iter;
private String device;
private Long rss;
}
VO 类:
@Data
public class ItersPeakRssVO {
private Integer iter;
private String device;
private Long rss;
// 注意:不包含 projectId,因为前端不需要
}
转换过程:
@Service
public class MemoryService {
public List<ItersPeakRssVO> getItersPeakRss(String projectId) {
// 1. 从数据库查询 PO
List<MemoryPeakRssPO> poList = memoryDorisService.queryItersPeakRss(projectId);
// 2. 转换 PO → VO
return poList.stream()
.map(po -> {
ItersPeakRssVO vo = new ItersPeakRssVO();
vo.setIter(po.getIter());
vo.setDevice(po.getDevice());
vo.setRss(po.getRss());
// 不设置 projectId
return vo;
})
.collect(Collectors.toList());
}
}
VO 类(已存在):
@Data
public class RoundRssVO {
private Integer iter;
private String device;
private Integer round;
private Long rss;
// 动态分类字段 - 这是业务需求,不是数据库字段
private Map<String, Long> categories;
private Map<String, Integer> categoryCounts;
}
可能的数据库表:
-- 方案 1: 扁平化存储
CREATE TABLE memory_round_rss (
project_id VARCHAR(255),
iter INT,
device VARCHAR(255),
round INT,
rss BIGINT,
category VARCHAR(255),
category_rss BIGINT,
category_count INT
);
-- 一个 round 可能有多行数据,每行代表一个分类
PO 类:
@Data
public class MemoryRoundRssPO {
private String projectId;
private Integer iter;
private String device;
private Integer round;
private Long rss;
private String category;
private Long categoryRss;
private Integer categoryCount;
}
转换逻辑:
public List<RoundRssVO> getRoundRss(String projectId) {
// 1. 查询所有数据(多行)
List<MemoryRoundRssPO> poList = memoryDorisService.queryRoundRss(projectId);
// 2. 按 (iter, device, round) 分组
Map<String, List<MemoryRoundRssPO>> grouped = poList.stream()
.collect(Collectors.groupingBy(po ->
po.getIter() + "_" + po.getDevice() + "_" + po.getRound()
));
// 3. 转换为 VO
return grouped.values().stream()
.map(group -> {
RoundRssVO vo = new RoundRssVO();
MemoryRoundRssPO first = group.get(0);
vo.setIter(first.getIter());
vo.setDevice(first.getDevice());
vo.setRound(first.getRound());
vo.setRss(first.getRss());
// 聚合分类数据到 Map
Map<String, Long> categories = new HashMap<>();
Map<String, Integer> counts = new HashMap<>();
for (MemoryRoundRssPO po : group) {
categories.put(po.getCategory(), po.getCategoryRss());
counts.put(po.getCategory(), po.getCategoryCount());
}
vo.setCategories(categories);
vo.setCategoryCounts(counts);
return vo;
})
.collect(Collectors.toList());
}
| 特性 | PO (Persistent Object) | VO (Value Object) |
|---|---|---|
| 用途 | 数据库映射 | 数据传输 |
| 位置 | DAO/Repository 层 | Controller/Service 层 |
| 设计依据 | 数据库表结构 | 业务需求 |
| 字段来源 | 数据库列 | 业务逻辑 |
| 是否包含敏感信息 | 可能包含 | 不包含 |
| 是否可以聚合 | 否 | 是 |
| 是否可以计算 | 否 | 是 |
| 命名风格 | 数据库风格 | 业务风格 |
PO 类:
// 对应数据库表 battery
@Data
public class Battery {
private String projectId; // 数据库字段
private Long ts; // 数据库字段
private BigDecimal value; // 数据库字段
private String device; // 数据库字段
}
VO 类:
// 返回给前端的数据
@Data
public class CombinationVO {
private List<List<String>> startup; // 聚合后的启动数据
private List<List<String>> userInput; // 聚合后的输入数据
private List<List<String>> battery; // 聚合后的电池数据
private List<List<String>> instrsPerSec; // 聚合后的指令数据
}
// PO 类命名
Battery.java // 直接用表名
MemoryPeakRss.java // 或加 PO 后缀
MemoryPeakRssPO.java
// VO 类命名
BatteryVO.java // 加 VO 后缀
ItersPeakRssVO.java
model/
├── po/ # PO 类
│ ├── powerConsumption/
│ │ └── Battery.java
│ └── memory/
│ └── MemoryPeakRss.java
└── vo/ # VO 类
├── powerconsumption/
│ └── CombinationVO.java
└── coldstartMemory/
└── ItersPeakRssVO.java
public class MemoryConverter {
public static ItersPeakRssVO toVO(MemoryPeakRssPO po) {
ItersPeakRssVO vo = new ItersPeakRssVO();
vo.setIter(po.getIter());
vo.setDevice(po.getDevice());
vo.setRss(po.getRss());
return vo;
}
public static List<ItersPeakRssVO> toVOList(List<MemoryPeakRssPO> poList) {
return poList.stream()
.map(MemoryConverter::toVO)
.collect(Collectors.toList());
}
}
总结:
这样设计的好处是:数据库结构变化时,只需要修改 PO 和转换逻辑,不影响 API 接口;业务需求变化时,只需要修改 VO,不影响数据库结构。
微博网页版登录入口-微博官网地址直达
一天一个开源项目(第45篇):OpenAI Agents SDK Python - 轻量级多 Agent 工作流框架,支持 100+ LLM 与实时语音
2026-03-10
2026-03-10