Reputation: 1195
i have the following meetings table
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;
}}
and i have this meetings attachment table which stores the location of the attachments
package ng.telecomroadmap.model;
import java.io.Serializable;
import javax.persistence.*;
/**
* The persistent class for the meetingsattachment database table.
*
*/
@Entity
@NamedQuery(name="Meetingsattachment.findAll", query="SELECT m FROM Meetingsattachment m")
public class Meetingsattachment implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int maid;
private String attachment;
//bi-directional many-to-one association to Meeting
@ManyToOne
@JoinColumn(name="meetingsid")
private Meeting meeting;
public Meetingsattachment() {
}
public int getMaid() {
return this.maid;
}
public void setMaid(int maid) {
this.maid = maid;
}
public String getAttachment() {
return this.attachment;
}
public void setAttachment(String attachment) {
this.attachment = attachment;
}
public Meeting getMeeting() {
return this.meeting;
}
public void setMeeting(Meeting meeting) {
this.meeting = meeting;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + attachment.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Meetingsattachment))
return false;
Meetingsattachment other = (Meetingsattachment) obj;
if (!(other.getAttachment()).equals(attachment))
return false;
return true;
}
}
i am calling this method to update
public String updateButton(){
List<Meetingsattachment> mal= meeting.getMeetingsattachments();
//fileuploading code removed from here for troubleshooting
mrm.updateMeeting(meeting);
return "meetings?faces-redirect=true";
}
inside my entity manager
public void updateMeeting(Meeting meet){
Meeting m=em.find(Meeting.class, meet.getMeetingsid());
m.setDate(meet.getDate());
m.setDescription(meet.getDescription());
m.setMeetingsattachments(meet.getMeetingsattachments());
em.flush();
}
now my problem is that when i update the entry of meeting it is duplicating the already inserted values in meeting attachment
example: lets say i have a meeting with id 27 and in meeting attachment i have the following
31 /path/to/files/t22-1700585698/1.pdf 27
now when i try to edit meeting 27 and call the updatebutton method without changing anything or attaching any new attachments in the meetings attachment table the following happens
31 /path/to/files/t22-1700585698/1.pdf 27
32 /path/to/files/t22-1700585698/1.pdf 27
i cant figure out what i am doing wrong that meeting attachments is getting added again into the database
from the answers below i have understood the problem is the meetingattachments not being in the persistant context but i am still not sure how to solve this
current solution that is working for me but i do not believe it is ideal
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();
}
Upvotes: 2
Views: 1390
Reputation: 3309
I think the reason is that you have two different instances of Meeting
class: meet
and m
.
Meeting m=em.find(Meeting.class, meet.getMeetingsid());
loads m
into the persistence context. Because the relationship is one-to-many
the attachments will be loaded lazily. And therefore they are not in the persistence context. Now when you assign the list of attachments in
m.setMeetingsattachments(meet.getMeetingsattachments());
they will be considered as new entries and will be persisted. That should be the reason why you are getting duplicates.
So the problem should be solved if the updateMeeting()
method is implemented as follows:
public void updateMeeting(Meeting meet){
em.merge(meet);
em.flush();
}
The only case where you need another reference like m
is if you want to change the state of the managed entity in the updateMeeting()
method, for example:
public void updateMeeting(Meeting meet){
Meeting m = em.merge(meet);
m.setDate(<some_date>);
em.flush();
}
The reason is that meet
will not be the managed instance. The persistence provider will create a new instance in the persistence context and copy the state of meet
to it, and then that is what will be saved to the database when the em
is flushed or the transaction commits. So if you don't use the returned instance from the merge()
method the change (in this case the date) will not be saved to the DB.
Upvotes: 1
Reputation: 23552
That means that Meetingsattachment
s contained in the meet
do not have the proper ids (all are zero, which seems to be the default unsaved-value for primitive types), thus Hibernate considers them transient (new).
So, the solution is to provide proper ids and handle detached objects, or merge the collection manually as you are already doing in your workaround.
Upvotes: 0
Reputation: 86
Unfortunately a wild guess, but are you sure about your equals method in your Meetingsattachment class? Looks like it will always return false => jpa won't find an attachment, so it will create a new one.
Edit: Some code
@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;
}}
Upvotes: 0