I am developing an application to help my office track and manage reviews. In the application, I am using JPA 2.0 with Hibernate 3.6.3 as my underlying provider. I am also using spring to inject the persistence context into my DAO. I have built the domain such that there is a Review, a Participant and a Role Entity.
The problem I am having is when I get a Review from the Entity Manager there are duplicate copies of the same Participant(i.e. same ID) in the list of participants if the Participant has more than one Role. I have also found the number of duplicates is directly related to the number Roles (i.e. if the a Participant has 3 Roles then the Participant occurs 3 times in the Review's list of participants)
I have used straight Hibernate before but this is my first time using JPA so I am sure I have configured something wrong. I just don't know what it is.
Here is the code:
Review:
@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 which uses the 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);
    }
}
Call which receives duplicates:
    Review review = reviewDao.getReviewById(1776L);
    List<Participant> participants = review.getParticipants();
    for (Participant participant : participants)
    {
        System.out.println(participant.getPerson().getName());
    }
 
     
     
    