JPA - 如果已获取父项,则 JpaRepository 子记录具有父 ID 而不是实体记录 [重复]

Posted

技术标签:

【中文标题】JPA - 如果已获取父项,则 JpaRepository 子记录具有父 ID 而不是实体记录 [重复]【英文标题】:JPA - JpaRepository child record has parent id instead of entity record if parent already fetched [duplicate] 【发布时间】:2020-05-22 19:25:06 【问题描述】:

我仍在掌握 JPA 概念,似乎无法在任何地方找到我的问题的答案!

假设

两个类都用@GeneratedValue(strategy = GenerationType.IDENTITY)注解,都有getter和setter。

Parent
    ....
    @OneToMany(mappedBy = "parent")
    Collection<Child> children;
    ....


Child
    ...
    @JoinColumn(name = "parent", referencedColumnName = "id", nullable = true)
    @ManyToOne(optional = false)
    Parent parent;
    ...

然后我实现了标准的 JpaRepository 并设置了我的控制器

问题出在这里 当我查询所有子记录时,只有映射到特定父项的第一个子记录才会包含父实体对象。其余的将有一个引用父实体的 id。

这是一个例子: 从 POSTMAN 获取所有子节点返回:

[
    
        "id": 1,
        "name": "child1",
        "parent": 
            "id": 1,
            "firstName": "..."
            ...
            
    ,
    
        "id": 2,
        "name": "child2",
        "parent": 1
    
    
        "id": 3,
        "name": "child3",
        "parent": 
            "id": 2,
            "firstName": "..."
            ...
            
    ,
    
        "id": 4,
        "name": "child4",
        "parent": 2
    
]

如您所见,child2 只有 "parent": 1,因为 child1 首先映射到该父级! 同样,child4 只有 "parent": 2,因为 child3 首先映射到该父级!

谁能解释一下这种行为?我在父母身上试过fetch = FetchType.EAGER,但没有帮助! 我希望所有的孩子都有一个全面的父对象来防止另一个 DB 之旅。

提前致谢!

用实际课程更新问题:


家长

package backend.application.payroll.models;

import com.fasterxml.jackson.annotation.*;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;

@Data
@Entity
@Table(name = "employees")
@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "id")
public class Employee implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    @Column(name = "emp_code", nullable = false)
    private String empCode;
    @Column(name = "first_name", nullable = false)
    private String firstName;
    @Column(name = "middle_name", nullable = true)
    private String middleName;
    @Column(name = "last_name", nullable = false)
    private String lastName;
    @Column(name = "dob", nullable = false)
    @Temporal(TemporalType.DATE)
    private Date dob;
    @Column(name = "id_number", nullable = true)
    private String idNumber;
    @Column(name = "passport_number", nullable = true)
    private String passportNumber;
    @Column(name = "email_address", nullable = true)
    private String emailAddress;
    @OneToOne(cascade = CascadeType.DETACH)
    @JoinColumn(name = "pay_grade", referencedColumnName = "id", nullable = true)
    private Salary payGrade;
    @Column(name = "basic_pay", nullable = true)
    private BigDecimal basicPay;
    @OneToOne(cascade = CascadeType.DETACH)
    @JoinColumn(name = "department", referencedColumnName = "id", nullable = true)
    private Department department;
    @OneToOne(cascade = CascadeType.DETACH)
    @JoinColumn(name = "position", referencedColumnName = "id", nullable = true)
    private Position position;
    @Column(name = "tax_number", nullable = true)
    private String taxNumber;
    @Column(name = "hire_date", nullable = true)
    @Temporal(TemporalType.DATE)
    private Date hireDate;
    @Column(name = "address1", nullable = true)
    private String address1;
    @Column(name = "address2", nullable = true)
    private String address2;
    @Column(name = "postal_code", nullable = true)
    private String postalCode;
    //country
    @Column(name = "phone_number", nullable = true)
    private String phoneNumber;
    //banking details

    //HERE IT WORKS FINE SINCE IT'S ONETOONE - YOU CAN IGNORE
    @OneToOne(mappedBy = "employee")
    //@JsonManagedReference//used in conjunction with @JsonBackReference on the other end - works like @JsonIdentityInfo class annotation.
    private User user;

    //THIS IS WHAT CAUSING THE PROBLEM
    @OneToMany(mappedBy = "owner", fetch = FetchType.LAZY)
    //@JsonBackReference
    @JsonIgnore
    private Set<Costcentre> costcentres = new HashSet<>();

    public Employee() 

    


儿童

package backend.application.payroll.models;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;


@Entity
@Table(name = "costcentres")
@Data
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Costcentre implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    @Column(name = "name", nullable = false)
    private String name;
    @Column(name = "description", nullable = true)
    private String description;
    @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
    @JoinColumn(name = "owner", referencedColumnName = "id", nullable = true)
    //@JsonManagedReference
    private Employee owner; //CULPRIT

    public Costcentre() 

    
    public Costcentre(long id, String name, String description) 
        super();
        this.id = id;
        this.name = name;
        this.description = description;
    

【问题讨论】:

【参考方案1】:

在父子节点上添加JsonIdentityInfo,您可以在父节点上添加fetch = FetchType.EAGER,在父节点上添加JsonIgnore以忽略获取循环子节点和父节点

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")

,像这样:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
Parent
    ....
    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
    @JsonIgnore
    Collection<Child> children;
    ....


@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
Child
    ...
    @JoinColumn(name = "parent", referencedColumnName = "id", nullable = true)
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    Parent parent;
    ...

【讨论】:

感谢您的回复@sc0der 除了fetch = FetchType.EAGER,我已经有了这些注释,但即使这样也没有解决问题。问题仍然存在。我什至考虑放弃我的整个数据库,因为我后来才添加了这种关系,但我怀疑这会有所帮助(我认为 JPA 足够聪明)。我认为我唯一做的就是删除子表以确保正确重新创建 FK,但即使这样我也没有运气! 我之前尝试实现Serializable,它没有改变任何东西,我将其删除。我刚刚把它放回去,结果一样。某些东西显然阻止了对父母的多次检索,我想知道它是什么!事实上,我什至不认为这与多次检索有关,这只是嵌入先前检索到的实体的结果的问题! JSON问题?太混乱了! 查看这个:tutorialspoint.com/jackson_annotations/… 还有这个:baeldung.com/… 我实际上遇到了这些文章并尝试返回并管理参考但没有运气!如果用实际的 2 个类更新我的问题可以吗? (希望它适合)

以上是关于JPA - 如果已获取父项,则 JpaRepository 子记录具有父 ID 而不是实体记录 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

JPA / Hibernate - 删除子项删除父项(从同一个表)

如何通过 JPA 获取已保存对象的 id?

在JPA中删除子项时保持实体关系同步

spring boot快速入门 5: 事务管理

具有按父项分组的变体的产品

SQL Server:如果左连接没有结果,则返回占位符/文本[重复]