JPA / Hibernate: сохраняются отношения ManyToOne - PullRequest
0 голосов
/ 30 мая 2018

следующая ситуация.У меня есть три класса (резюме = биографические данные, профессиональный опыт и промышленность).Мой REST Controller получает запрос 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 может иметь уже существующие промышленные объекты в качестве ссылок (здесь ИТ) или новые (здесь продажи).

Здесь (я надеюсь) все важные части класса ProfessionalExperience и Industry

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: отдельная сущность передана в постоянное состояние , но безуспешно.

Отрасль:

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

Я играл с различными комбинациями CascadeTypes на ProfessionalExperience в отрасли, но так и не получил правильную комбинацию.Если я удаляю CascadeType из Industry, я просто получаю

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 голосов
/ 30 мая 2018

Наконец исправили.Я сделал две ошибки

  1. Я полностью удалил CascadaType из ProfessionalExperience (только что ушел 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;
    }
    

Возвращается значение true, если мы сравним два несохраненных ProfessionalExperiences, поэтому мой contains в addProfessionalExperience не работал должным образом.Я только что изменил последнюю строку на return id != null ? id.equals(that.id) : that.id != null, которая работает нормально.

Это наконец оставляет меня с ограничением, что я не могу назначать несохраненные отрасли, но Нарендер Сингх уже упоминал, как справиться с этим.

0 голосов
/ 30 мая 2018

Вы сделали отношения немного сложнее :) Хотя отношения будут зависеть от ваших вариантов использования, но я все же рекомендую иметь такие отношения:

  • Резюме для профессионального опыта: 1 к nдвунаправленный (который у вас уже есть)
  • Профессиональный опыт в промышленности: n-to-1 (вы упомянули, что у вас есть это отношение, но ваш код показывает, что у вас есть 1-n-n-двунаправленный)

Также создайте отрасль, метаданные, т. Е. У них будет свой жизненный цикл (вам необходимо сохранить его перед использованием)

Также, если вам нужен весь профессиональный опыт по отраслям, вы можете получить его, применивдополнительный запрос, что-то вроде findAllByIndustry(Industry industry).

PS включает вторичный кеш.он разделит нагрузку на производительность с базой данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...