简体   繁体   English

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

[英]How can I return multi-level json using Hibernate/JPA in Spring Boot

I have a Postgres Database that has 4 tables Parents, Children, Groups and Group_Membership. 我有一个Postgres数据库,有4个表父母,子女,团体和Group_Membership。

Groups can have multiple parents and Parents can have multiple groups. 群组可以有多个父母,父母可以有多个群组。 Parents can have multiple children but children can only have one parent. 父母可以有多个孩子,但孩子只能有一个孩子。

This is the dumbed down version of the schema. 这是模式的愚蠢版本。 在此输入图像描述

I am using Spring Boot with Hibernate JPA. 我正在使用Spring Boot和Hibernate JPA。

Parent.java 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<Child> children;
    @ManyToMany(cascade = { CascadeType.ALL })
    @JoinTable(
            name= "Group_Membership",
            joinColumns = { @JoinColumn(name = "parent_id") },
            inverseJoinColumns = { @JoinColumn(name = "group_id") }
    )
    private Set<Group> groups = new HashSet<>();

    //Constructor 
    //Getters and Setters
}

Child.java 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 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<Parent> parents = new HashSet<>();

    //Constructor 
    //Getters and Setters
}

I have repositories for all of them set up like this: 我有所有这些设置的存储库:

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

Group Membership Table 集团成员表

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)
);

When I go to http://localhost:8080/groups 当我去http:// localhost:8080 / groups

I get this response: 我收到了这个回复:

{
  "_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
  }
}

Then when I want to look at the parents in the group I go to http://localhost:8080/groups/1/parents 然后当我想看看小组中的父母时,我会去http:// localhost:8080 / groups / 1 / parents

Response 响应

{
  "_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"
    }
  }
}

Finally when I want to see the children of the first parent in the group I go to http://localhost:8080/parents/1/children 最后,当我想看到组中第一个父母的孩子时,我会去http:// localhost:8080 / parents / 1 / children

Response 响应

{
  "_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"
    }
  }
}

I would like to be able to call one endpoint like http://localhost:8080/groups/search/findAllGroupMembers?group_id=1 我希望能够调用一个端点,如http:// localhost:8080 / groups / search / findAllGroupMembers?group_id = 1

and have it return multi level json with the group, all parents in the group, and all of the children of each parent. 并让它返回组中的多级json,组中的所有父母以及每个父母的所有孩子。

I know how to write a query with sub-queries to return this information, but I was just curious if there is a more "JPA/Hibernate" way to do this? 我知道如何使用子查询编写查询以返回此信息,但我只是好奇是否有更多的“JPA / Hibernate”方法来执行此操作?

Thanks! 谢谢!

EDIT: Fixed using Alan Hay's answer 编辑:修复使用Alan Hay的答案

GroupFullProjection.java GroupFullProjection.java

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

ParentFullProjection.java ParentFullProjection.java

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

json Response with all required information json回复所有必需的信息

Endpoint: http://localhost:8080/groups/1?projection=groupFullProjection 端点: 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 loading has absolutely nothing to do with it. Lazy / Eager加载与它完全无关。 The REST services in your application are provided by Spring Data Rest and having an understanding of why it looks like it does and how you can change it is probably worth learning. 应用程序中的REST服务由Spring Data Rest提供,并且了解它的外观以及如何更改它可能值得学习。

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

Essentially you get the links to the associations because these entities have their own repositories which are exposed as REST respources. 基本上,您可以获得指向关联的链接,因为这些实体具有自己的存储库,这些存储库作为REST资源公开。 If children/parent were not exposed as REST reources then the data would be inlined (as there would be no other way to access them). 如果未将子/父作为REST资源公开,那么数据将被内联(因为没有其他方法可以访问它们)。

You can however use Projections to fetch alternative views of the data. 但是,您可以使用Projections来获取数据的替代视图。 So you can then define a projection that would inline the assocations. 因此,您可以定义一个内联关联的投影。 Clients can request this specific view of the data. 客户端可以请求此特定的数据视图。

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

This involves creating a simple interface that defines the properties to be exposed in that view of the data. 这涉及创建一个简单的接口,该接口定义要在该数据视图中公开的属性。

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

This might look something like: 这看起来像是这样的:

@Projection(name = "parentFullProjecton", types = { Parent.class })
interface ParentFullProjection{

    // inline the child collection
    Set<Child> getChildren();

    // other fields
}

@Projection(name = "groupFullProjection", types = { Group.class })
interface GroupFullProjection{

    //inline the parents collection and use the view which inlines Children
    Set<ParentFullProjecton> getParent();

    // other fields
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM