2016-12-09 5 views
1

Ich habe ein Problem, das sich auf eine EJB Singleton und Datenbanksperrung bezieht.EJB Singleton und JPA optimistische Sperrwürfe OptimisticLockException

Die folgende Klasse repräsentiert eine Entität für Sendungsnummern. Es ist zwingend erforderlich, dass jede Sendungsnummer eindeutig ist. Beachten Sie, dass diese Klasse für das optimistische Sperren vorbereitet wurde, indem Sie die Versionseigenschaft definieren.

@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; 
    }  
} 

Eine Lieferung Unternehmen bietet eine Reihe von vielen tausend Sendungsnummern, die werden in die Datenbank beibehalten werden.

Beim Erstellen eines neuen Frachtauftrags muss die nächste "unverbrauchte" Sendungsnummer aus diesem Satz abgerufen und an den anfordernden Dienst gesendet werden.

Der Dienst ist in diesem Fall ein EJB-Singleton, der die Servicemethode "calculateConnoteNumber()" bereitstellt. Da die ejb-Klasse mit "@Lock (LockType.WRITE)" annotiert ist, sollte der gleichzeitige Zugriff auf diese Methode verhindert werden. Beim Abruf der nächsten unverbrauchten Einheit wird das Flag "verbraucht" als wahr gekennzeichnet und das Verbrauchsdatum wird gesetzt. Schließlich wird die aktualisierte Entität zusammengeführt und der Wert von connote wird zurückgegeben.

Allerdings haben wir während des Belastungstests "OptimisticLockExceptions" (Durchschnitt 5 von 1000) beim Erstellen neuer Aufträge erlebt.

Was ich nicht verstehe ist, wie dies möglich ist, da der Zugriff auf die Geschäftsmethode und dabei auf die Transaktion sequentiell sein soll.

Änderung der Verriegelungsart auf Persistenz-Schicht durch

query.setLockMode(LockModeType.PESSIMISTIC_WRITE); 

Arbeiten einstellen.

Meiner Meinung nach sollte dies nicht notwendig sein und optimistisches Sperren sollte ausreichen, solange gleichzeitiger Zugriff auf die Geschäftsmethode verhindert wird. Habe ich hier etwas verpasst?

Umwelt verwendet: Wildfly 8.2.0, MySQL 5.7, xa-Datenquelle

/** 
* 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); 
     } 

    } 
} 

aktualisieren Der Dienst wird von einem anderen staatenlos, lokalen EJB "PDFCreatorFacade" verbraucht, die die Singleton spritzt

@EJB 
private ConnoteCalculatorFacade connoteCalculator; 
... 

und ruft dann die Methode auf dem Singleton in seiner (nicht kommentierten) Geschäftsmethode auf.

public Label createPdfDocument(Label label) throws OrderManagementException { 
    .... 
    String connoteNumber = connoteCalculator.calculateConnoteNumber(); 
    .... 
} 
+0

Ist dies ein Einzelknoten-System? –

+0

Ja, es ist ein Ein-Knoten-System. – chrisF

+0

Können Sie den Code posten, der die 'calculateConnoteNumber' Methode aufruft ... Wie rufen Sie Ihre EJB Singleton Definition auf? –

Antwort

1

Sie erhalten diesen Effekt, weil Sie nicht @ TransactionAttribute(REQUIRES_NEW) auf Ihrem calculateConnoteNumber Methode angegeben haben.

Diese Methode wird von mehreren Clients aufgerufen, von denen ich erwarte, dass sie auch EJBs sind. Der erste EJB in der Aufrufkette startet die Transaktion und versucht anschließend, die Transaktion zu übernehmen, und zwar alles außerhalb Ihrer Steuerelemente für den gemeinsamen Zugriff.

+0

SteveC, danke für deine Antwort. Entschuldigung, ich habe beim Posten der Frage einen Fehler gemacht. Die Geschäftsmethode wurde tatsächlich mit @TransactionAttribute (TransactionAttributeType.REQUIRES_NEW) versehen, ich entfernte sie, als ich in pessimistisches Sperren wechselte, und vergaß, sie für die Frage zurückzugeben (aktualisierte meine Frage jetzt) ​​Habe noch OptimisticLockingExceptions (durchschnittlich 5/1000) mit der Testfall in meiner Frage. – chrisF

+0

Es würde sich lohnen, stattdessen WildFly 10.1 zu versuchen. Möglicherweise haben Sie in der Serverimplementierung einen guten alten Fehler –

Verwandte Themen