Spring JPA,Hibernate 仅从其他实体获取 PK 或 Id

Posted

技术标签:

【中文标题】Spring JPA,Hibernate 仅从其他实体获取 PK 或 Id【英文标题】:Spring JPA, Hibernate fetch PK or Ids only from other entities 【发布时间】:2018-03-31 20:39:18 【问题描述】:

我有以下三个实体:父母:

@Setter
@Getter
@Entity
public class Parent 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String parentName;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, targetEntity = Child.class)
    private List<Child> children;

孩子:

@Setter
@Getter
@Entity
public class Child 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String childName;

    @ManyToOne(targetEntity = Parent.class, cascade = CascadeType.MERGE)
    @JoinColumn(name = "parent_id")
    private Parent parent;

    @OneToMany(mappedBy = "child", cascade = CascadeType.ALL)
    private List<GrandChild> grandChildren;

孙子:

@Getter
@Setter
@Entity
public class GrandChild 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(cascade = CascadeType.MERGE, targetEntity = Child.class)
    @JoinColumn(name = "child_id")
    private Child child;

当我打电话给parentRepository.findById(id) 时,我会得到一个父母及其孩子和孩子的grandChildren。但是,这会导致 2 个问题:

无限递归com.fasterxml.jackson.databind.JsonMappingException:,因为所有实体都包含相互引用。

来自 Hibernate 的不必要查询,因为我的 ParentDto 仅包含 List&lt;Long&gt; childrenIds

有没有办法让 Hibernate 从每个实体的引用中仅查询 Id 或 PK 而不是获取整个对象?类似的东西:

Parent: 
  "parentName" : "Parent Name",
  "children" : [ 
    "id" : 0
  , 
    "id" : 1
  , 
    "id" : 2
   ]

或者:Parent: "parentName" : "Parent Name", "childrenIds" : [0, 1, 2]

【问题讨论】:

我会得到一个父母和它的孩子和孩子的孙子:不,你不会。 OneToMany 关联是惰性的。 无限递归:否:Hibernate 当然可以处理这个问题,既然您将 DTO 序列化为 JSON,为什么要将实体序列化为 JSON? @JBNizet 我创建了 1 个父母和 3 个孩子,每个孩子有 3 个孙子。当我找回那个父母时,我有 3 个孩子和所有 9 个孙子。我究竟做错了什么 ? [图片链接]:imgur.com/JYypVjc [图片链接]:imgur.com/vC08yPo @JBNizet 关于无限递归:只是有时我将数据记录出来查看,它会引发异常。 您只想获取childIds 而不是grandChildrenIds。对吗? 通过将父级序列化为 JSON,Jackson 在子级集合中导航,然后到孙子级集合,以生成 JSON。这迫使 JPA 初始化惰性集合,从而从数据库中加载所有内容。如果您没有要求将父级序列化为 JSON 的日志记录语句,而是仅将父级转换为 DTO,而不通过孙子导航(因为您不希望它们出现在 DTO 中),JPA 不会初始化 th 孙子集合。 【参考方案1】:

所以给定一个parentId,你想检索parentName,并且只有关联的childIds -

我宁愿在childRepository 中添加new method with @Query,而不是通过parentRepository。该方法和@Query 如下所示:

@Query("select c.parent.id, c.parent.parentName, c.id from Child c where c.parent.id = ?1")

List<Object[]> findChildIdsByParentId(Long parentId);

每个对象 [] 将在索引 0 处包含 parentId,在索引 1 处包含 parentName,在 2 处包含 childId。

一旦你有了这些数据,你就可以将它填充到符合你的 json 格式的 DTO(杰克逊稍后序列化)中。

【讨论】:

【参考方案2】:

关于第一个问题 - 为了避免无限递归,只需在每个“子”集合中添加注释 @JsonIgnoreProperties,例如:

@JsonIgnoreProperties("parent")
private List<Child> children;

@JsonIgnoreProperties("child")
private List<GrandChild> grandChildren;

【讨论】:

以上是关于Spring JPA,Hibernate 仅从其他实体获取 PK 或 Id的主要内容,如果未能解决你的问题,请参考以下文章

Spring-data-jpa + Hibernate 未创建预期表

EAGER 和分页:Spring MVC + JPA Repository + Hibernate

仅从 Spring Data JPA 中的联接表(多对多)中选择特定列

将 Hibernate Sessions 功能与 Spring Data JPA 一起使用

Spring Data JPA / Hibernate中锁的范围是什么?

spring.jpa.hibernate.hbm2ddl 和 spring.jpa.hibernate.ddl 之间的区别