今天前端问我能不能帮忙把菜单数据存进去,我看了下大概就是下面这样的数据结构,就是一个 JSON 数组,然后里面有 children
[{"name": "1001","path": "/1001","component": "1001.base","meta": {"title": "1001","i18nKey": "1001.base","order": 1},"children": [{"name": "100101","path": "/1001/01","component": "1001.01","meta": {"title": "100101","i18nKey": "1001.01"},"children": [{"name": "10010101","path": "/1001/01/01","component": "1001.01.01","meta": {"title": "10010101","i18nKey": "1001.01.01"}}]}]},{"name": "1002","path": "/1002","component": "1002.base","meta": {"title": "1002","i18nKey": "1002.base","order": 1},"children": [{"name": "100201","path": "/1002/01","component": "1002.01","meta": {"title": "100201","i18nKey": "1002.01"}}]}
]
然后写了方法用来解析他给我的文本,这里因为里面就一个JSON格式的字符串,我直接把后缀改成了 .json,通过 JsonNode 再递归获取数据
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;import java.io.File;
import java.sql.*;
import java.util.*;public class MenuParser {private static final String DB_URL = "jdbc:mysql://192.168.1.119:3306/ceshi?useUnicode=true&characterEncoding=utf-8&useSSL=false";private static final String DB_USER = "admin";private static final String DB_PASSWORD = "px123456";public static void main(String[] args) {try {// 读取 JSON 文件ObjectMapper objectMapper = new ObjectMapper();JsonNode rootNode = objectMapper.readTree(new File("C:\\Users\\72364\\Desktop\\fsdownload\\menu.json"));// 存储菜单项,使用 tempId 作为 keyMap<String, MenuItem> menuMap = new LinkedHashMap<>();parseMenu(rootNode, null, menuMap); // 递归解析// 插入数据到数据库,并更新 parentIdinsertMenusIntoDB(menuMap);System.out.println("✅ 数据插入成功,所有层级已正确关联!");} catch (Exception e) {e.printStackTrace();}}/*** **递归解析 JSON,分配临时 ID,并维护父子关系*** @param nodeList JSON 菜单数据* @param parentTempId 父级的临时 ID* @param menuMap 存储解析后的菜单项*/private static void parseMenu(JsonNode nodeList, String parentTempId, Map<String, MenuItem> menuMap) {for (JsonNode node : nodeList) {MenuItem menu = new MenuItem(node);String tempId = UUID.randomUUID().toString(); // 生成临时 IDmenu.setTempId(tempId);menu.setParentTempId(parentTempId); // 关联父级的 tempIdmenuMap.put(tempId, menu);// 递归解析子菜单JsonNode children = node.get("children");if (children != null && children.isArray()) {parseMenu(children, tempId, menuMap);}}}/*** **插入菜单数据,并更新 parentId*** @param menuMap 存储解析后的菜单项*/private static void insertMenusIntoDB(Map<String, MenuItem> menuMap) {String insertSQL = "INSERT INTO qy_platform_menu (parent_id, status, menu_type, menu_name, route_name, " +"route_path, component, `order`, i18nKey, icon, hide_in_menu, keep_alive, create_time, update_time, del_flag) " +"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW(), ?)";String updateSQL = "UPDATE qy_platform_menu SET parent_id = ? WHERE id = ?";Map<String, Long> tempIdToDbId = new HashMap<>(); // 记录 tempId → 数据库 IDtry (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) {conn.setAutoCommit(false); // 开启事务// **第一遍插入数据库**try (PreparedStatement pstmt = conn.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS)) {for (MenuItem menu : menuMap.values()) {pstmt.setObject(1, null); // 先插入,parentId 设为 nullpstmt.setInt(2, 1);pstmt.setString(3, menu.getMenuType());pstmt.setString(4, menu.getMenuName());pstmt.setString(5, menu.getRouteName());pstmt.setString(6, menu.getRoutePath());pstmt.setString(7, menu.getComponent());pstmt.setInt(8, menu.getOrder() != null ? menu.getOrder() : 0);pstmt.setString(9, menu.getI18nKey());pstmt.setString(10, menu.getIcon());pstmt.setInt(11, 0);pstmt.setInt(12, 0);pstmt.setInt(13, 0);pstmt.executeUpdate();// 获取新插入的 IDtry (ResultSet rs = pstmt.getGeneratedKeys()) {if (rs.next()) {long newId = rs.getLong(1);tempIdToDbId.put(menu.getTempId(), newId);menu.setId(newId);}}}}// **第二遍:更新 parentId**try (PreparedStatement updateStmt = conn.prepareStatement(updateSQL)) {for (MenuItem menu : menuMap.values()) {if (menu.getParentTempId() != null && tempIdToDbId.containsKey(menu.getParentTempId())) {long parentId = tempIdToDbId.get(menu.getParentTempId());long menuId = menu.getId();updateStmt.setLong(1, parentId);updateStmt.setLong(2, menuId);updateStmt.executeUpdate();}}}conn.commit(); // 提交事务System.out.println("✅ 数据插入完成,parentId 关联成功!");} catch (SQLException e) {e.printStackTrace();}}/*** **执行插入 SQL 并获取生成的 ID***/private static long insertMenuAndGetId(PreparedStatement pstmt, MenuItem menu) throws SQLException {pstmt.setObject(1, menu.getParentId());pstmt.setInt(2, 1);pstmt.setString(3, menu.getMenuType());pstmt.setString(4, menu.getMenuName());pstmt.setString(5, menu.getRouteName());pstmt.setString(6, menu.getRoutePath());pstmt.setString(7, menu.getComponent());pstmt.setInt(8, menu.getOrder() != null ? menu.getOrder() : 0);pstmt.setString(9, menu.getI18nKey());pstmt.setString(10, menu.getIcon());pstmt.setInt(11, 0);pstmt.setInt(12, 0);pstmt.setInt(13, 0);pstmt.executeUpdate();try (ResultSet rs = pstmt.getGeneratedKeys()) {if (rs.next()) {return rs.getLong(1);}}return -1;}
}
这里加了个临时 ID 用来处理上下级的父子关联
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;@Data
public class MenuItem {private Long id; // 数据库 IDprivate Long parentId; // 真实 parent_idprivate String tempId; // 临时 IDprivate String parentTempId; // 临时 parent_idprivate Integer status;private String menuType;private String menuName;private String routeName;private String routePath;private String component;private Integer order;private String i18nKey;private String icon;public MenuItem(JsonNode node) {if (node.get("name") != null) this.routeName = node.get("name").asText();if (node.get("path") != null) this.routePath = node.get("path").asText();if (node.get("component") != null) this.component = node.get("component").asText();if (node.get("meta") != null) {if (node.get("meta").get("order") != null) this.order = node.get("meta").get("order").asInt();if (node.get("meta").get("title") != null) this.menuName = node.get("meta").get("title").asText();if (node.get("meta").get("i18nKey") != null) this.i18nKey = node.get("meta").get("i18nKey").asText();if (node.get("meta").get("icon") != null) this.icon = node.get("meta").get("icon").asText();}}
}
可以看到数据正常存进去了