2016-12-09 1 views
1

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(); 
    .... 
} 
+0

S'agit-il d'un système à noeud unique? –

+0

Oui, il s'agit d'un système à un seul noeud. – chrisF

+0

Pouvez-vous poster le code qui appelle la méthode 'calculateConnoteNumber' ... Comment appelez-vous (ou injectez-vous) votre définition EJB Singleton? –

Répondre

1

vous obtenez cet effet parce que vous n'avez pas spécifié @ TransactionAttribute(REQUIRES_NEW) sur votre méthode calculateConnoteNumber.

Cette méthode est appelée par plusieurs clients qui, je l'espère, sont également des EJB. Le premier EJB de la chaîne d'appel lancera la transaction et tentera ensuite de valider la transaction, en dehors de vos contrôles de concurrence.

+0

SteveC, merci pour votre réponse. Désolé, j'ai fait une erreur en publiant la question. La méthode de gestion a bien été annotée avec @TransactionAttribute (TransactionAttributeType.REQUIRES_NEW), je l'ai supprimée lors du passage au verrouillage pessimiste et j'ai oublié de la remettre à la question. (Mise à jour ma question maintenant) Toujours avec OptimisticLockingExceptions (avg 5/1000) avec le cas de test fourni dans ma question. – chrisF

+0

Il serait intéressant d'essayer WildFly 10.1 à la place. Vous rencontrez peut-être un bon bug dans l'implémentation du serveur –