如何在Spring Boot中使用Hibernate / JPA返回多级json

我有一个Postgres数据库,有4个表父母,子女,团体和Group_Membership。

群组可以有多个父母,父母可以有多个群组。 父母可以有多个孩子,但孩子只能有一个孩子。

这是架构的愚蠢版本。 在此处输入图像描述

我正在使用Spring Boot和Hibernate JPA。

Parent.java

@Entity @Table(name = "parents") public class Parent { @Id @GeneratedValue @Column(name="parent_id") private Long parentId; @Column(name= "first_name") private String firstName; @Column(name= "last_name") private String lastName; @OneToMany(mappedBy="parent") private Set children; @ManyToMany(cascade = { CascadeType.ALL }) @JoinTable( name= "Group_Membership", joinColumns = { @JoinColumn(name = "parent_id") }, inverseJoinColumns = { @JoinColumn(name = "group_id") } ) private Set groups = new HashSet(); //Constructor //Getters and Setters } 

Child.java

 @Entity @Table(name = "children") public class Child { @Id @GeneratedValue @Column(name= "child_id") private Long childId; @Column(name= "first_name") private String firstName; @Column(name= "last_name") private String lastName; @ManyToOne @JoinColumn(name="parent_id", nullable=false) private Parent parent; //Constructor //Getters and Setters } 

Group.java

 @Entity @Table(name = "groups") public class Group { @Id @GeneratedValue @Column(name= "group_id") private Long groupId; private String name; @ManyToMany(mappedBy = "groups") private Set parents = new HashSet(); //Constructor //Getters and Setters } 

我有所有这些设置的存储库:

 public interface GroupRepository extends PagingAndSortingRepository { @RestResource(rel = "name-contains", path = "containsName") Page findByNameContains(@Param("name") String name, Pageable page); } 

集团成员表

 CREATE TABLE GROUP_MEMBERSHIP ( PARENT_ID INT NOT NULL, GROUP_ID INT NOT NULL, PRIMARY KEY (PARENT_ID, GROUP_ID), CONSTRAINT GROUP_MEMBERSHIP_IBFK_1 FOREIGN KEY (PARENT_ID) REFERENCES PARENTS (PARENT_ID), CONSTRAINT GROUP_MEMBERSHIP_IBFK_2 FOREIGN KEY (GROUP_ID) REFERENCES GROUPS (GROUP_ID) ); 

当我去http:// localhost:8080 / groups

我收到了这个回复:

 { "_embedded": { "groups": [ { "name": "Hyde Park", "_links": { "self": { "href": "http://localhost:8080/groups/1" }, "group": { "href": "http://localhost:8080/groups/1" }, "parents": { "href": "http://localhost:8080/groups/1/parents" } } } ] }, "_links": { "self": { "href": "http://localhost:8080/groups" }, "profile": { "href": "http://localhost:8080/profile/groups" }, "search": { "href": "http://localhost:8080/groups/search" } }, "page": { "size": 20, "totalElements": 1, "totalPages": 1, "number": 0 } } 

然后当我想看看小组中的父母时,我会去http:// localhost:8080 / groups / 1 / parents

响应

 { "_embedded": { "parents": [ { "firstName": "Cherice", "lastName": "Giannoni", "_links": { "self": { "href": "http://localhost:8080/parents/1" }, "parent": { "href": "http://localhost:8080/parents/1" }, "groups": { "href": "http://localhost:8080/parents/1/groups" }, "children": { "href": "http://localhost:8080/parents/1/children" } } }, { "firstName": "Aylmer", "lastName": "Feckey" "_links": { "self": { "href": "http://localhost:8080/parents/2" }, "parent": { "href": "http://localhost:8080/parents/2" }, "groups": { "href": "http://localhost:8080/parents/2/groups" }, "children": { "href": "http://localhost:8080/parents/2/children" } } } ] }, "_links": { "self": { "href": "http://localhost:8080/groups/1/parents" } } } 

最后,当我想看到小组中第一个父母的孩子时,我会去http:// localhost:8080 / parents / 1 / children

响应

