J'ai un problème lié à un EJB singleton et à un verrouillage de base de données.EJB Singleton et JPA verrouillent de façon optimiste OptimisticLockException
La classe suivante représente une entité pour les numéros d'envoi. Il est obligatoire que chaque numéro d'envoi soit unique. Veuillez noter que cette classe a été préparée pour un verrouillage optimiste en définissant la propriété version.
@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;
}
}
Une société de livraison fournit un ensemble de plusieurs milliers de numéros d'expédition, qui sera persistait à la base de données.
Lors de la création d'une nouvelle commande de fret, le numéro d'envoi "non consommé" suivant de cet ensemble doit être récupéré et remis au service demandeur.
Le service dans ce cas est un singleton EJB, qui fournit la méthode de service "calculateConnoteNumber()". Comme la classe ejb est annotée avec "@Lock (LockType.WRITE)" l'accès simultané à cette méthode doit être évité. Lors de la récupération de l'entité suivante non consommée , le drapeau "consommé" sera marqué à vrai et la date de consommation sera fixée. Enfin, l'entité mise à jour sera fusionnée et la valeur de connote sera renvoyée. Cependant, lors des tests de charge, nous avons rencontré des "OptimisticLockExceptions" (5 sur 1000) lors de la création de nouvelles commandes. Ce que je ne comprends pas est, comment cela est possible que l'accès à la méthode de gestion et ainsi à la transaction devrait être séquentielle.
La modification du type de verrouillage sur la couche de persistance en définissant
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
travaux. À mon avis, cela ne devrait pas être nécessaire et un verrouillage optimiste devrait suffire tant que l'accès simultané à la méthode commerciale est empêché . Ai-je manqué quelque chose ici?
Environnement utilisé: 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);
}
}
}
Mise à jour Le service est consommé par un autre apatride, EJB locale "PDFCreatorFacade", qui injecte le singleton
@EJB
private ConnoteCalculatorFacade connoteCalculator;
...
puis d'appeler la méthode sur le singleton dans sa méthode métier (non annotée).
public Label createPdfDocument(Label label) throws OrderManagementException {
....
String connoteNumber = connoteCalculator.calculateConnoteNumber();
....
}
S'agit-il d'un système à noeud unique? –
Oui, il s'agit d'un système à un seul noeud. – chrisF
Pouvez-vous poster le code qui appelle la méthode 'calculateConnoteNumber' ... Comment appelez-vous (ou injectez-vous) votre définition EJB Singleton? –