У меня проблема, связанная с синглтоном EJB и блокировкой базы данных.
Следующий класс представляет собой сущность для номеров партий. Каждый номер партии товара обязательно должен быть уникальным. Обратите внимание, что этот класс был подготовлен для оптимистической блокировки путем определения свойства версии.
@Entity
@NamedQuery(name = Connote.FIND_NEXT, query = "SELECT c from Connote c WHERE c.consumed = false")
public class Connote implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public static final String FIND_NEXT = "Connotes.FindNext";
@Id
@Size(max = 15)
private String connote;
@Version
private Long version;
private Date insertedDate;
private boolean consumed;
private Date consumedDate;
public Connote() {
super();
}
public String getConnote() {
return connote;
}
public void setConnote(String connote) {
this.connote = connote;
}
public Date getInsertedDate() {
return insertedDate;
}
public void setInsertedDate(Date insertedDate) {
this.insertedDate = insertedDate;
}
public boolean isConsumed() {
return consumed;
}
public void setConsumed(boolean consumed) {
this.consumed = consumed;
}
public Date getConsumedDate() {
return consumedDate;
}
public void setConsumedDate(Date consumedDate) {
this.consumedDate = consumedDate;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
Компания-доставщик предоставляет набор из многих тысяч номеров грузовых отправлений, которые будут сохранены в базе данных.
При создании нового заказа на фрахт необходимо получить номер следующей «неизрасходованной» партии из этого набора и доставить ее запрашивающей службе.
В данном случае служба представляет собой синглтон EJB, который предоставляет метод службы «calculateConnoteNumber ()». Поскольку класс ejb снабжен аннотацией «@Lock (LockType.WRITE)», одновременный доступ к этому методу должен быть предотвращен. При извлечении следующего неиспользованного объекта флаг «потреблен» будет установлен в истинное значение и будет установлена дата потребления. Наконец, обновленная сущность будет объединена, и будет возвращено значение connote.
Однако во время нагрузочного тестирования мы столкнулись с "OptimisticLockExceptions" (в среднем 5 из 1000) при создании новых заказов.
Я не понимаю, как это возможно, поскольку доступ к бизнес-методу и, следовательно, к транзакции должен быть последовательным.
Изменение типа блокировки на уровне сохраняемости путем установки
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
работает.
На мой взгляд, в этом нет необходимости, и оптимистичной блокировки должно быть достаточно, если одновременный доступ к бизнес-методу запрещен. Я что-то здесь пропустил?
Используемая среда: WildFly 8.2.0, MySql 5.7, xa-datasource
/**
* Session Bean implementation class ConnoteCalculatorFacade
*/
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@TransactionManagement(TransactionManagementType.CONTAINER)
@Lock(LockType.WRITE)
public class ConnoteCalculatorFacade {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@PersistenceContext(unitName = "some_unit_name")
private EntityManager entityManager;
/**
* Default constructor.
*/
public ConnoteCalculatorFacade() {
if (logger.isDebugEnabled()) {
logger.debug("ConnoteCalculatorFacade instantiated!");
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public String calculateConnoteNumber() throws OrderManagementException {
if (logger.isInfoEnabled()) {
logger.info("Retrieving new RS connote!");
}
try {
Query query = this.entityManager.createNamedQuery(Connote.FIND_NEXT);
query.setMaxResults(1);
//query.setLockMode(LockModeType.PESSIMISTIC_WRITE); <-- works when using pessimistic_write
List<Connote> validConnoteList = query.getResultList();
if (validConnoteList.size() == 1) {
Connote connote = validConnoteList.get(0);
connote.setConsumed(true);
connote.setConsumedDate(new Date());
this.entityManager.merge(connote);
this.entityManager.flush();
return connote.getConnote();
} else {
throw new OrderManagementException(AddressLabelExceptionReason.NO_CONNOTES_AVAILABLE);
}
} catch (Exception e) {
logger.error("Error calculating connote number! ",e);
throw new OrderManagementException(AddressLabelExceptionReason.ERROR_ASSIGNING_CONNOTE);
}
}
}
Обновление. Служба используется другим локальным EJB без сохранения состояния "PDFCreatorFacade", который внедряет синглтон
@EJB
private ConnoteCalculatorFacade connoteCalculator;
...
а затем вызывает метод синглтона в его (не аннотированном) бизнес-методе.
public Label createPdfDocument(Label label) throws OrderManagementException {
....
String connoteNumber = connoteCalculator.calculateConnoteNumber();
....
}
calculateConnoteNumber
... Как вы вызываете (или вводите) свое определение синглтона EJB? - person Carlitos Way   schedule 09.12.2016