I am using Hibernate as the JPA provider in a NetBeans 6.9.1 application. I don't know what hibernate ships with 6.9.1.
I have 2 entities, slots and documents where in a given time slot (1 hour typically) there will be multiple documents processed. Furthermore, given a document it may appear in multiple time slots.
What I am seeing are periodic deletes against the join table. In no case am I instigating a delete or remove. Since the join table is dynamically created the rows do not have a version for MVCC and the delete forces a table lock. This, in turn, slows the other threads down and occasionally triggers a 1205, timeout waiting for lock. Consequently, there are 1 1/2 problems here, performing the delete at all is one, dying while waiting for the table lock is another. The underlying db is mysql with the innodb engine (5.1).
Code:
package analytics.models;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;
/**
*
* @author walt
*/
@Entity
@Table(name = "Documents", catalog = "Analytics")
@NamedQueries({
@NamedQuery(name = "Document.findAll", query = "SELECT a FROM Document a"),
@NamedQuery(name = "Document.findById", query = "SELECT a FROM Document a WHERE a.docKey.docid = :id"),
@NamedQuery(name = "Document.findByIdDocType", query = "SELECT a FROM Document a WHERE a.docKey.docid = :id and a.docKey.type = :doc")})
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)
@JoinTable(name = "slot_doc",
joinColumns = {
@JoinColumn(name="doc_id")
},
inverseJoinColumns = {
@JoinColumn(name="slot_id")
}
)
private Collection<ActivityTimeSlot> times;
@Basic(optional = false)
@Version
@Column(name = "version")
private int version;
public Document() {
times = new ArrayList<ActivityTimeSlot>(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;
}
public void addSlot(ActivityTimeSlot slot) {
slot.getDocuments().add(this);
times.add(slot);
}
}
Code:
package analytics.models;
import java.io.Serializable;
import java.util.ArrayList;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Collection;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
/**
*
* @author walt
*/
@Entity
@Table(name = "ActivityTimeSlot", catalog = "Analytics")
@NamedQueries({
@NamedQuery(name = "ATS.findAll", query = "SELECT a FROM ActivityTimeSlot a"),
@NamedQuery(name = "ATS.findById", query = "SELECT a FROM ActivityTimeSlot a WHERE a.slotKey.slotDate = :date"),
@NamedQuery(name = "ATS.findMaxDateByEngineDocCnty", query = "SELECT max(a.slotKey.slotDate) FROM ActivityTimeSlot a where a.slotKey.engine= :engine and a.slotKey.docType = :docType and a.slotKey.language = :country"),
@NamedQuery(name = "ATS.findByDateEngineLangDocType", query = "SELECT a FROM ActivityTimeSlot a WHERE a.slotKey.slotDate = :date and a.slotKey.engine = :engine and a.slotKey.language = :country and a.slotKey.docType = :docType")})
public class ActivityTimeSlot 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 ActivityTimeSlotKey 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, mappedBy="times")
@JoinTable(name = "slot_doc",
joinColumns = {
@JoinColumn(name="slot_id")
},
inverseJoinColumns = {
@JoinColumn(name="doc_id")
}
)
private Collection<Document> documents;
@Basic(optional = false)
@Version
@Column(name = "version")
private int version;
public ActivityTimeSlot() {
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;
}
ActivityTimeSlot other = (ActivityTimeSlot) object;
if ((this.slotKey == null && other.slotKey != null) || (this.slotKey != null && !this.slotKey.equals(other.slotKey))) {
return false;
}
return true;
}
public int getDeletes() {
return deletes;
}
public void setDeletes(int deletes) {
this.deletes = deletes;
}
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 slotKey.toString();
}
public int getActive() {
return active;
}
public void setActive(int active) {
this.active = active;
}
public void incrInserts() {
inserts++;
}
public void incrDeletes() {
deletes++;
}
public int getSlotHour() {
return slotHour;
}
public void setSlotHour(int slotHour) {
this.slotHour = slotHour;
}
public ActivityTimeSlotKey getSlotKey() {
return slotKey;
}
public void setSlotKey(ActivityTimeSlotKey slotKey) {
this.slotKey = slotKey;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void addDocument(Document doc) {
if (doc != null) {
doc.getTimes().add(this);
documents.add(doc);
}
}
}
The salient section of code that populates the tables
Code:
temComposites.getTransaction().begin();
for (Updeventlog event : eventsList) {
currentEvent = event.getUpdEventLogEventId();
String tmp = formatter.format(event.getUpdEventLogEpoch());
Date epoch = null;
try {
epoch = formatter.parse(tmp);
} catch (ParseException ex) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, null, ex);
}
queryTimeSlot.setParameter("date", epoch);
cal.setTime(epoch);
Tuple tuple = holding.get(epoch);
if (tuple == null) {
tuple = new Tuple();
holding.put(epoch, tuple);
}
/*
* this instance won't change that frequently so it is more
* efficient to check to see if the current instance in this
* LUW and hour is correct
*/
if (!(ats != null && ats.getSlotKey().getSlotDate().getTime() == epoch.getTime())) {
try {
ats = (ActivityTimeSlot) queryTimeSlot.getSingleResult();
insertATS = false;
} catch (Exception e) {
insertATS = true;
ats = new ActivityTimeSlot();
atsk = new ActivityTimeSlotKey();
atsk.setSlotDate(epoch);
atsk.setLanguage(language);
atsk.setDocType(docType);
atsk.setEngine(engine);
ats.setHour(cal.get(Calendar.HOUR_OF_DAY));
ats.setSlotKey(atsk);
}
}
if (processMM) {
queryDocument.setParameter("id", event.getUpdEventLogDocId());
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.addDocument(doc);
} catch (Exception e) {
Logger.getLogger(Converter.class.getName()).log(Level.SEVERE, "{0}", e);
System.exit(-1);
}
if (event.getUpdEventLogMisc().indexOf("=D") != -1) {
if (processMM) {
doc.incrDeletes();
}
if (interimMap.containsKey(event.getUpdEventLogDocId()) == true) {
tuple.incrDeletes();
ats.incrDeletes();
interimMap.remove(event.getUpdEventLogDocId());
}
} else {
if (processMM) {
doc.incrInserts();
}
if (interimMap.containsKey(event.getUpdEventLogDocId()) == false) {
Inter interim = new Inter();
interim.date = epoch;
interim.docid = event.getUpdEventLogDocId();
interimMap.put(interim.docid, interim);
ats.incrInserts();
tuple.incrInserts();
}
}
if (insertATS) {
try {
temComposites.persist(ats);
} catch (PersistenceException e) {
ActivityTimeSlot merge = temComposites.merge(ats);
}
}
if (processMM && insertDoc) {
try {
temComposites.persist(doc);
} catch (PersistenceException e) {
temComposites.merge(doc);
}
}
}
temComposites.getTransaction().commit();
I see no place there where I would be triggering a delete action. The goal of this code is to initially populate the tables/
If someone would explain what is triggering the delete on the join table I would appreciate it. My goal is to negate whatever reason Hibernate thinks the section of the join table must be removed. When I did catch the sql it was in the order of 'delete from slot_doc where slot_id = 1; Which means it is removing the entire first time slot.
Thanks.