Entity Manager returns duplicate copies of the @OneToMany associated object

I am developing an application to help my office track and manage reviews. In the application, I use JPA 2.0 with Hibernate 3.6.3 as the main provider. I also use spring to insert a persistence context into my DAO. I created the domain in such a way that there is a Review, Member and Role.

The problem that I encountered is when I get a review from the Entity Manager, the list of participants has duplicate instances of the same Participant (that is, an identifier) ​​if the Participant has several Roles. I also found that the number of duplicates is directly related to the number of roles (i.e. if the Participant has 3 Roles, then the Participant 3 times appears in the list of participants)

I used direct Hibernate before, but this is my first time using JPA, so I'm sure I did something wrong. I just don't know what that is.

Here is the code:

Overview:

@Entity public class Review extends BaseModel{ @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER, optional=false) private Item item; @Column(name="ISNEW", nullable=false) private boolean isNew; @Enumerated(EnumType.STRING) @Column(name="STATUS", nullable=false) private ReviewStatus status; @Enumerated(EnumType.STRING) @Column(name="PHASE", nullable=false) private Phase phase; @Enumerated(EnumType.STRING) @Column(name="REVIEW_TYPE", nullable=false) private ReviewType reviewType; @OneToMany( cascade=CascadeType.ALL, fetch=FetchType.EAGER) private List<Participant> participants; @OneToMany(cascade=CascadeType.ALL) private List<Defect> defects; @Column(name="START_DATE", nullable=false) private Date startDate; @Column(name="MEETING_DATE", nullable=false) private Date meetingDate; @Column(name="FINISH_DATE") private Date finishDate; @Column(name="DURATION", nullable=false) private Double duration; public Item getItem() { return item; } public void setItem(Item item) { this.item = item; } public List<Participant> getParticipants() { return participants; } public void setParticipants(List<Participant> participants) { this.participants = participants; } public List<Defect> getDefects() { return defects; } public void setDefects(List<Defect> defects) { this.defects = defects; } public boolean isNew() { return isNew; } public void setNew(boolean isNew) { this.isNew = isNew; } public Phase getPhase() { return phase; } public void setPhase(Phase phase) { this.phase = phase; } public Double getDuration() { return duration; } public void setDuration(Double duration) { this.duration = duration; } public ReviewStatus getStatus() { return status; } public void setStatus(ReviewStatus status) { this.status = status; } public Date getStartDate() { return startDate; } public void setStartDate(Date startDate) { this.startDate = startDate; } public Date getMeetingDate() { return meetingDate; } public void setMeetingDate(Date meetingDate) { this.meetingDate = meetingDate; } public Date getFinishDate() { return finishDate; } public void setFinishDate(Date finishDate) { this.finishDate = finishDate; } public ReviewType getReviewType() { return reviewType; } public void setReviewType(ReviewType reviewType) { this.reviewType = reviewType; } 

}

Participant:

 @Entity public class Participant extends BaseModel{ private Double inspectionTime; @ManyToOne(cascade=CascadeType.ALL, optional=false) private Person person; @ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER ) private Set<Role> roles; public Double getInspectionTime() { return inspectionTime; } public void setInspectionTime(Double preInspectionTime) { this.inspectionTime = preInspectionTime; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } } 

Role:

 @Entity public class Role extends BaseModel{ @Column(nullable=false, unique=true) private String name; private String responsiblity; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getResponsiblity() { return responsiblity; } public void setResponsiblity(String responsiblity) { this.responsiblity = responsiblity; } } 

Base:

 @MappedSuperclass public abstract class BaseModel { @Id @GeneratedValue private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } } 

DataAccessObject that uses entityManager:

 @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public class ReviewDaoJpaImpl implements ReviewDao { @PersistenceContext private EntityManager em; public Review getReviewById(Long id) { return em.find(Review.class, id); } } 

A call that receives duplicates:

  Review review = reviewDao.getReviewById(1776L); List<Participant> participants = review.getParticipants(); for (Participant participant : participants) { System.out.println(participant.getPerson().getName()); } 
+7
source share
2 answers

The task is called (problem N + 1) and is associated with an impatient selection of roles in the Participant.

The easiest way to handle the handle is to replace the impatient sample with lazy loading.

+12
source

Full link to hibernation documentation

Just set Criteria.DISTINCT_ROOT_ENTITY, it will create a set internally, and duplicates are automatically deleted.

 List result = session.createCriteria(Order.class) .setFetchMode("lineItems", FetchMode.JOIN) .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) .list(); 
+3
source

All Articles