jpa多对一关联在更新时创建重复

jpa多对一关联在更新时创建重复,jpa,duplicates,many-to-one,Jpa,Duplicates,Many To One,我有下面的会议桌 package ng.telecomroadmap.model; import java.io.Serializable; import javax.persistence.*; import java.util.Date; import java.util.List; /** * The persistent class for the meetings database table. * */ @Entity @Table(name="meetings")

我有下面的会议桌

 package ng.telecomroadmap.model;

import java.io.Serializable;
import javax.persistence.*;
import java.util.Date;
import java.util.List;


/**
 * The persistent class for the meetings database table.
 * 
 */
@Entity
@Table(name="meetings")
@NamedQuery(name="Meeting.findAll", query="SELECT m FROM Meeting m")
public class Meeting implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int meetingsid;

    @Temporal(TemporalType.DATE)
    private Date date;

    private String description;

    //bi-directional many-to-one association to Meetingsattachment
    @OneToMany(mappedBy="meeting", cascade = CascadeType.PERSIST)
    private List<Meetingsattachment> meetingsattachments;

    public Meeting() {
    }

    public int getMeetingsid() {
        return this.meetingsid;
    }
public void setMeetingsid(int meetingsid) {
    this.meetingsid = meetingsid;
}

public Date getDate() {
    return this.date;
}

public void setDate(Date date) {
    this.date = date;
}

public String getDescription() {
    return this.description;
}

public void setDescription(String description) {
    this.description = description;
}

public List<Meetingsattachment> getMeetingsattachments() {
    return this.meetingsattachments;
}

public void setMeetingsattachments(List<Meetingsattachment> meetingsattachments) {
    this.meetingsattachments = meetingsattachments;
}

public Meetingsattachment addMeetingsattachment(Meetingsattachment meetingsattachment) {
    getMeetingsattachments().add(meetingsattachment);
    meetingsattachment.setMeeting(this);

    return meetingsattachment;
}

public Meetingsattachment removeMeetingsattachment(Meetingsattachment meetingsattachment) {
    getMeetingsattachments().remove(meetingsattachment);
    meetingsattachment.setMeeting(null);

    return meetingsattachment;
}}
我正在调用此方法以进行更新

public String updateButton(){
        List<Meetingsattachment> mal= meeting.getMeetingsattachments();

        //fileuploading code removed from here for troubleshooting


        mrm.updateMeeting(meeting); 



        return "meetings?faces-redirect=true";


    }
现在我的问题是,当我更新会议记录时,它会复制会议附件中已插入的值

示例:假设我有一个id为27的会议,在会议附件中我有以下内容

31  /path/to/files/t22-1700585698/1.pdf 27
现在,当我尝试编辑meeting 27并调用updatebutton方法而不更改任何内容或在meetings附件表中附加任何新附件时,会发生以下情况

31  /path/to/files/t22-1700585698/1.pdf 27
32  /path/to/files/t22-1700585698/1.pdf 27
我不知道我做错了什么,会议附件又被添加到数据库中了

从下面的答案中,我了解到问题在于会议附件不在持续的上下文中,但我仍然不确定如何解决这个问题

目前的解决方案对我有效,但我不认为它是理想的

public void updateMeeting(Meeting meet){
    Meeting m=em.find(Meeting.class, meet.getMeetingsid());

    m.setDate(meet.getDate());
    m.setDescription(meet.getDescription());
    List<Meetingsattachment> mal = m.getMeetingsattachments();
    for(Meetingsattachment ma:meet.getMeetingsattachments()){
        if(!mal.contains(ma)){
            m.addMeetingsattachment(ma);
        }
    }
    //m.setMeetingsattachments(meet.getMeetingsattachments());

    em.flush();


}
公共作废更新会议(会议){
Meeting m=em.find(Meeting.class,meet.getMeetingsid());
m、 setDate(meet.getDate());
m、 setDescription(meet.getDescription());
List mal=m.getMeetingsattachments();
for(Meetingsattachment ma:meet.getMeetingsattachments()){
如果(!mal.contains(ma)){
m、 添加会议附件(ma);
}
}
//m、 setMeetingsattachments(meet.getMeetingsattachments());
em.flush();
}

不幸的是,这是一个胡乱猜测,但您确定Meetingsattachment类中的equals方法吗?看起来它总是返回false=>jpa找不到附件,所以它会创建一个新附件

编辑:一些代码

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (!(obj instanceof Meetingattachment))
        return false;
    String other = (Meetingattachment) obj.getAttachment;
    if (!other.equals(attachment))
        return false; 
    return true;
}}

我认为原因是你有两个不同的
Meeting
类实例:
Meeting
m

Meeting m=em.find(Meeting.class, meet.getMeetingsid());
m
加载到持久性上下文中。因为关系是一对多,附件将被延迟加载。因此,它们不在持久性上下文中。现在,当您在中分配附件列表时

m.setMeetingsattachments(meet.getMeetingsattachments());
它们将被视为新条目,并将被持久化。 这应该是你得到重复的原因

因此,如果
updateMeeting()
方法按如下方式实现,则问题应该得到解决:

public void updateMeeting(Meeting meet){
    em.merge(meet);
    em.flush();
}
需要另一个引用(如
m
)的唯一情况是,如果要在
updateMeeting()
方法中更改托管实体的状态,例如:

public void updateMeeting(Meeting meet){
    Meeting m = em.merge(meet);
    m.setDate(<some_date>);
    em.flush();
}
公共作废更新会议(会议){
会议m=em.merge(会议);
m、 设置日期();
em.flush();
}
原因是
meet
将不是托管实例。持久性提供程序将在持久性上下文中创建一个新实例,并将
meet
的状态复制到该实例中,然后在刷新
em
或提交事务时将其保存到数据库中。因此,如果不使用从
merge()
方法返回的实例,更改(在本例中是日期)将不会保存到数据库中

  • Hibernate通过对象的ID标识对象
  • Hibernate将在数据库中插入新记录,仅用于临时实体实例
  • 这意味着
    Meetingsattachment
    中包含的
    meeting
    没有正确的ID(都是零,这似乎是基本类型的默认值),因此Hibernate认为它们是暂时的(新的)


    因此,解决方案是提供适当的ID并处理分离的对象,或者手动合并集合,就像您已经在解决方案中所做的那样

    你是对的,equals方法是错误的,但不幸的是,修复它并没有解决问题,同样的事情还在发生。我编写了equals方法,用于我为排除故障而删除的文件上载代码,以确保我只在meetingsattachment列表中插入唯一值。如果您注释掉
    m.setMeetingsattachments(meet.getMeetingsattachments())发生了什么?我很确定你是对的,但我不知道如何解决这个问题,我能想到的唯一解决方案是一个非常肮脏的解决方案,我在meet和m中循环使用附件,然后删除重复的附件,然后执行m.setMeetingsattachments(meet.getMeetingsattachments());就我在上面的代码中所见,在
    updateMeeting()
    方法中没有您正在做的事情。因此,您只需要方法中的以下行,其余部分应按预期工作:
    public void updateMeeting(Meeting-Meeting){em.merge(Meeting);}
    。原因是,您的实体具有数据库标识,使得持久性提供者将其视为一个分离的实体并将其合并。它工作了,谢谢,我到处读过,除非使用大容量操作,否则不应该使用合并。请在答案中加入合并内容,我会将其标记为已接受。谢谢again@yahh:修改了答案。
    public void updateMeeting(Meeting meet){
        em.merge(meet);
        em.flush();
    }
    
    public void updateMeeting(Meeting meet){
        Meeting m = em.merge(meet);
        m.setDate(<some_date>);
        em.flush();
    }