【问题标题】:How to make nested JSON response from database tables如何从数据库表中进行嵌套的 JSON 响应
【发布时间】:2021-05-19 07:46:59
【问题描述】:

我有 2 张桌子。

DATA

+------+--------+---------------------+------------+
| ID   | Name   | Desc                | Type       |
+------+--------+---------------------+------------+
| 1001 | Motor  | Main motor 50 volt  | Mechanical |
| 1002 | Nut    | 25 mm dia           | Mechanical |
| 1003 | Bolt   | Hexa bolt           | Mechanical |
| 1004 | Engine | 750cc liquid cooled | Mechanical |
| 1005 | Oil    | 1 liter             | NA         |
| 1006 | Filter | Airfilter           | NA         |
| 1007 | AC     | 1000w               | Electrical |
+------+--------+---------------------+------------+

Relationship

+-----------+-----------+----------+
| Parent_id | Relation  | Child_id |
+-----------+-----------+----------+
| 1001      | Accessory | 1002     |
| 1001      | Accessory | 1003     |
| 1001      | Service   | 1005     |
| 1001      | Service   | 1006     |
| 1004      | Accessory | 1002     |
| 1004      | Accessory | 1003     |
| 1004      | Service   | 1005     |
+-----------+-----------+----------+

当用户搜索1001时,我想提供以下响应

{
    "id": "1001",
    "name": "Motor",
    "desc": "Main motor 50 volt",
    "Accessory": [{
            "id": "1002",
            "name": "Nut"
        },
        {
            "id": "1003",
            "name": "Bolt"
        }
    ],
    "Service": [{
            "id": "1005",
            "name": "Oil"
        },
        {
            "id": "1006",
            "name": "Filter"
        }
    ]
}

这里的AccessoryService 来自relationship 表列relation。我对弹簧靴一无所知。在谷歌的帮助下,我完成了以下代码。这给了我一个级别的 json。但我无法找到如何创建这个嵌套的 JSON。请提供一些建议。

存储库:

@Repository
public interface MyDataRepo extends JpaRepository<Items, String> {

    @Query(value="SELECT D.id,D.name,D.desc,R.relation,R.child_id as childid,DC.name as childname
                  FROM DATA D 
                  JOIN RELATIONSHIP R ON D.ID=R.PARENT_ID 
                  JOIN DATA DC ON DC.id=R.CHILD_ID
                  WHERE D.ID=?1",nativeQuery=true)
    List<Data> findAllCategory(String id);

    public static interface Data {
      String getid();
      String getname();
      String getdesc();
      String getrelation();
      String getchildid();
      String getchildname();
    }
}

服务:

public List<Data> getMyData(String id) {
    return repo.findAllCategory(id);
}

控制器:

@GetMapping("/getData/{id}")
public ResponseEntity<List<Data>> retrieveData(@PathVariable String id) {
    List<Data> stud = service.getMyData(id);
    return ResponseEntity.ok().body(stud);
}

【问题讨论】:

    标签: java json spring-boot spring-data-jpa nativequery


    【解决方案1】:

    由于Relation字段是动态的,我们无法使用POJO来生成相关对象,所以我选择了一个Map对象来生成最终结果。

    @GetMapping("/getData/{id}")
    public ResponseEntity<Map<String, Object>> retrieveData(@PathVariable("id") Long id) {
        List<MyDataRepo.Data> stud = myDataRepo.findAllCategory(id);
    
        Map<String, Object> parent = new HashMap<>();
        parent.put("id", stud.get(0).getid());
        parent.put("name", stud.get(0).getname());
        parent.put("desc", stud.get(0).getdesc());
    
        stud.forEach(d -> {
            String r = d.getrelation();
    
            Map<String, String> child = new HashMap<>();
            child.put("id", d.getchildid());
            child.put("name", d.getchildname());
    
            if (parent.containsKey(r)) {
                List<Map<String, String>> children = (List<Map<String, String>>) parent.get(r);
                children.add(child);
            } else {
                List<Map<String, String>> children = new ArrayList<>();
                children.add(child);
                parent.put(r, children);
            }
        });
    
        return ResponseEntity.ok().body(parent);
    }
    

    最后,我们得到了结果

    {
      "id": "1001",
      "name": "Motor",
      "desc": "Main motor 50 volt",
      "Accessory": [
        {
          "id": "1002",
          "name": "Nut"
        },
        {
          "id": "1003",
          "name": "Bolt"
        }
      ],
      "Service": [
        {
          "id": "1005",
          "name": "Oil"
        },
        {
          "id": "1006",
          "name": "Filter"
        }
      ]
    }
    

    【讨论】:

