Java 案例:融合多源数据进行综合处理
下面通过一个完整可运行的案例,演示如何在 Java 中融合多源数据(数据库、JSON API、CSV 文件、日志等),并做统一清洗、关联、聚合与输出。

场景说明
假设要构建一个 "用户 360 视图",数据来自四个源:
| 数据源 | 格式 | |
|---|---|---|
| MySQL 用户表 | id、姓名、手机号 | 结构化 |
| 订单系统 API | 用户订单金额、次数 | JSON HTTP |
| CRM 文件 | 用户等级、标签 | CSV |
| 行为日志 | 最近活跃时间、PV | Log 文本 |
目标:按 userId 把四路数据对齐、去重、补全、聚合,输出统一的 UserProfile 对象。
核心设计思路
多源融合的四大关键步骤:
采集 (Extract) → 各源独立读取为中间对象
2. 标准化 (Normalize) → 统一字段名、类型、ID 主键
3. 关联 (Join) → 以主键 join / 摊平
4. 加工 (Enrich+Aggregate) → 补全缺失、规则引擎、聚合指标
工程上建议用 Pipeline + Map<userId, 融合对象> 的范式,避免 O(n²) 嵌套 join。
完整代码
统一模型
// 融合后的目标对象
public class UserProfile {
private String userId;
private String name;
private String phone;
private String level; // 来自 CRM
private List<String> tags; // 来自 CRM
private double totalAmount; // 来自订单
private int orderCount; // 来自订单
private long lastActiveTs; // 来自日志
private int pv; // 来自日志
// getters/setters 省略
}
各源的中间对象(标准化)
public record UserBasic(String userId, String name, String phone) {}
public record OrderStat(String userId, double amount, int count) {}
public record CrmInfo(String userId, String level, String tags) {}
public record Behavior(String userId, long lastActiveTs, int pv) {}
采集层(各源独立读取)
public class DataSourceLoader {
// ① 数据库
public List<UserBasic> loadFromDb(Connection conn) throws SQLException {
List<UserBasic> list = new ArrayList<>();
try (PreparedStatement ps = conn.prepareStatement(
"SELECT id, name, phone FROM users");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
list.add(new UserBasic(rs.getString("id"),
rs.getString("name"),
rs.getString("phone")));
}
}
return list;
}
// ② HTTP API(JSON)— 用 Jackson 举例
public List<OrderStat> loadFromOrderApi(String url) throws Exception {
ObjectMapper om = new ObjectMapper();
List<OrderStat> result = new ArrayList<>();
JsonNode root = om.readTree(new URL(url).openStream());
for (JsonNode n : root) {
result.add(new OrderStat(
n.get("userId").asText(),
n.get("amount").asDouble(),
n.get("count").asInt()));
}
return result;
}
// ③ CSV 文件
public List<CrmInfo> loadFromCsv(Path path) throws IOException {
List<CrmInfo> list = new ArrayList<>();
try (BufferedReader br = Files.newBufferedReader(path)) {
br.readLine(); // 跳过表头
String line;
while ((line = br.readLine()) != null) {
String[] p = line.split(",");
list.add(new CrmInfo(p[0], p[1],
Arrays.asList(p[2].split("\\|"))));
}
}
return list;
}
// ④ 日志文件:2025-01-01 12:00:00,u1001,/home, 之类
public List<Behavior> loadFromLog(Path path) throws IOException {
Map<String, Behavior> agg = new HashMap<>();
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(line -> {
String[] p = line.split(",");
String uid = p[1];
long ts = parseTs(p[0]);
Behavior b = agg.getOrDefault(uid, new Behavior(uid, 0, 0));
agg.put(uid, new Behavior(uid,
Math.max(b.lastActiveTs(), ts),
b.pv() + 1));
});
}
return new ArrayList<>(agg.values());
}
private long parseTs(String s) {
return LocalDateTime.parse(s,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
}
融合层(关键:以主键为索引,逐源"贴补")
public class FusionService {
private final DataSourceLoader loader;
public FusionService(DataSourceLoader loader) {
this.loader = loader;
}
public Map<String, UserProfile> fuse(
Connection conn, String apiUrl,
Path crmCsv, Path logFile) throws Exception {
// 1. 主数据(基线),使用 LinkedHashMap 保证顺序
Map<String, UserProfile> merged = new LinkedHashMap<>();
for (UserBasic u : loader.loadFromDb(conn)) {
UserProfile p = new UserProfile();
p.setUserId(u.userId());
p.setName(u.name());
p.setPhone(u.phone());
p.setTags(new ArrayList<>());
merged.put(u.userId(), p);
}
// 2. 订单数据:只 enrich 已存在用户,不新增(主数据为准)
for (OrderStat o : loader.loadFromOrderApi(apiUrl)) {
UserProfile p = merged.get(o.userId());
if (p != null) {
p.setTotalAmount(o.amount());
p.setOrderCount(o.count());
}
}
// 3. CRM 数据:可能比主数据多,可选择性新增
for (CrmInfo c : loader.loadFromCsv(crmCsv)) {
UserProfile p = merged.computeIfAbsent(c.userId(), id -> {
UserProfile np = new UserProfile();
np.setUserId(id);
np.setTags(new ArrayList<>());
return np;
});
p.setLevel(c.level());
p.getTags().addAll(c.tags());
}
// 4. 行为日志:补全 lastActive、PV
for (Behavior b : loader.loadFromLog(logFile)) {
UserProfile p = merged.get(b.userId());
if (p != null) {
p.setLastActiveTs(b.lastActiveTs());
p.setPv(b.pv());
}
}
// 5. 清洗 & 补全默认值
for (UserProfile p : merged.values()) {
if (p.getLevel() == null) p.setLevel("普通");
if (p.getPhone() != null && p.getPhone().length() > 7) {
p.setPhone(p.getPhone().substring(0, 3) + "****"
+ p.getPhone().substring(7)); // 脱敏
}
}
return merged;
}
}
主程序 & 输出
public class Main {
public static void main(String[] args) throws Exception {
DataSourceLoader loader = new DataSourceLoader();
FusionService svc = new FusionService(loader);
try (Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/app", "root", "pwd")) {
Map<String, UserProfile> profiles = svc.fuse(
conn,
"http://order-svc/api/orders/stat",
Path.of("crm.csv"),
Path.of("access.log"));
// 综合:按标签分组做聚合报表
Map<String, DoubleSummaryStatistics> byTag = profiles.values().stream()
.flatMap(p -> p.getTags().stream()
.map(t -> Map.entry(t, p)))
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.summarizingDouble(e -> e.getValue().getTotalAmount())));
byTag.forEach((tag, stat) ->
System.out.printf("标签[%s] 用户数=%d 总消费=%.2f%n",
tag, stat.getCount(), stat.getSum()));
}
}
}
多源融合的工程要点
| 关注点 | 做法 |
|---|---|
| 主键对齐 | 统一 userId,先确定"基线源",其他源按主键 enrich |
| 缺失处理 | computeIfAbsent + 默认值(如 level="普通") |
| 冲突处理 | 定义优先级(如 CRM 覆盖 DB 的标签),或用更新时间戳取胜 |
| 性能 | 用 Map O(1) join,避免嵌套循环;大数据量用流式 / 分片 |
| 类型标准化 | 统一时间戳格式、金额单位为 BigDecimal |
| 幂等 & 可重跑 | 每源读取独立、加工纯函数化,整体可重算 |
| 容错 | 某源异常不阻断,用 try/catch 降级为空列表 |
| 脱敏 | 融合后统一做 PII 掩码 |
进阶:用 Stream / CompletableFuture 并行采集
CompletableFuture<List<UserBasic>> f1 = CompletableFuture
.supplyAsync(() -> { try { return loader.loadFromDb(conn); }
catch (Exception e) { return List.of(); }});
CompletableFuture<List<OrderStat>> f2 = CompletableFuture
.supplyAsync(() -> { try { return loader.loadFromOrderApi(url); }
catch (Exception e) { return List.of(); }});
// ... 等 4 个源
CompletableFuture.allOf(f1, f2, f3, f4).join();
// 再进入统一的 fuse 逻辑
Java 中融合多源数据的稳定范式是:
各源独立采集 → 标准化到统一 POJO → 以主键为 Map 索引逐源 enrich → 清洗补全 → 聚合输出
关键不是用多复杂的框架,而是:
- 统一主键(一切以业务主键为索引)
- 主数据基线 + 其余源叠加(避免互相覆盖)
- 每源隔离(一个源挂了不影响整体)
- 纯函数化加工(可重跑、可测试)
如需处理超大规模(千万级以上),可扩展为 Spark/Flink 版本,思路完全等价,只是把 Map join 换成分布式 join。