【问题标题】:Entity Manager returns duplicate copies of a @OneToMany related Entity实体管理器返回 @OneToMany 相关实体的重复副本
【发布时间】:2011-08-09 21:55:18
【问题描述】:

我正在开发一个应用程序来帮助我的办公室跟踪和管理评论。在应用程序中,我使用 JPA 2.0 和 Hibernate 3.6.3 作为我的底层提供程序。我还使用 spring 将持久性上下文注入到我的 DAO 中。我已经建立了一个域,以便有一个评论、一个参与者和一个角色实体。

我遇到的问题是,当我从 Entity Manager 获得评论时,如果参与者拥有多个角色,参与者列表中会出现相同参与者(即相同 ID)的重复副本。我还发现重复的数量与角色的数量直接相关(即,如果参与者有 3 个角色,那么参与者在评论的参与者列表中出现 3 次)

我以前直接使用过 Hibernate,但这是我第一次使用 JPA,所以我确定我配置了错误。我只是不知道它是什么。

代码如下:

评论:

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

}

参与者:

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

}

角色:

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

}

基地:

@MappedSuperclass
public abstract class BaseModel {

    @Id
    @GeneratedValue
    private Long id;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }   

}

使用entityManager的DataAccessObject:

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

接收重复的调用:

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

【问题讨论】:

标签: hibernate spring jpa-2.0


【解决方案1】:

这个问题被称为 (N+1 Problem) 并且与 Participant 中角色的急切获取有关。

处理它的最简单方法是通过延迟加载替换急切获取。

【讨论】:

  • 就是这样。我很尴尬 N+1 没有出现在我对这个问题的任何搜索中。我想我没有使用正确的关键字。谢谢你的帮助。我知道我做错了什么。
  • 你能详细解释一下N+1吗?
  • @Odelya Google 对于“休眠中的 n+1 问题”的第一次点击是:stackoverflow.com/questions/97197/what-is-the-n1-selects-issue
  • 也可以使用@Fetch(FetchMode.SUBSELECT) 而不是让它变得懒惰。
【解决方案2】:

Complete Reference to Hibernate Documentation

只需输入 Criteria.DISTINCT_ROOT_ENTITY 它将在内部创建一个 Set 并自动删除重复项。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-06
    • 1970-01-01
    相关资源
    最近更新 更多