2016-12-09 9 views
1

EJBシングルトンとデータベースのロックに関連する問題があります。EJB SingletonとJPAオプティミスティック・ロック・スローOptimisticLockException

以下のクラスは、委託番号のエンティティを表します。 各委託番号は一意でなければなりません。この クラスは、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; 
    }  
} 

配送業者は がデータベースに永続化される何千もの委託番号のセットを提供します。

新しい貨物発注を作成する場合は、このセット の次の「使用されていない」貨物番号を取得し、要求サービスに配信する必要があります。

この場合のサービスは、サービスメソッド 「calculateConnoteNumber()」を提供するEJBシングルトンです。 ejbクラスに "@Lock(LockType.WRITE)"と注釈が付けられているので、このメソッドへの同時アクセスは防止する必要があります。 消費されていない次の エンティティの検索時に、フラグ "consumed"はtrueにフラグが立てられ、消費日が設定されます。 最後に、更新されたエンティティがマージされ、connoteの値が返されます。

しかし、負荷テスト中に、新しい注文を作成するときに「OptimisticLockExceptions」(1000から5) が発生しました。

私には分かりませんが、これはビジネスメソッドへのアクセスとしてどのように可能であり、 によってトランザクションが順次行われるべきです。

query.setLockMode(LockModeType.PESSIMISTIC_WRITE); 

作品を設定することにより、永続層のロックの種類を変更します。

私の考えでは、これは必要ではなく、ビジネスメソッドへの同時アクセスが防止されている限り、楽観的なロックで十分です。 私はここに何かを忘れましたか?

環境が使用:WildFly 8.2.0は、MySQL 5.7、XA-データソース

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

これはシングルノードシステムですか? –

+0

はい、単一ノードシステムです。 – chrisF

+0

'calculateConnoteNumber'メソッドを呼び出すコードを投稿できますか... EJB Singleton Definitionをどのように呼び出す(または注入する)のですか? –

答えて

1

あなたcalculateConnoteNumber方法で@ TransactionAttribute(REQUIRES_NEW)を指定していないため、この効果を得ています。

このメソッドは、EJBであると予想される複数のクライアントから呼び出されています。コール・チェーン内の最初のEJBはトランザクションを開始し、その後、トランザクションをコミットしようとします。これらはすべて並行性制御の外にあります。

+0

SteveC、あなたの答えに感謝します。申し訳ありませんが、質問を投稿するときに間違いました。ビジネスメソッドは実際に@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)で注釈が付けられていましたが、悲観的なロックに変更すると削除されました。質問のために戻して忘れました。(私の質問を更新しました)まだOptimisticLockingExceptions(平均5/1000)テストケースは私の質問で提供されます。 – chrisF

+0

代わりにWildFly 10.1を試してみる価値があります。あなたはサーバーの実装で良い古いバグに遭遇している可能性があります –

関連する問題