> 技术文档 > Java 三剑客模拟数据库操作

Java 三剑客模拟数据库操作


文章目录

    • 前言
    • 数据结构设计
      • 1. 表结构模拟
      • 2. 关联关系模拟
    • 基础CRUD操作
      • 1. INSERT操作
      • 2. SELECT操作
      • 3. UPDATE操作
      • 4. DELETE操作
    • JOIN操作模拟
      • 1. INNER JOIN
      • 2. LEFT JOIN
      • 3. RIGHT JOIN
      • 4. FULL OUTER JOIN
    • 高级功能扩展
      • 1. 索引优化
      • 2. 事务支持
      • 3. 分页查询
    • 性能对比与限制
    • 完整代码示例
      • 数据库模拟器核心类
      • 运行结果
    • 总结

前言

当你迷茫的时候,请点击 总目录大纲 快速查看前面的技术文章,相信你总能找到前行的方向

另外,本文相关代码已上传 gitee仓库,欢迎关注收藏

以下将基于Java集合框架(List/Set/Map)模拟MySQL数据库的核心操作,涵盖SELECT、INSERT、UPDATE、DELETE以及各类JOIN操作。通过数据结构的组合实现关系型数据库的核心功能。

数据结构设计

假设有两个表,一个用户表,一个订单表,两者以 用户id 来关联
Java 三剑客模拟数据库操作

1. 表结构模拟

Map表示数据库表:

  • Key:主键(如用户ID)
  • Value:行数据(字段名到值的映射)

构建 map 工具方法

// 构建 map 工具方法private static Map<String, Object> mapOf(Object... args) { HashMap<String, Object> map = new HashMap<>(16); int len = args.length; for (int i = 0; i < len; i += 2) { if (i + 1 > len) { return map; } Object key = args[i]; Object val = args[i + 1]; if (key != null) { map.put(key.toString(), val); } } return map;}

模拟users表(主键为id)

// 模拟users表(主键为id)Map<String, Map<String, Object>> users = new HashMap<>();users.put(\"1\", mapOf(\"id\", \"1\", \"name\", \"Alice\", \"age\", 25));users.put(\"2\", mapOf(\"id\", \"2\", \"name\", \"Bob\", \"age\", 30));

2. 关联关系模拟

List存储关联表(如订单表),通过外键关联:

// 模拟orders表(外键user_id关联users.id)List<Map<String, Object>> orders = new ArrayList<>();orders.add(mapOf(\"order_id\", \"A1\", \"user_id\", \"1\", \"amount\", 100));orders.add(mapOf(\"order_id\", \"A2\", \"user_id\", \"2\", \"amount\", 200));

基础CRUD操作

1. INSERT操作

向集合插入新数据:

// 插入新用户Map<String, Object> newUser = mapOf(\"id\", \"3\", \"name\", \"Charlie\", \"age\", 28);users.put(newUser.get(\"id\").toString(), newUser);

2. SELECT操作

通过条件筛选数据:

// 查询年龄>25的用户(模拟WHERE)List<Map<String, Object>> result = users.values().stream() .filter(user -> (int) user.get(\"age\") > 25) .collect(Collectors.toList());

3. UPDATE操作

修改指定记录:

// 更新用户2的年龄users.computeIfPresent(\"2\", (k, v) -> { v.put(\"age\", 35); return v;});

4. DELETE操作

移除指定记录:

// 删除用户3users.remove(\"3\");

JOIN操作模拟

SQL JOIN操作用于把来自两个或多个表的行结合起来

Java 三剑客模拟数据库操作

1. INNER JOIN

获取两表交集数据:

List<Map<String, Object>> innerJoinResult = orders.stream() .filter(order -> users.containsKey(order.get(\"user_id\"))) .map(order -> { Map<String, Object> user = users.get(order.get(\"user_id\")); return mapOf( \"user_name\", user.get(\"name\"), \"order_id\", order.get(\"order_id\"), \"amount\", order.get(\"amount\") ); }).collect(Collectors.toList());

原理:通过外键过滤有效订单,再合并用户信息

2. LEFT JOIN

保留左表所有数据,右表无匹配时补null

List<Map<String, Object>> leftJoinResult = orders.stream() .map(order -> { Map<String, Object> user = users.getOrDefault(order.get(\"user_id\"), null); Map<String, Object> row = new HashMap<>(order); row.put(\"user_name\", user != null ? user.get(\"name\") : null); return row; }).collect(Collectors.toList());

原理:遍历左表(订单),强制关联右表(用户),无匹配则填充空值

3. RIGHT JOIN

保留右表所有数据(通过反转LEFT JOIN实现):

Set<Object> ordersSet = orders.stream().map(order -> order.get(\"user_id\")).collect(Collectors.toSet());List<Map<String, Object>> rightJoinResult = users.values().stream().flatMap(user -> { Object userId = user.get(\"id\"); // user 多出部分 if (!ordersSet.contains(userId)) { return Stream.of(mapOf(\"user_name\", user.get(\"name\"), \"order_id\", null, \"amount\", null)); } // 交集 return orders.stream().filter(order -> order.get(\"user_id\").equals(user.get(\"id\")))  .map(order -> mapOf(\"user_name\", user.get(\"name\"),  \"order_id\", order.get(\"order_id\"), \"amount\", order.get(\"amount\"))); }).collect(Collectors.toList());

原理:以用户表为基准,反向关联订单表

4. FULL OUTER JOIN

通过LEFT JOINRIGHT JOIN的并集模拟:

Set<Map<String, Object>> fullJoin = new HashSet<>(leftJoinResult);fullJoin.addAll(rightJoinResult);List<Map<String, Object>> fullOuterJoinResult = new ArrayList<>(fullJoin);

原理:利用集合的并集操作实现全外连接

高级功能扩展

1. 索引优化

Map<Object, List>构建字段索引:

// 为年龄字段创建索引Map<Integer, List<Map<String, Object>>> ageIndex = users.values().stream() .collect(Collectors.groupingBy(user -> (int) user.get(\"age\")));

2. 事务支持

通过操作日志实现原子性:

// 事务操作示例List<Runnable> transaction = new ArrayList<>();transaction.add(() -> users.put(\"4\", mapOf(\"id\", \"4\", \"name\", \"Dave\")));transaction.add(() -> orders.add(mapOf(\"order_id\", \"A3\", \"user_id\", \"4\")));transaction.forEach(Runnable::run); // 原子提交

3. 分页查询

结合Streamskiplimit

// 分页查询(每页2条,第1页)List<Map<String, Object>> page = users.values().stream() .skip(0).limit(2) .collect(Collectors.toList());

性能对比与限制

操作类型 Java集合实现 MySQL实际性能 SELECT O(n)线性扫描 O(log n)索引优化 JOIN O(n²)嵌套循环 O(n)哈希连接优化 INSERT O(1)直接插入 O(1) B+树插入 索引查询 O(1) Map直接访问 O(1) 聚簇索引

关键限制:

  1. 数据规模超过10万级时,嵌套循环JOIN性能急剧下降
  2. 无法实现真正的ACID事务
  3. 缺乏MySQL的查询优化器自动选择执行计划

完整代码示例

数据库模拟器核心类

import java.util.ArrayList;import java.util.HashMap;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set;import java.util.stream.Collectors;import java.util.stream.Stream;public class TestDbLsm { /** * 构建map * * @param args 参数 * @return Map */ private static Map<String, Object> mapOf(Object... args) { HashMap<String, Object> map = new HashMap<>(16); int len = args.length; for (int i = 0; i < len; i += 2) { if (i + 1 > len) { return map; } Object key = args[i]; Object val = args[i + 1]; if (key != null) { map.put(key.toString(), val); } } return map; } public static void main(String[] args) { // 模拟users表(主键为id) Map<String, Map<String, Object>> users = new HashMap<>(); users.put(\"1\", mapOf(\"id\", \"1\", \"name\", \"Alice\", \"age\", 25)); users.put(\"2\", mapOf(\"id\", \"2\", \"name\", \"Bob\", \"age\", 30)); System.out.println(\"users: \" + users); // 模拟orders表(外键user_id关联users.id) List<Map<String, Object>> orders = new ArrayList<>(); orders.add(mapOf(\"order_id\", \"A0\", \"user_id\", \"0\", \"amount\", 0)); orders.add(mapOf(\"order_id\", \"A1\", \"user_id\", \"1\", \"amount\", 100)); orders.add(mapOf(\"order_id\", \"A2\", \"user_id\", \"2\", \"amount\", 200)); System.out.println(\"orders: \" + orders); // 插入新用户 testAdd(users); // 查询年龄>25的用户(模拟WHERE) testQuery(users); // 更新用户2的年龄 testUpdate(users); // 删除用户3 testDelete(users); // INNER JOIN testInnerJoin(orders, users); // LEFT JOIN: (order left join user) List<Map<String, Object>> leftJoinResult = testLeftJoin(orders, users); // RIGHT JOIN: (order right join user = user left join order) List<Map<String, Object>> rightJoinResult = testRightJoin(orders, users); // FULL JOIN testFullJoin(leftJoinResult, rightJoinResult); // 为年龄字段创建索引 testIndex(users); // 事务操作示例 testTransaction(users, orders); // 分页查询(每页2条,第1页) testPage(users); } private static void testPage(Map<String, Map<String, Object>> users) { List<Map<String, Object>> page = users.values().stream() .skip(0).limit(2) .collect(Collectors.toList()); System.out.println(\"分页查询: \" + page); } private static void testTransaction(Map<String, Map<String, Object>> users, List<Map<String, Object>> orders) { List<Runnable> transaction = new ArrayList<>(); transaction.add(() -> users.put(\"4\", mapOf(\"id\", \"4\", \"name\", \"Dave\"))); transaction.add(() -> orders.add(mapOf(\"order_id\", \"A3\", \"user_id\", \"4\"))); // 原子提交 transaction.forEach(Runnable::run); System.out.println(\"事务操作: \" + transaction); } private static void testIndex(Map<String, Map<String, Object>> users) { Map<Integer, List<Map<String, Object>>> ageIndex = users.values().stream() .collect(Collectors.groupingBy(user -> (int) user.get(\"age\"))); System.out.println(\"为年龄字段创建索引: \" + ageIndex); } private static void testFullJoin(List<Map<String, Object>> leftJoinResult, List<Map<String, Object>> rightJoinResult) { Set<Map<String, Object>> fullJoin = new HashSet<>(leftJoinResult); fullJoin.addAll(rightJoinResult); List<Map<String, Object>> fullOuterJoinResult = new ArrayList<>(fullJoin); System.out.println(\"FULL JOIN: \" + fullOuterJoinResult); } private static List<Map<String, Object>> testRightJoin(List<Map<String, Object>> orders, Map<String, Map<String, Object>> users) { Set<Object> ordersSet = orders.stream().map(order -> order.get(\"user_id\")).collect(Collectors.toSet()); List<Map<String, Object>> rightJoinResult = users.values().stream().flatMap(user -> {  Object userId = user.get(\"id\");  // user 多出部分  if (!ordersSet.contains(userId)) { return Stream.of(mapOf(\"user_name\", user.get(\"name\"), \"order_id\", null, \"amount\", null));  }  // 交集  return orders.stream().filter(order -> order.get(\"user_id\").equals(user.get(\"id\"))) .map(order ->  mapOf(\"user_name\", user.get(\"name\"),\"order_id\", order.get(\"order_id\"), \"amount\", order.get(\"amount\"))); } ).collect(Collectors.toList()); System.out.println(\"[order] RIGHT JOIN [user]: \" + rightJoinResult); return rightJoinResult; } private static List<Map<String, Object>> testLeftJoin(List<Map<String, Object>> orders, Map<String, Map<String, Object>> users) { List<Map<String, Object>> leftJoinResult = orders.stream() .map(order -> {  Map<String, Object> user = users.getOrDefault(order.get(\"user_id\"), null);  Map<String, Object> row = new HashMap<>(order);  row.put(\"user_name\", user != null ? user.get(\"name\") : null);  return row; }).collect(Collectors.toList()); System.out.println(\"[order] LEFT JOIN [user]: \" + leftJoinResult); return leftJoinResult; } private static void testInnerJoin(List<Map<String, Object>> orders, Map<String, Map<String, Object>> users) { List<Map<String, Object>> innerJoinResult = orders.stream() .filter(order -> users.containsKey(order.get(\"user_id\"))) .map(order -> {  Map<String, Object> user = users.get(order.get(\"user_id\"));  return mapOf( \"user_name\", user.get(\"name\"), \"order_id\", order.get(\"order_id\"), \"amount\", order.get(\"amount\")  ); }).collect(Collectors.toList()); System.out.println(\"INNER JOIN: \" + innerJoinResult); } private static void testDelete(Map<String, Map<String, Object>> users) { users.remove(\"3\"); System.out.println(\"删除用户3: \" + users); } private static void testUpdate(Map<String, Map<String, Object>> users) { users.computeIfPresent(\"2\", (k, v) -> { v.put(\"age\", 35); return v; }); System.out.println(\"更新用户2的年龄: \" + users); } private static void testQuery(Map<String, Map<String, Object>> users) { List<Map<String, Object>> result = users.values().stream() .filter(user -> (int) user.get(\"age\") > 25) .collect(Collectors.toList()); System.out.println(\"查询年龄>25的用户: \" + result); } private static void testAdd(Map<String, Map<String, Object>> users) { // 插入新用户 Map<String, Object> newUser = mapOf(\"id\", \"3\", \"name\", \"Charlie\", \"age\", 28); users.put(newUser.get(\"id\").toString(), newUser); newUser = mapOf(\"id\", \"4\", \"name\", \"Doge\", \"age\", 30); users.put(newUser.get(\"id\").toString(), newUser); System.out.println(\"插入新用户: \" + users); }}

运行结果

users: {1={name=Alice, id=1, age=25}, 2={name=Bob, id=2, age=30}}orders: [{amount=0, user_id=0, order_id=A0}, {amount=100, user_id=1, order_id=A1}, {amount=200, user_id=2, order_id=A2}]插入新用户: {1={name=Alice, id=1, age=25}, 2={name=Bob, id=2, age=30}, 3={name=Charlie, id=3, age=28}, 4={name=Doge, id=4, age=30}}查询年龄>25的用户: [{name=Bob, id=2, age=30}, {name=Charlie, id=3, age=28}, {name=Doge, id=4, age=30}]更新用户2的年龄: {1={name=Alice, id=1, age=25}, 2={name=Bob, id=2, age=35}, 3={name=Charlie, id=3, age=28}, 4={name=Doge, id=4, age=30}}删除用户3: {1={name=Alice, id=1, age=25}, 2={name=Bob, id=2, age=35}, 4={name=Doge, id=4, age=30}}INNER JOIN: [{amount=100, user_name=Alice, order_id=A1}, {amount=200, user_name=Bob, order_id=A2}][order] LEFT JOIN [user]: [{amount=0, user_id=0, order_id=A0, user_name=null}, {amount=100, user_id=1, order_id=A1, user_name=Alice}, {amount=200, user_id=2, order_id=A2, user_name=Bob}][order] RIGHT JOIN [user]: [{amount=100, user_name=Alice, order_id=A1}, {amount=200, user_name=Bob, order_id=A2}, {amount=null, user_name=Doge, order_id=null}]FULL JOIN: [{amount=200, user_name=Bob, order_id=A2}, {amount=100, user_id=1, order_id=A1, user_name=Alice}, {amount=200, user_id=2, order_id=A2, user_name=Bob}, {amount=0, user_id=0, order_id=A0, user_name=null}, {amount=100, user_name=Alice, order_id=A1}, {amount=null, user_name=Doge, order_id=null}]为年龄字段创建索引: {35=[{name=Bob, id=2, age=35}], 25=[{name=Alice, id=1, age=25}], 30=[{name=Doge, id=4, age=30}]}事务操作: [tool.TestDbLsm$$Lambda$22/159413332@7cd84586, tool.TestDbLsm$$Lambda$23/1028214719@30dae81]分页查询: [{name=Alice, id=1, age=25}, {name=Bob, id=2, age=35}]Process finished with exit code 0

总结

通过结合Java集合与算法,可实现对关系型数据库核心功能的近似模拟,但需根据实际场景权衡性能与功能完整性。