Java解析多层嵌套JSON数组并将数据存入数据库示例

今天前端问我能不能帮忙把菜单数据存进去,我看了下大概就是下面这样的数据结构,就是一个 JSON 数组,然后里面有 children

bash 复制代码
[
	{
		"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 再递归获取数据

java 复制代码
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 作为 key
            Map<String, MenuItem> menuMap = new LinkedHashMap<>();
            parseMenu(rootNode, null, menuMap); // 递归解析

            // 插入数据到数据库,并更新 parentId
            insertMenusIntoDB(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(); // 生成临时 ID
            menu.setTempId(tempId);
            menu.setParentTempId(parentTempId); // 关联父级的 tempId
            menuMap.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 → 数据库 ID

        try (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 设为 null
                    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();

                    // 获取新插入的 ID
                    try (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 用来处理上下级的父子关联

java 复制代码
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;

@Data
public class MenuItem {
    private Long id;             // 数据库 ID
    private Long parentId;       // 真实 parent_id
    private String tempId;       // 临时 ID
    private String parentTempId; // 临时 parent_id

    private 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();
        }
    }
}

可以看到数据正常存进去了

相关推荐
小杨xyyyyyyy6 分钟前
Mysql - 日志相关问题
数据库·mysql·面试
小安同学iter9 分钟前
SpringMVC(八)Knife4j 接口文档
java·开发语言·spring boot·spring·java-ee·intellij-idea
飞奔的马里奥11 分钟前
力扣Hot100——169. 多数元素
java·算法·leetcode
日暮南城故里19 分钟前
Java学习------static、final、this、super关键字
java·学习
等风来不如迎风去29 分钟前
【python】http post 在body中传递json数据 以发送
python·http·json
交响梦1 小时前
医院信息系统平台总体架构原则
大数据·数据库·人工智能·架构·健康医疗
程序员沉梦听雨1 小时前
【实战篇】exists语法解析
数据库·mysql
码上飞扬1 小时前
SQL Server运维实战:十大高频问题分析与解决方案
运维·数据库·oracle
小画家~2 小时前
第二:go 链接mysql 数据库
数据库·mysql
TayTay的学习笔记2 小时前
LinkedList底层结构和源码分析(JDK1.8)
java·笔记·学习