Я хочу сохранить список объектов JPA в базе данных MySQL, используя JTA (транзакции, управляемые контейнером) с EclipseLink на сервере приложений Glassfish 3.1.2.
Список поступает по JMS одно за другим как сообщения, каждый раз, когда приходит сообщение, используя фасад без состояния, я смотрю, существует ли эта сущность, если да, то я вызываю с того же фасада обновление, в противном случае - метод вставки.
Однако после вставки одной сущности, если я получаю то же самое из сообщения, метод exists не извлекает вновь вставленную, и поэтому фасад пытается вставить ее снова с результирующим исключением дублированного ключа.
Как я могу сказать EntityManager (или кому-то еще :-)), что вновь вставленные объекты должны быть доступны немедленно?
Любая помощь приветствуется ...
МОЙ ЛИЦ
пакет 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 ")}) открытый класс Eom реализует сериализуемый {частный статический окончательный длинный serialVersionUID = 1L; private String имя проекта; частные дефекты списка;
public Eom() {
}
@Id
@Column(unique=true, nullable=false, length=150)
public String getProjectname() {
return this.projectname;
}
public void setProjectname(String projectname) {
this.projectname = projectname;
}
Мой DAO
пакет clearquest.crud.domain; import java.io.Serializable;
import javax.ejb.Lock; import javax.ejb.LockType; import javax.persistence. *;
import java.util.List;
@Stateless @Singleton публичный класс EomDao реализует 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;
}
}
}
И ФАСАД
пакет clearquest.crud.domain; import java.io.Serializable;
import javax.ejb.Lock; import javax.ejb.LockType; import javax.persistence. *;
import java.util.List;
@Stateless @Singleton общедоступный класс DefectListFacade реализует 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);
}
И ЭТО МОЕ СООБЩЕНИЕ
@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());
}
}
Сделав то, что сказал Крис, я узнал, что происходит. Это своего рода состояние гонки.
[# | 2014-03-27T11: 22: 39.051 + 0100 | ИНФОРМАЦИЯ | glassfish3.1.2 | com.generali.tools.myemi.components.clearquest.crud.facade.DefectListFacade | _ThreadID = 2605; _ThreadName = Thread-10; | EOM - ВСТАВЛЕННЫЙ PRJ-00359 | #]
[# | 2014-03-27T11: 22: 39.051 + 0100 | ИНФОРМАЦИЯ | glassfish3.1.2 | com.generali.tools.myemi.components.clearquest.crud.facade.DefectListFacade | _ThreadID = 2603; _ThreadName = Thread-10; | EOM - ВСТАВЛЕННЫЙ PRJ-00359 | #]
[# | 2014-03-27T11: 22: 39.051 + 0100 | ПРЕДУПРЕЖДЕНИЕ | glassfish3.1.2 | javax.enterprise.system.core.transaction.com.sun.jts.jta | _ThreadID = 2605; _ThreadName = Thread-10; | JTS5054: Непредвиденная ошибка произошла после завершения
Стек локальных исключений: Исключение [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException Внутреннее исключение: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityationConceptionVision «PRJ-00359» для ключа «PRIMARY». Код ошибки: 1062. Вызов: INSERT INTO eom (PROJECTNAME) VALUES (?) Bind => [1 параметр bound]
Есть 2 сообщения JMS, которые идут рядом друг с другом с одинаковым именем EOM (первичным ключом), и номер потока 2605 сначала вставляет EOM. Но есть еще один поток с номером 2603, который также вставляет тот же первичный ключ сразу после первой вставки. Во всяком случае, второй поток не знает о первой вставке.
Теперь возникает вопрос, как я могу заблокировать, чтобы вторая вставка ожидала завершения первой?