JPA / Hibernate:持久化ManyToOne关系

时间:2018-05-30 09:24:26

标签: java hibernate spring-boot jpa

以下情况。我有三个班级(简历=简历,专业经验和行业)。我的REST控制器收到更新现有CV的PUT请求。 JSON看起来像这样:

{
"id": 102,
"currentSalery":100,
"desiredSalery":120,
"professionalExperiences": [
    { "jobTitle" : "Technical Project Lead", "company" : "Example Company 1", 
        "monthStart" : 10, "yearStart" : 2008, "monthEnd" : 11, "yearEnd" : 2017, "industry" : {"id" : 1001, "name" : "IT"}
    },
    { "jobTitle" : "Software Consultant", "company" : "Example Company 2", 
        "monthStart" : 11, "yearStart" : 2017, "industry" : {"name" : "Sales"}
    }
]}

CV和ProfessionalExperience之间的关系是1:n。 ProfessionalExperience与Industry之间的关系是n:1。请注意,JSON可以将现有的Industry Objects作为引用(此处为IT)或新的(此处为Sales)。

这里(我希望)是ProfessionalExperience和Industry class的所有重要部分

ProfessionalExperience:

@Entity
public class ProfessionalExperience  implements Serializable {

@Id
@SequenceGenerator(name="prof_seq", initialValue=1, allocationSize=100)
@GeneratedValue(strategy= GenerationType.SEQUENCE, generator="prof_seq")
private Long id;

@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@JoinColumn(name = "industry_id")
private Industry industry;

@JsonIgnore
@ManyToOne
@JoinColumn(name = "cv_id")
private CV cv;

public Industry getIndustry() {
    return industry;
}

/**
 * Set new industry. The method keeps relationships consistency
 * * this professionalExperience is removed from the previous industry
 * * this professionalExperience is added to next industry
 *
 * @param industry
 */
public void setIndustry(Industry industry) {
    //prevent endless loop
    if (sameAsFormer(industry))
        return ;
    //set new industry
    Industry oldIndustry = this.industry;
    this.industry = industry;
    //remove from the industry
    if (oldIndustry!=null)
        oldIndustry.removeProfessionalExperience(this);
    //set myself into industry
    if (industry!=null)
        industry.addProfessionalExperience(this);
}


private boolean sameAsFormer(Industry newIndustry) {
    return industry==null? newIndustry == null : industry.equals(newIndustry);
}



}

我实现了JPA/Hibernate: detached entity passed to persist中提到的setter但没有成功。

行业:

@Entity
public class Industry  implements Serializable {

@Id
@SequenceGenerator(name="industry_seq", initialValue=1, allocationSize=100)
@GeneratedValue(strategy= GenerationType.SEQUENCE, generator="industry_seq")
private Long id;

 // REMOVED cascade = CascadeType.ALL for testing
@JsonIgnore
@OneToMany(mappedBy = "industry")
private List<ProfessionalExperience> professionalExperiences = new ArrayList<>();

/**
 * Returns a collection with owned professional experiences. The
 * returned collection is a defensive copy.
 *
 * @return a collection with owned professional experiences
 */
public List<ProfessionalExperience> getProfessionalExperiences() {
    return new ArrayList<ProfessionalExperience>(professionalExperiences);
}

/**
 * Add new professionalExperience to the industry. The method keeps
 * relationships consistency:
 */
public void addProfessionalExperience(ProfessionalExperience professionalExperience) {
    // prevent endless loop
    if(professionalExperiences.contains(professionalExperience))
        return;

    professionalExperiences.add(professionalExperience);
    professionalExperience.setIndustry(this);
}

/**
 * Removes the professionalExperience from the industry. The method keeps
 * relationships consistency:
 */
public void removeProfessionalExperience (ProfessionalExperience professionalExperience) {
    // prevent endless loop
    if(!professionalExperiences.contains(professionalExperience))
        return;

    professionalExperiences.remove(professionalExperience);
    professionalExperience.setIndustry(null);
}   }

我在ProfessionalExperience的一个行业方面玩过不同的CascadeTypes组合,但从来没有得到正确的组合。如果我从Industry中删除CascadeType,我就得到

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: x.y.z.Industry

两侧都有CascadeType.ALL,它取决于我想要保存的Industry对象。如果有所有新的(或没有重复),那么它正在工作。但是如果两个ProfessionalExperiences引用相同的Industry对象,我会得到这个:

Caused by: java.lang.IllegalStateException: Multiple representations of the same entity [x.y.z.Industry#303] are being merged

有人可以帮帮我吗?提前完成!

2 个答案:

答案 0 :(得分:0)

你让关系变得有点复杂:) 虽然关系取决于您的使用案例,但我仍然建议建立这样的关系:

  • CV to Professional Experience:1对n双向(您已经拥有)
  • 行业专业经验:n-to-1(你提到你有这种关系,但你的代码显示你有1对n的双向)

同样制作行业,元数据,即它将拥有自己的生命周期(您需要在使用之前保存它)

此外,如果您需要按行业划分的所有专业经验,那么您可以通过触发其他查询来获取它们,例如<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <div id="app"> <input v-model="tax" type="number" > <button @click="reset">Reset</button> </div>

P.S。启用二级缓存。它将与数据库分担性能负担。

答案 1 :(得分:0)

最后修好了。我犯了两个错误

  1. 我从ProfessionalExperience中完全删除了CascadaType(刚刚离开ManyToOne)并再次向行业添加CascadeType.ALL
  2. 我的自动生成等于ProfessionalExperience中的方法不正确:

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof ProfessionalExperience)) return false;
    
        ProfessionalExperience that = (ProfessionalExperience) o;
    
        return id != null ? id.equals(that.id) : that.id == null;
    }
    
  3. 如果我们比较两个未保存的ProfessionalExperiences,则返回true,因此contains中的addProfessionalExperience未按预期工作。我刚刚将最后一行更改为return id != null ? id.equals(that.id) : that.id != null,工作正常。

    这最终让我有了限制,我无法分配未保存的行业,但Narender Singh已经提到了如何处理这个问题。