Json树形结构数据转Java对象并存储到数据库的实现-超简单的JSON复杂数据处理 .

本文介绍了一种将具有层级结构的省市县JSON数据导入数据库的方法。通过定义实体类和工具类,采用递归方式处理JSON数据,实现了数据的批量存储。
    在网站开发中经常遇到级联数据的展示,比如选择城市的时候弹出的省市县选择界面。很多前端制作人员习惯于从JSON中而不是从数据库中获取省市县数据。那么在选择了省市县中的某一个城市 ,存储到数据库中需要存储所选城市的代码。所以需要一个能将JSON数据(一般存储在javascript脚本中)结构全部导入到数据库中的功能。

       JSON的特点是支持层级结构、支持数组表示的对象 。下面的示例介绍如何将JSON的省市县数据保存到数据库中,实现原理非常简单,就是利用JSON的java工具包API,将层次结构的JSON对象数组通过递归的方式首选转换为Java对象数组,然后保存到数据库中。

      实现步骤是:

    (一)首先定义一个JsonItem实体类:

package org.openjweb.core.entity;

public class JsonItem
{
 private String sub_id;
 private String sub_name;
 private JsonItem[] items;
 
 public JsonItem[] getItems() {
  return items;
 }
 public void setItems(JsonItem[] items) {
  this.items = items;
 }
 public String getSub_id() {
  return sub_id;
 }
 public void setSub_id(String sub_id) {
  this.sub_id = sub_id;
 }
 public String getSub_name() {
  return sub_name;
 }
 public void setSub_name(String sub_name) {
  this.sub_name = sub_name;
 }
 
}

(二)定义一个工具类,在工具类中读取Json数据文件,并进行递归调用 :

 

 public static String importJson(String fullFileName,String jsonType,String encoding,HttpServletRequest request) throws Exception
 {
  //Json转换为实体类参考:http://www.cnblogs.com/hoojo/archive/2011/04/21/2023805.html
  String sReturn = "";
  String jsonData = "";
  try
  {
   jsonData = FileUtil.getTextFileContent(fullFileName, encoding);
  }
  catch(Exception ex)
  {
   sReturn ="读Json文件失败!";
  }
  
  //获取rootId
  //logger.info("");
  jsonData = jsonData.replace("\"items\":\"\"", ""); //去掉空的 items
  String parentId = jsonData.substring(jsonData.indexOf("\"id\":")+5);
  parentId = parentId.substring(0,parentId.indexOf(",")).trim();
  parentId = parentId.replace("\"", "");
  logger.info("root id=="+parentId);
  String parentName = jsonData.substring(jsonData.indexOf("\"name\":")+7);
  parentName = parentName.substring(0,parentName.indexOf(",")).trim();
  parentName = parentName.replace("\"", "");
  logger.info("root Name=="+parentName);
  String rootData = jsonData.substring(jsonData.indexOf("\"items\":")+8,jsonData.lastIndexOf("}"));
  rootData = jsonData.substring(jsonData.indexOf("[")+1,jsonData.lastIndexOf("]"));
  //不同json的id,name表示不一样,统一换成sub_id,subname以便与JsonItem的类属性匹配
        //  替换后方便统一处理
  rootData = rootData.replace("city_id", "sub_id");
  rootData = rootData.replace("sub_city", "sub_name");
  rootData = rootData.replace("city", "sub_name");
  
  rootData = rootData.replace("sub_txt", "sub_name");
  
  rootData = rootData.replace("sub_industry", "sub_name");
  rootData = rootData.replace("industry_id", "sub_id");
  rootData = rootData.replace("industry", "sub_name");
  
  rootData = rootData.replace("sub_profession", "sub_name");
  rootData = rootData.replace("profession_id", "sub_id");
  rootData = rootData.replace("profession", "sub_name");
  
  
  
  //将rootData转换为array
  rootData =  "[" + rootData + "]";
  try
  {
   FileUtil.str2file(rootData, "d:/jsonData.txt", "utf-8");//存储到磁盘检查字符串转换是否正确
  }
  catch(Exception ex)
  {
   
  }
  JSONArray jsonArray = JSONArray.fromObject(rootData);
  Object[] os = jsonArray.toArray();
  JsonItem[] items = (JsonItem[]) JSONArray.toArray(jsonArray, JsonItem.class);
        
        saveJsonEnt(jsonType,parentId,parentName,"-1",new Long(1));
       
  dealJson(items,parentId,jsonType,new Long(1));
        return sReturn ;
 }
 
 private static void saveJsonEnt (String jsonType,String jsonId,String jsonName,String parentId,Long levelId) throws Exception
 {
  logger.info(jsonType+"/"+jsonId+"/"+jsonName+"/"+parentId+"/"+String.valueOf(levelId));
  CommJsonData ent = new CommJsonData();
  ent.setJsonType(jsonType);
  ent.setJsonCode(jsonId);
  ent.setJsonName(jsonName);
  ent.setRowId(StringUtil.getUUID());
  ent.setParentCode(parentId);
  ent.setLevelId(levelId);
  IDBSupportService service = (IDBSupportService)ServiceLocator.getBean("IDBSupportService3");
  service.saveOrUpdate(ent);
  
 }
 
  private static String dealJson(JsonItem[] jsonItem,String parentId,String jsonType,Long level)
 {
  String sReturn = "";
  if(jsonItem!=null&&jsonItem.length>0)
  {
   for(int i=0;i<jsonItem.length;i++)
   {
    JsonItem ent = jsonItem[i];
    //System.out.println(ent.getSub_id());
    //System.out.println(ent.getSub_name());
       try
       {
        saveJsonEnt(jsonType,ent.getSub_id(),ent.getSub_name(),parentId,level+1);
       }
       catch(Exception ex)
       {
        ex.printStackTrace();
       }
    if(ent.getItems()!=null)
    {
     //System.out.println("子项数:"+ent.getItems().length);
     dealJson(ent.getItems(),ent.getSub_id(),jsonType,level+1);
    }
    else
    {
     //System.out.println("没有子项!");
    }
   }
  }
  //此函数
  return sReturn ;
  
 
 } 