 { "_embedded": { "children": [ { "firstName": "Richard", "lastName": "Giannoni" "_links": { "self": { "href": "http://localhost:8080/children/2" }, "child": { "href": "http://localhost:8080/children/2" }, "parent": { "href": "http://localhost:8080/children/2/parent" } } }, { "firstName": "Deeanne", "lastName": "Giannoni" "_links": { "self": { "href": "http://localhost:8080/children/1" }, "child": { "href": "http://localhost:8080/children/1" }, "parent": { "href": "http://localhost:8080/children/1/parent" } } } ] }, "_links": { "self": { "href": "http://localhost:8080/parents/1/children" } } } 

我希望能够调用一个端点,如http:// localhost:8080 / groups / search / findAllGroupMembers?group_id = 1

并让它返回组中的多级json,组中的所有父母以及每个父母的所有孩子。

我知道如何使用子查询编写查询以返回此信息,但我只是好奇是否有更多“JPA / Hibernate”方法来执行此操作?

谢谢!

编辑:修复使用Alan Hay的答案

GroupFullProjection.java

 @Projection(name = "groupFullProjection", types = {Group.class}) public interface GroupFullProjection { Long getGroupId(); String getName(); Set getParents(); } 

ParentFullProjection.java

 @Projection(name = "parentFullProjection", types = {Parent.class}) public interface ParentFullProjection { Long getParentId(); String getFirstName(); String getLastName(); Set getChildren(); } 

json回复所有必需的信息

端点: http:// localhost:8080 / groups / 1?projection = groupFullProjection

 { "name": "Hyde Park", "groupId": 1, "parents": [ { "children": [ { "firstName": "Richard", "lastName": "Giannoni", }, { "firstName": "Deeanne", "lastName": "Giannoni", } ], "parentId": 1, "firstName": "Cherice", "lastName": "Giannoni", "_links": { "self": { "href": "http://localhost:8080/parents/1{?projection}", "templated": true }, "groups": { "href": "http://localhost:8080/parents/1/groups" }, "children": { "href": "http://localhost:8080/parents/1/children" } } }, { "children": [ { "firstName": "Hanson", "lastName": "Feckey", } ], "parentId": 2, "firstName": "Aylmer", "lastName": "Feckey", "_links": { "self": { "href": "http://localhost:8080/parents/2{?projection}", "templated": true }, "groups": { "href": "http://localhost:8080/parents/2/groups" }, "children": { "href": "http://localhost:8080/parents/2/children" } } } ], "_links": { "self": { "href": "http://localhost:8080/groups/1" }, "group": { "href": "http://localhost:8080/groups/1{?projection}", "templated": true }, "parents": { "href": "http://localhost:8080/groups/1/parents" } } } 

Lazy / Eager加载与它完全无关。 应用程序中的REST服务由Spring Data Rest提供,并且了解它的外观以及如何更改它可能值得学习。

https://docs.spring.io/spring-data/rest/docs/current/reference/html/#repository-resources.fundamentals

基本上,您可以获得指向关联的链接,因为这些实体具有自己的存储库,这些存储库作为REST资源公开。 如果未将子/父作为REST资源公开,那么数据将被内联(因为没有其他方法可以访问它们)。

但是,您可以使用Projections来获取数据的替代视图。 因此,您可以定义一个内联关联的投影。 客户端可以请求此特定数据视图。

例如http:// localhost:8080 / groups / 1?projection = groupFullProjection

这涉及创建一个简单的接口,该接口定义要在该数据视图中公开的属性。

请参阅: https : //docs.spring.io/spring-data/rest/docs/current/reference/html/#projections-excerpts

这看起来像是这样的:

 @Projection(name = "parentFullProjecton", types = { Parent.class }) interface ParentFullProjection{ // inline the child collection Set getChildren(); // other fields } @Projection(name = "groupFullProjection", types = { Group.class }) interface GroupFullProjection{ //inline the parents collection and use the view which inlines Children Set getParent(); // other fields }