    • 谢谢。它几乎可以工作。但我的桌子有其他关系,它正在获得所有关系。我如何限制为只有AccessoryService
    • 还有如何检查NULL 值。假设如果用户提供了一些数据库中不可用的id,我应该提供一些自定义消息。目前它正在给Indexoutofthebounds异常
    • 问题一:可以写sql来过滤关系,例如select d.id, d.name, d.desc, r.relation, r.child_id as childid, dc.name as childname from DATA d join Relationship r on d.id=r.parent_id join DATA dc on dc.id=r.child_id where d.id=1001 and r.relation='Accessory' or r.relation='Service';
    • 问题2:我觉得可以查看数组项,见:stackoverflow.com/questions/12897615/…
    • 嗨@Spirit,再次感谢。在某些情况下,关系表中不会有 AccessoryService 的任何值。在这种情况下,它应该给我空列表。但是在这里它会抛出错误,例如地图不能为空。如何解决?
    【解决方案2】:

    为了构建嵌套投影,您应该在实体之间建立关系,例如:

    @Entity
    @Table(name = "Data")
    public class Data {
    
        @Id
        private long id;
    
        @Column(name = "name")
        private String name;
    
        @Column(name = "desc")
        private String desc;
    
        @OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        private Set<Relationship> relationship;
    
        ...getters/setters...
    }
    
    @Entity
    @Table(name = "Relationship")
    @IdClass(RelationshipId.class)
    public class Relationship {
    
        @Id
        @Column(name = "parent_id")
        private long parentId;
    
        @Id
        @Column(name = "relation")
        private String name;
    
        @Id
        @Column(name = "child_id")
        private long childId;
    
        @OneToOne
        @JoinColumn(name = "child_id", unique = true, nullable = false, updatable = false, insertable = false)
        private Data child;
    
        ...getters/setters...
    }
    

    这是复合键的 id 类:

    public class RelationshipId implements Serializable {
    
        private long parentId;
    
        private String name;
    
        private long childId;
    
        // default constructor
    
        public RelationshipId(long parentId, String name, long childId) {
            this.parentId = parentId;
            this.name = name;
            this.childId = childId;
        }
    
        // equals() and hashCode()
    }
    

    之前,我检查了两个选项:@IdClass 和@EmbeddedId。 我认为为您使用 @IdClass 注释的更合适的方法。 详细说明:https://www.baeldung.com/jpa-composite-primary-keys

    以下预测:

    public interface DataView {
    
        String getId();
    
        String getName();
    
        String getDesc();
    
        List<RelationshipView> getRelationship();
    
    }
    
    public interface RelationshipView {
    
        String getId();
    
        String getName();
    
        String getChildId();
    
        DataItemView getChild();
    
    }
    
    public interface DataItemView {
    
        String getId();
    
        String getName();
    
        String getDesc();
    
    }
    

    在 repo 中,您可以通过单个查询接收所有需要的数据,并将数据重组到适当的 DTO 视图中:

    public interface DataRepo extends JpaRepository<Data, String> {
    
        @Query(value = "SELECT d from Data d " +
                "left join fetch d.relationship as r " +
                "left join fetch r.child as c " +
                "where d.id=:id")
        DataView getDataViewById(long id);
    
    }
    
    @Service
    public class DataService {
    
        @Autowired
        private DataRepo dataRepo;
    
        public DataDto getMyData(long id) {
            DataView data = dataRepo.getDataViewById(id);
            DataDto dataDto = new DataDto();
            dataDto.setId(data.getId());
            dataDto.setName(data.getName());
            dataDto.setDesc(data.getDesc());
    
            for (RelationshipView rs : data.getRelationship()) {
                DataItemView dataItemView = rs.getChild();
                switch (rs.getName()) {
                    case "Accessory":
                        dataDto.getAccessory().add(new DataItemDto(dataItemView.getId(), dataItemView.getName()));
                        break;
                    case "Service":
                        dataDto.getService().add(new DataItemDto(dataItemView.getId(), dataItemView.getName()));
                        break;
                }
    
            }
    
            return dataDto;
        }
    
    }
    

    DTO 的类:

    public class DataDto {
        String id;
        String name;
        String desc;
        List<DataItemDto> accessory = new LinkedList<>();
        List<DataItemDto> service = new LinkedList<>();
        ...getters/setters...
    }
    
    public class DataItemDto {
        String id;
        String name;
    
        public DataItemDto(String id, String name) {
            this.id = id;
            this.name = name;
        }
        ...getters/setters...
    }
    

    【讨论】:

    • 非常感谢。我在Relationship 课程中有一个问题。我不能为单列声明@id。 `Parent_id | 的组合关系 | Child_id ` 是唯一的。所以它在那里失败了。你能帮忙解决吗?
    • @Avinash 我为Relationship 类添加了复合键配置,请再次检查并告诉我是否一切正常。
    猜你喜欢
    • 2021-09-06
    • 2019-07-25
    • 2022-01-09
    • 2019-06-22
    • 1970-01-01
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多