简体   繁体   中英

Hibernate, Spring. Multiple representations of the same entity are being merged

I have been getting an error while trying to update a list of entities (Job):

Multiple representations of the same entity [com.introlabsystems.upworkscraper.model.entities.Skill#42] are being merged. Detached: [Skill(id=42, skillName=Web Scraping)]; Detached: [Skill(id=42, skillName=Web Scraping)]

I think it is in case that three entities have ManyToMany relationship with Skill entity.

@Entity
public class Job {

  @Id
  @Column(unique = true, nullable = false)
  @SerializedName(value = "ciphertext")
  private String id;
  @Column(length = 2048)
  private String title;
  @Column(columnDefinition = "TEXT")
  @JsonAdapter(value = DescriptionAdapter.class)
  private String description;
  @SerializedName(value = "subcategory2")
  private String category;

  @ManyToMany(cascade = {
      CascadeType.MERGE
  }, fetch = FetchType.EAGER)
  @JoinTable(
      joinColumns = {@JoinColumn(name = "job_id")},
      inverseJoinColumns = {@JoinColumn(name = "skill_id")}
  )
  @JsonAdapter(value = SkillsAdapter.class)
  private Set<Skill> skills;

  private String duration;
  private String engagement;
  @JsonAdapter(value = AmountAdapter.class)
  private String amount;
  @JsonAdapter(value = AmountAdapter.class)
  private String maxAmount;

  private String freelancersToHire;
  private String enterpriseJob;
  private String proposalsTier;
  private String stickyLabel;
  @SerializedName(value = "tierLabel")
  private String tier;

  @JsonAdapter(value = DateTimeAdapter.class)
  private LocalDateTime createdOn;
  @JsonAdapter(value = DateTimeAdapter.class)
  private LocalDateTime publishedOn;
  @JsonAdapter(value = DateTimeAdapter.class)
  private LocalDateTime renewedOn;
  private LocalDateTime hiredOn;

  @OneToOne(cascade = CascadeType.ALL)
  private Contract contract;

  @SerializedName(value = "type")
  @JsonAdapter(value = JobTypeAdapter.class)
  @Enumerated(value = EnumType.STRING)
  private JobType jobType;

  @Enumerated(value = EnumType.STRING)
  private JobStatus jobStatus = JobStatus.OPEN;

  private String projectStage;
  private String ongoingProject;
  private String projectType;
  private String finalOutput;
  private String Platform;
  private String oneTimeProject;
  private String jobSuccessScore;
  private String includeRisingTalent;
  private String englishLevel;
  private String upworkHours;
  private String freelancerType;
  private String preferredLocation;
  private String interviewing;

  @OneToOne(cascade = CascadeType.ALL)
  private PrivateStatus privateStatus;
}

@Data
@Entity
public class Skill {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;
  private String skillName;
}

@Data
@Entity
public class Contract {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @JsonAdapter(value = ContractJobIdAdapter.class)
  @SerializedName(value = "jobInfo")
  private String jobId;

  private String totalCharge;
  private String totalHours;

  @JsonAdapter(value = AmountAdapter.class)
  private String rate;

  @JsonAdapter(value = DateTimeAdapter.class)
  private LocalDateTime startDate;
  @JsonAdapter(value = DateTimeAdapter.class)
  private LocalDateTime endDate;

  @ManyToOne(cascade = CascadeType.MERGE)
  @SerializedName(value = "contractorInfo")
  private Freelancer freelancer;

  @ManyToOne(cascade = CascadeType.ALL)
  private Client client;
}

@Data
@Entity
public class Freelancer {

  @Id
  @SerializedName(value = "ciphertext")
  private String id;
  @SerializedName(value = "contractorName")
  private String name;
  @Column(length = 2048)
  private String title;
  @Column(columnDefinition = "TEXT")
  @JsonAdapter(value = DescriptionAdapter.class)
  private String description;
  @JsonAdapter(value = FreelancerLocationAdapter.class)
  private String location;

  @ManyToMany(cascade = CascadeType.ALL)
  @JoinTable(
      joinColumns = {@JoinColumn(name = "freelancer_id")},
      inverseJoinColumns = {@JoinColumn(name = "skill_id")}
  )
  @JsonAdapter(value = SkillsAdapter.class)
  private Set<Skill> skills;

  private String hourlyRate;
  private String privateFeedbackHireAgain;
  private String rating;
  private String scores;
  private String totalEarnings;
  private String totalFeedback;
  private String totalFixedJobs;
  private String totalHourlyJobs;
  private String totalHours;
  private String totalJobsWorked;
  private String topRatedStatus;
  private String successScore;

  @OneToMany(cascade = CascadeType.ALL)
  private List<ContractHistory> contractHistories;

  @ManyToMany(cascade = {
      CascadeType.PERSIST,
      CascadeType.MERGE
  })
  @JoinTable(
      joinColumns = {@JoinColumn(name = "freelancer_id")},
      inverseJoinColumns = {@JoinColumn(name = "agency_id")}
  )
  private Set<Agency> agency;
}


@Data
@Entity
public class Agency {

  @Id
  @SerializedName(value = "chipertext")
  private String id;

  @JsonAdapter(value = AmountAdapter.class)
  private String minRate;
  @JsonAdapter(value = AmountAdapter.class)
  private String maxRate;
  private String city;
  private String country;
  @Column(columnDefinition = "TEXT")
  @JsonAdapter(value = DescriptionAdapter.class)
  private String description;
  private String jobSuccessScore;
  private String name;
  @Column(length = 2048)
  private String title;
  private String topRatedStatus;
  private String totalEarnings;
  private String totalHours;
  private String totalJobs;
  private String recentEarnings;
  private String averageRecentEarnings;
  @JsonAdapter(value = DateTimeAdapter.class)
  private LocalDateTime memberSince;

  @ManyToMany(cascade = CascadeType.ALL)
  @JoinTable(
      joinColumns = {@JoinColumn(name = "agency_id")},
      inverseJoinColumns = {@JoinColumn(name = "skill_id")}
  )
  @JsonAdapter(value = SkillsAdapter.class)
  private Set<Skill> skills;
}

I trying to save a job. Before do it I get skills from job, agency, freelancer, trying to search them in DB and substitute ids.

for (Skill skill: skills) 
    {
        Skill skillWithID = 
        skillDataService.findBySkillName(skill.getSkillName())
          .orElseGet(() -> skillDataService.save(skill));
        skill.setId(skillWithID.getId());
    }

Changed

CascadeType.MERGE

to

CascadeType.PERSIST,
CascadeType.DETACH,
CascadeType.REFRESH,
CascadeType.REMOVE

and it works

you are facing this issue because you are adding the object to the list which is already available.while performing save operation with oneToMany mapping

Now removing CascadeType.MERGE from cascade is one solution but not a best solution because after removing MERGE from Cascade you won't be able to update the mapped object ever

If you want to perform update operation also along with save for the mapped objects then before adding mapped object to list/collection just check/search within the list for the object and if the mapped object is available within the list then perform operation on that particular object.

check my other post for example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM