@ManyToMany with Hibernate and JPA
I have two tables that are bound by a ManyToMany relationship.
Table 1 is TimeSlot and has a collection of documents created in that time slot. Table 2 is Documents and has a collection of TimeSlots as a given document could be modified many times in different timeslots.
In this particular program for each document I find or create a document row for it. Further I find or create a time slot that represents the epoch of the action, create, update, delete. Note: delete is an event performed on the document, not on the row that represents it. In this program there are no deletes or removes performed against any row of either table.
However I am seeing as many deletes against the timeslot_document mapping table as there are inserts.
My question is this, why is Hibernate issuing deletes? It appears to be related to dirty update processing. By why repeated deletes from timeslot where id=1?
When I had OneToMany I didn't see the deletes but the model was inacc开发者_StackOverflow中文版urate then (as you might imagine).
For every document I determine the date (hour accuracy only) that the action occured on, increment a tally and add the document to the collection.
Something else curious is I implement MVCC via the @Version annotation yet in a 'show innodb status\G' I see the table locked. At this moment there is one row for timeslot, 1988 documents tallied in it and the version of the row is at 1999. The table locking is concerning me.
Here is the main code fragment:
for (Eventlog event : eventsList) {
currentEvent = event.getEventId();
String tmp = formatter.format(event.getEpoch());
Date epoch = null;
try {
epoch = formatter.parse(tmp);
} catch (ParseException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
}
cal.setTime(epoch);
Tuple tuple = holding.get(epoch);
if (tuple == null) {
tuple = new Tuple();
holding.put(epoch, tuple);
}
queryTimeSlot.setParameter("docType", docType);
queryTimeSlot.setParameter("date", epoch);
try {
ats = (TimeSlot)queryTimeSlot.getSingleResult();
insertATS = false;
} catch (Exception e) {
insertATS = true;
ats = new TimeSlot();
atsk = new TimeSlotKey();
atsk.setSlotDate(epoch);
atsk.setDocType(docType);
ats.setHour(cal.get(Calendar.HOUR_OF_DAY));
ats.setSlotKey(atsk);
}
if (processMM) {
queryDocument.setParameter("id", event.getUpdEventLogDocId());
queryDocument.setParameter("doc", docType);
try {
doc = (Document)queryDocument.getSingleResult();
insertDoc = false;
} catch (Exception e) {
doc = new Document();
docKey = new DocumentKey();
docKey.setDocid(event.getUpdEventLogDocId());
docKey.setType(docType);
doc.setDocKey(docKey);
insertDoc = true;
}
}
try {
ats.getDocuments().add(doc);
} catch (Exception e) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, "{0}", e);
System.exit(-1);
}
if (processMM) {
doc.getTimes().add(ats);
}
TimeSlot Entity
@Entity
@Table(name = "TimeSlot", catalog = "Analytics")
public class TimeSlot implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Basic(optional=false)
@Column(name="id")
private Integer id;
@Basic(optional = false)
@Column(name = "slotKey")
private TimeSlotKey slotKey;
@Basic(optional = false)
@Column(name = "slotHour")
private int slotHour;
@Basic(optional = false)
@Column(name = "inserts")
private int inserts;
@Basic(optional = false)
@Column(name = "deletes")
private int deletes;
@Basic(optional = false)
@Column(name = "active")
private int active;
@ManyToMany(fetch=FetchType.LAZY)
@JoinColumn(name="doc_id")
private Collection<Document> documents;
@Basic(optional = false)
@Version
@Column(name = "version")
private int version;
public TimeSlot() {
documents = new ArrayList<Document>(0);
}
public Collection<Document> getDocuments() {
return documents;
}
public void setDocuments(Collection<Document>documents) {
this.documents = documents;
}
public int getHour() {
return slotHour;
}
public void setHour(int slotHour) {
this.slotHour = slotHour;
}
@Override
public int hashCode() {
int hash = 0;
hash += (slotKey != null ? slotKey.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof ActivityTimeSlot)) {
return false;
}
}
Documents follows:
@Entity
@Table(name = "Documents", catalog = "Analytics")
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Basic(optional=false)
@Column(name="id")
private Integer id;
@Basic(optional = false)
@Column(name = "docKey")
private DocumentKey docKey;
@Basic(optional = false)
@Column(name = "inserts")
private int inserts;
@Basic(optional = false)
@Column(name = "deletes")
private int deletes;
@ManyToMany(fetch=FetchType.LAZY, mappedBy="")
@JoinColumn(name="slot_id")
private Collection<TimeSlot> times;
@Basic(optional = false)
@Version
@Column(name = "version")
private int version;
public Document() {
times = new ArrayList<TimeSlot>(0);
inserts = 0;
deletes = 0;
}
@Override
public int hashCode() {
return docKey.hashCode();
}
@Override
public boolean equals(Object object) {
return docKey.equals(object);
}
public int getDeletes() {
return deletes;
}
public void setDeletes(int deletes) {
this.deletes = deletes;
}
public void incrDeletes() {
deletes++;
}
public void incrInserts() {
inserts++;
}
public int getInserts() {
return inserts;
}
public void setInserts(int inserts) {
this.inserts = inserts;
}
public Pair getPair() {
return new Pair(inserts, deletes);
}
@Override
public String toString() {
return docKey.toString();
}
public Collection<ActivityTimeSlot> getTimes() {
return times;
}
public void setTimes(Collection<ActivityTimeSlot>times) {
this.times = times;
}
public DocumentKey getDocKey() {
return docKey;
}
public void setDocKey(DocumentKey docKey) {
this.docKey = docKey;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
New info
I happened across this ManyToMany assoicate delete join table entry
which is almost identical. It's just I don't understand Arthur's answer. Also I noticed in the mysql innodb status it is doing an outer left join...is that really necessary?
So in EntityA I should have a method add(EntityB) and in EntityB I should have a method called add(EntityA) in these methods I
public void add(EntityB entity) {
entity.getList().add(this);
getList().add(entity);
}
I am not seeing how this is functionally different than what I have.
精彩评论