示例数据(部分):

 

{
  "name": "全国",
  "id": "0000000000",
  "description": "崇德易城市数据",
  "modified": "2012年08月",
  "copyright": "http://www.chongdeyi.com/",
  "items": [
  {
   "city": "北京市",
   "city_id": "1001000000",
   "items": [
   {
    "sub_city":"东城区",
    "sub_id":"2001001000"
   },
   {
    "sub_city":"西城区",
    "sub_id":"2001002000"
   },
   {
    "sub_city":"朝阳区",
    "sub_id":"2001006000"
   },
   {
    "sub_city":"丰台区",
    "sub_id":"2001007000"
   },
   {
    "sub_city":"石景山区",
    "sub_id":"2001008000"
   },
   {
    "sub_city":"海淀区",
    "sub_id":"2001009000"
   },
   {
    "sub_city":"门头沟区",
    "sub_id":"2001010000"
   },
   {
    "sub_city":"房山区",
    "sub_id":"2001011000"
   },
   {
    "sub_city":"通州区",
    "sub_id":"2001012000"
   },
   {
    "sub_city":"顺义区",
    "sub_id":"2001013000"
   },
   {
    "sub_city":"昌平区",
    "sub_id":"2001014000"
   },
   {
    "sub_city":"大兴区",
    "sub_id":"2001015000"
   },
   {
    "sub_city":"怀柔区",
    "sub_id":"2001016000"
   },
   {
    "sub_city":"平谷区",
    "sub_id":"2001017000"
   },
   {
    "sub_city":"延庆县",
    "sub_id":"2001018000"
   },
   {
    "sub_city":"密云县",
    "sub_id":"2001019000"
   }]
  },{
   "city": "天津市",
   "city_id": "1002000000",
   "items": [
   {
    "sub_city":"和平区",
    "sub_id":"2002001000"
   },

<think>嗯,用户的问题是想在Java实现树形结构菜单的JSON数据导入数据库,如果菜单存在则覆盖,不存在则新增。首先,我需要明确用户的具体需求,可能他们有一个树形结构的菜单数据,以JSON格式存在,需要将这些数据持久化到数据库中。且需要处理重复的情况,即如果菜单项已经存在,就更新,否则新增。 首先,我得考虑如何解析JSON数据。用户可能使用的是像Fastjson或者Jackson这样的库,所以需要先反序列化JSONJava对象。比如,定义一个Menu类,包含id、name、parentId、子节点列表等字段。这一步应该没问题,但需要注意JSON结构和Java类的字段是否匹配。接下来是数据库操作部分。用户提到存在则覆盖,不存在则新增,这类似于upsert操作。在JPA中,可以使用save方法,如果实体有id且存在,则会更新,否则插入。但需要确认id的生成策略,如果是数据库自增,可能需要先查询是否存在,再进行操作。或者用户可能使用其他方式生成ID,比如UUID,这时候可能需要根据某个唯一字段判断是否存在,比如根据name和parentId的组合,但更可能的是根据ID来判断,因为ID是主键。然后,树形结构的处理。通常,树形结构数据需要递归处理,因为每个节点可能有子节点。比如,先处理父节点,再递归处理子节点。这里需要注意的是,在保存父节点之后,子节点的parentId需要指向父节点的id,所以可能需要先保存父节点,再处理子节点。或者,如果使用JPA的级联操作,可能可以一次性保存整个树结构,但需要配置级联保存。另外,用户提供的引用中有递归组装树形结构的代码,可能用户之前已经实现了从数据库查询构建树形结构,现在需要反过来,将JSON导入数据库。所以需要处理的是如何将树形结构JSON数据换为实体对象逐层保存到数据库,同时处理存在性检查。可能的步骤是:1.解析JSON为Menu对象的列表或树形结构。2.遍历每个节点,先检查数据库中是否存在该ID的记录。 3.如果存在,则更新该记录;不存在则插入。 4.递归处理子节点,确保父节点的ID正确关联。 但需要考虑事务管理,保证整个导入过程的原子性,要么全部成功,要么回滚。此外,如果菜单的ID是由数据库自动生成的,那么在导入时可能需要先处理父节点生成ID,再处理子节点,这会更复杂。但根据用户的需求,可能ID是手动指定的,比如在JSON中已经包含ID,这样可以直接根据ID判断是否存在。另外,用户引用中提到了Hibernate,可能他们使用的是Hibernate或SpringData JPA。所以代码示例可能基于SpringData的CrudRepository或JpaRepository的save方法,该方法在存在主键时会执行更新,否则插入。 然后,如何处理树形结构的递归保存?例如,对于每个菜单节点,先保存自己,然后递归保存子节点。这需要确保父节点已经存在,以便子节点的parentId可以正确设置。但如果是先保存父节点,那么如果父节点是新增的,其ID可能由数据库生成,这时候子节点的parentId就需要在保存父节点后获取。不过,如果JSON中的ID是预先定义好的,比如不是自增的,而是固定的,比如1,2,3等,那么可以直接使用这些ID作为主键,这样在保存时可以直接根据ID判断存在性,无需考虑生成的问题。 因此,假设Menu实体类的ID是手动指定的,不是自增的,那么处理步骤可以是:1.解析JSON成Menu对象树形结构。2.遍历所有节点,对于每个节点:a.检查数据库中是否存在该ID的菜单。b.存在则更新,否则新增。c.递归处理子节点,确保它们的parentId正确指向父节点ID。但需要考虑如何遍历整个树结构。可以采用广度优先或深度优先的方式,但需要确保父节点在处理子节点之前已经被处理,否则子节点的parentId可能引用不存在的父节点ID。不过,如果JSON中的父节点已经在子节点之前被处理,那么没有问题。或者,在保存子节点时,父节点可能尚未保存,但此时如果父节点不存在,应该如何处理?比如,在数据库中可能已经存在父节点,或者在当前导入的JSON数据中父节点已经包含,但尚未保存。所以可能需要先将所有节点保存到一个临时列表,然后按层级处理,或者确保父节点先于子节点处理。 另一个方法是,将所有节点收集到一个列表中,然后按父ID排序,确保父节点先被处理。例如,根节点的parentId为0,先处理根节点,然后处理parentId为根节点的子节点,依此类推。这样在保存子节点时,父节点已经存在数据库中。 或者,在递归处理时,先处理当前节点,再处理子节点。例如,对于某个节点,先保存自己,然后递归保存子节点。这样,在保存子节点时,父节点已经存在,因为父节点已经保存过了。但这种方法需要树形结构的遍历顺序是父节点在前,子节点在后,这在递归处理时自然满足。因此,可能的代码结构如下: -解析JSON为List<Menu>,其中每个Menu对象包含子节点列表。-定义一个方法,递归处理每个节点及其子节点:-对于当前节点,检查是否存在,存在则更新,否则新增。-递归处理每个子节点,传入父节点的ID作为parentId。但需要注意,如果子节点在JSON中的parentId可能不正确,需要在处理时设置正确的parentId。例如,在解析JSON时,子节点的parentId应该已经正确指向父节点的ID,否则需要手动设置。这可能取决于JSON的结构是否正确。此外,数据库操作需要批量处理或逐条处理,考虑性能问题。如果数据量较大,可能需要批量操作,但示例代码可能先处理单条记录。用户提供的引用中有使用Stream API处理树形结构的例子,可能可以参考类似的递归方法,但改为保存到数据库。可能的代码示例:使用JPA的MenuRepository,假设有findById和save方法。处理每个节点时,先保存自己,然后处理子节点。但需要处理事务,可能需要在方法上添加@Transactional注解,保证所有操作在一个事务中。另外,关于覆盖已存在的菜单,可能需要从数据库中获取现有菜单,更新其属性,如名称、parentId等,然后保存。或者,如果使用save方法,当实体对象的主键存在时,会自动更新。 例如,使用Spring DataJPA的save方法,当menu对象的id存在时,会执行update,否则insert。因此,代码的大致步骤:1.解析JSON为Menu对象树形结构。2.递归遍历每个节点:a.检查数据库中是否存在该id的菜单,使用menuRepository.findById(id)。b.如果存在,则更新该菜单的属性(如name,parentId等),然后保存。c.如果不存在,则直接保存。d.递归处理子节点,处理每个子节点时,设置其parentId为当前节点的id。但需要注意,如果子节点的parentId在JSON中已经正确指向父节点的id,则无需修改。否则,可能需要手动设置。例如,在递归处理子节点时,将子节点的parentId设置为当前节点的id。例如,在递归方法中:privatevoid processMenu(Menu menu, LongparentId) {menu.setParentId(parentId);//检查是否存在保存//然后处理子节点for (Menu child: menu.getChildren()) {processMenu(child, menu.getId());}} 但需要确保在保存当前节点后,menu.getId()是正确的,尤其是在新增的情况下,如果id是数据库生成的,则需要在保存后获取生成的id。但根据用户的需求,可能id是在JSON中指定的,比如是固定的,所以保存时可以直接使用该id,无需生成。 假设Menu的id是手动指定的,那么在保存时可以直接使用该id,判断是否存在,然后覆盖或新增。 因此,代码示例可能如下: @Servicepublicclass MenuService{@AutowiredprivateMenuRepositorymenuRepository;@Transactionalpublic voidimportMenus(List<Menu> menus) {for(Menumenu :menus){processMenu(menu,null);//根节点的parentId为null或0,根据实际情况}}private voidprocessMenu(Menumenu, LongparentId) {menu.setParentId(parentId);//设置父ID//检查是否存在Optional<Menu> existingMenu= menuRepository.findById(menu.getId());if(existingMenu.isPresent()){//更新现有菜单Menuexisting =existingMenu.get();existing.setName(menu.getName());existing.setParentId(menu.getParentId());//其他需要更新的字段menuRepository.save(existing);}else {//新增菜单menuRepository.save(menu);}//递归处理子节点for (Menu child: menu.getChildren()) {processMenu(child, menu.getId());}}} 但需要注意,这里假设Menu实体有children字段,且使用JPA的@OneToMany等注解来映射父子关系。但实际中,数据库中可能不会直接存储children字段,而是通过parentId来关联。因此,在保存时,只需要处理每个节点的parentId,而不需要处理子节点列表,因为子节点会在递归中被处理。另外,解析JSON时,可能需要将子节点列表换为Menu对象的children字段,这需要正确的反序列化配置。例如,Menu类可能如下:@Entitypublic classMenu {@IdprivateLong id;private Stringname;privateLong parentId;@Transient//不作为数据库字段privateList<Menu>children;//gettersand setters} 这里使用@Transient注解,表示children字段不持久化到数据库,仅用于处理树形结构。实际存储时,每个子节点的parentId指向父节点的id。因此,在导入时,递归处理每个节点的子节点,设置其parentId为当前节点的id,保存。另外,用户可能需要处理JSON中的树形结构,例如,根节点列表,每个根节点有子节点,子节点再有子节点等。所以在解析时,可能需要将整个JSON结构换为包含根节点的列表,然后递归处理每个根节点及其子节点。例如,JSON结构可能如下: [{"id":1,"name": "根节点","children": [{"id":2,"name": "子节点1","children":[...]},...]},... ]解析这样的JSON为List<Menu>,然后调用importMenus方法处理。 总结步骤: 1.解析JSON成包含树形结构的Menu列表。 2.递归处理每个节点,设置parentId,根据ID判断存在性,进行保存或更新。3.使用事务保证操作的原子性。 可能遇到的问题:-如果ID是数据库自增的,无法在JSON中指定,这时需要其他方式判断存在性,比如根据名称和父ID组合的唯一性。但根据用户需求,他们可能使用的是固定ID,所以示例中假设ID由用户提供。 -子节点的处理顺序,必须确保父节点已经保存,否则设置parentId时会引用不存在的记录。但使用递归处理,先处理父节点再处理子节点,可以避免这个问题。-事务管理,确保整个导入过程在事务中,避免部分失败导致数据不一致。 参考用户提供的引用,其中有一个递归查询子节点的方法,可以类似地改为递归保存子节点。例如,用户之前的代码使用Stream API过滤子节点递归设置子列表,这里可以改为递归保存。另外,用户引用中提到Hibernate的兼容性,可能他们使用的是Hibernate,所以代码示例基于JPA和Hibernate。综上,代码示例应该包括解析JSON、递归处理每个节点、判断存在性、保存或更新,以及处理子节点。需要确保parentId正确设置,事务管理,以及正确处理树形结构。</think>### Java实现树形菜单JSON导入数据库(存在覆盖/不存在新增) #### 核心步骤分析 1. **数据结构定义**:定义与JSON结构匹配的Menu实体类,包含父子关系字段 2. **递归处理逻辑**:深度优先遍历树形结构,确保父节点先于子节点处理 3. **存在性判断**:根据主键ID判断记录是否存在,使用JPA的`save()`方法实现覆盖/新增 4. **事务管理**:使用`@Transactional`保证数据一致性 #### 实现代码示例 ```java // 实体类 @Entity @Table(name = "sys_menu") public class Menu { @Id private Long id; private String name; private Long parentId; @Transient // 非数据库字段,用于接收JSON子节点 private List<Menu> children; // getters/setters } // 服务层 @Service public class MenuImportService { @Autowired private MenuRepository menuRepository; @Transactional public void importMenuTree(String json) { List<Menu> rootMenus = JSON.parseArray(json, Menu.class); processMenuRecursive(rootMenus, null); } private void processMenuRecursive(List<Menu> menus, Long parentId) { for (Menu menu : menus) { menu.setParentId(parentId); upsertMenu(menu); if (menu.getChildren() != null && !menu.getChildren().isEmpty()) { processMenuRecursive(menu.getChildren(), menu.getId()); } } } private void upsertMenu(Menu menu) { menuRepository.findById(menu.getId()) .ifPresentOrElse( existing -> updateExistingMenu(existing, menu), () -> menuRepository.save(menu) ); } private void updateExistingMenu(Menu existing, Menu newData) { existing.setName(newData.getName()); existing.setParentId(newData.getParentId()); menuRepository.save(existing); } } ``` #### 关键实现说明 1. **JSON解析**:使用FastjsonJSON字符串换为树形对象结构 2. **递归处理**:采用深度优先遍历,确保父节点先被持久化 3. **UPSERT操作**:`menuRepository.save()`在ID存在时自动执行更新操作[^3] 4. **事务控制**:`@Transactional`注解保证整个导入操作的原子性 #### 数据库表建议结构 ```sql CREATE TABLE sys_menu ( id BIGINT PRIMARY KEY, name VARCHAR(255) NOT NULL, parent_id BIGINT, FOREIGN KEY (parent_id) REFERENCES sys_menu(id) ); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值