I want to persist a list of JPA entities to a MySQL database using JTA (container managed transactions) with EclipseLink on a Glassfish 3.1.2 application server.
The list is coming over JMS one by one as Messages, everytime a message comes, using a stateless facade, I am looking if this entity exists, if yes, than I am calling from the same facade the update, otherwise the insert method.
However after the inserting one entity, if I get the same from the message, the exists method does not retrieve the newly inserted and therefore the facade tries to insert it again with a resulting duplicate key exception.
How can I tell the EntityManager (or someone else :-) ) that the newly inserted entities should be available immediately?
Any Help appreciated...
MY ENTITY
package clearquest.crud.domain; import java.io.Serializable;
import javax.ejb.Lock; import javax.ejb.LockType; import javax.persistence.*;
import java.util.List;
@Entity @Table(name="eom") @NamedQueries ({ @NamedQuery(name="Eom.findAll", query="SELECT e FROM Eom e"), @NamedQuery(name="getEomByProjectName", query="SELECT e FROM Eom e WHERE e.projectname = :projectname") }) public class Eom implements Serializable { private static final long serialVersionUID = 1L; private String projectname; private List defects;
public Eom() {
}
@Id
@Column(unique=true, nullable=false, length=150)
public String getProjectname() {
return this.projectname;
}
public void setProjectname(String projectname) {
this.projectname = projectname;
}
My DAO
package clearquest.crud.domain; import java.io.Serializable;
import javax.ejb.Lock; import javax.ejb.LockType; import javax.persistence.*;
import java.util.List;
@Stateless @Singleton public class EomDao implements EomDaoLocal {
private @PersistenceContext(unitName = "emiClearQuestAdapterPersistancy")
EntityManager em;
@Override
public void storeEom(Eom eom) {
em.persist(eom);
}
@Override
public void updateEom(Eom eom) {
em.merge(eom);
}
@Override
public void deleteEom(Eom eom) {
em.remove(eom);
}
@Override
public Eom getEomByProjectName(String projectName) throws NotFoundException {
em.getEntityManagerFactory().getCache().evictAll();
return em.find(Eom.class, projectName);
}
@Override
public boolean existsEom(String projectName) {
try {
if (this.getEomByProjectName(projectName) != null) {
return true;
} else {
return false;
}
}
catch (NotFoundException e) {
return false;
}
}
}
AND THE FACADE
package clearquest.crud.domain; import java.io.Serializable;
import javax.ejb.Lock; import javax.ejb.LockType; import javax.persistence.*;
import java.util.List;
@Stateless @Singleton public class DefectListFacade implements DefectListFacadeLocal {
private @EJB
DefectDaoLocal defectdao;
private @EJB
EomDaoLocal eomdao;
@Override
public void insertdefect(CQDefect defect) {
String projectName = defect.getFehlerprojektnummer().getLabel();
Eom eom = new Eom();
eom.setProjectname(projectName);
Defect domaindefect = new Defect();
List<Defect> defectlist = new ArrayList<Defect>();
transferJsonToDomain(domaindefect, defect);
domaindefect.setEom(eom);
defectlist.add(domaindefect);
eom.setDefects(defectlist);
eomdao.storeEom(eom);
Logger.getLogger(DefectListFacade.class.getName()).info(
"EOM -- INSERTED " + eom.getProjectname());
}
@Override
public void updatedefect(CQDefect defect) {
String projectName = defect.getFehlerprojektnummer().getLabel();
Logger.getLogger(DefectListFacade.class.getName()).info(
"EOM -- UPDATED " + projectName);
}
AND THIS MY MESSAGE
@Override
public void receiveMessage(BaseMessage message) {
if (message instanceof FehlerMessage) {
FehlerMessage fehlermsg = (FehlerMessage) message;
if (eomdao.existsEom(fehlermsg.getEom())) {
defectlistfacade.updatedefect(fehlermsg.getFehler());
} else {
defectlistfacade.insertdefect(fehlermsg.getFehler());
}
Logger.getLogger(FehlerMessageReceiver.class.getName()).fine(
fehlermsg.getEom() + " - "
+ fehlermsg.getFehler().getFehler_Titel());
monitoring.notifyEMDBMessageReceive(BusType.Base,
message.getTypeIdentifier(), message.getSize());
}
}
After doing what Chris told, I found out what is happening. This is a kind of Race Condition.
[#|2014-03-27T11:22:39.051+0100|INFO|glassfish3.1.2|com.generali.tools.myemi.components.clearquest.crud.facade.DefectListFacade|_ThreadID=2605;_ThreadName=Thread-10;|EOM -- INSERTED PRJ-00359|#]
[#|2014-03-27T11:22:39.051+0100|INFO|glassfish3.1.2|com.generali.tools.myemi.components.clearquest.crud.facade.DefectListFacade|_ThreadID=2603;_ThreadName=Thread-10;|EOM -- INSERTED PRJ-00359|#]
[#|2014-03-27T11:22:39.051+0100|WARNING|glassfish3.1.2|javax.enterprise.system.core.transaction.com.sun.jts.jta|_ThreadID=2605;_ThreadName=Thread-10;|JTS5054: Unexpected error occurred in after completion
Local Exception Stack: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'PRJ-00359' for key 'PRIMARY' Error Code: 1062 Call: INSERT INTO eom (PROJECTNAME) VALUES (?) bind => [1 parameter bound]
There are 2 JMS Messages which are coming next to each other with the same EOM Name (Primary Key) and the thread number 2605 inserts the EOM firstly. But there is another Thread with Number 2603 which also inserts the same Primary Key just after the first insert. Anyhow the second thread is not aware of the first insert.
Now is the question, how can I lock that the second insert waits for the first one completes?