当前位置: 首页 > 知识库问答 >
问题:

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

岳良策
2023-03-14

我有一个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<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

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

    //Constructor 
    //Getters and Setters
}

我为所有这些文件建立了如下存储库:

public interface GroupRepository extends PagingAndSortingRepository<Group, Long> {
    @RestResource(rel = "name-contains", path = "containsName")
    Page<Group> 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"
    }
  }
}

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

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

我知道如何编写带有子查询的查询来返回这些信息,但我只是好奇是否有一种更“JPA/Hibernate”的方法来实现这一点?

谢谢!

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

集体投射。JAVA

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

rojection.java

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

共有1个答案

宰父涵忍
2023-03-14

懒惰/急切的加载与此毫无关系。应用程序中的REST服务是由Spring Data REST提供的,了解它为什么会这样以及如何更改它可能是值得学习的。

https://docs.spring.io/spring-data/rest/docs/current/reference/html/#repository-资源。基本原理

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

但是,您可以使用投影来获取数据的其他视图。这样你就可以定义一个投影,它可以嵌入Assocation。客户端可以请求数据的这种特定视图。

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

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

见:https://docs.spring.io/spring-data/rest/docs/current/reference/html/#projections-节选

这可能看起来像:

@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
}
 类似资料:
  • 我正在实现一个基于实体属性值的持久化机制。所有数据库访问都是通过Hibernate完成的。我有一个包含节点路径的表,它非常简单,只有一个id和一个路径(字符串)。路径数量很少,大约几千条。 主表有数百万行,我没有重复路径,而是将路径规范化为它们自己的表。以下是我在插入主表时想要的行为 1)检查路径表中是否存在路径(通过实体管理器查询,使用路径值作为参数) 2) 如果不存在,则插入并获取id(通过实

  • 我希望在spring boot中返回类似以下内容的json响应: 我的RestController如下所示 但我得到的反应是这样的

  • 问题内容: 我有一个包含列表的类,并使用一对多的映射将其映射,如下所示: 这些Order还具有一个,用于根据以下条件进行过滤: 这可以正常工作,并且结果符合预期。 现在是我的问题:为什么当我将访存类型显式设置为时EAGER,Orders在结果列表中出现多次? 我如何更改我的标准代码才能在新设置下达到相同的结果? 问题答案: 如果我正确理解你的配置,这实际上是预期的行为。 你Order在任何结果中都

  • 问题内容: 在我看来,对多租户的支持现在已经hibernate了将近六个月,此后至少更新了一次。 在JPA之外获得多租户会话看起来相当琐碎: 但是,如何在通过JPA使用hibernate的应用程序中启用它呢?(如果可能的话)。 提前致谢。 问题答案: 您可以通过persistence.xml中的属性对其进行配置,如下所示: 如果使用SCHEMA,则不需要多租户策略。 您还可以在代码中设置这些属性,

  • 问题内容: 我正在使用JPA 2.0并hibernate。我有一个用户类和一个组类,如下所示: 然后,我创建一个用户和组,然后将该用户分配给该组。 我要拥有的是删除组时(当然),该组将被删除,并且该组具有的所有用户-组关系将从USER_GROUP连接表中自动删除,但用户本身不会从USER表。 使用上面的代码,当我删除组时,只有GROUP表中的行将被删除,并且用户在USER_GROUP连接表中仍然具

  • 我需要一个概念来设计一个使用Spring boot、Hibernate和JPA的多数据库应用程序。 目前我正在考虑支持4个关系数据库(Mysql,H2,SQLLite,Oracle)。 我所做的是使用spring boot profile特性选择正确的数据库profile,然后加载相关的数据库属性。