我遇到与EJB单例和数据库锁定有关的问题。
以下类表示寄售编号的实体。 每个寄售编号必须是唯一的。请注意,这个 class已通过定义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;
}
}
送货公司提供一套数千份寄售编号 将被保存到数据库中。
创建新货运单时,此集合中的下一个“未消耗”寄售编号 应该检索并发送给请求服务。
这种情况下的服务是EJB单例,它提供服务方法 “calculateConnoteNumber()”。由于ejb类使用“@Lock(LockType.WRITE)”进行注释 应该防止对此方法的并发访问。检索下一个未消耗的 实体,“消费”标志将被标记为真,并且将设置消费日期。 最后,将合并更新的实体,并返回connote的值。
然而,在负载测试期间,我们遇到了“OptimisticLockExceptions”(平均值为1000的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();
....
}
答案 0 :(得分:1)
您收到此效果是因为您未在@ TransactionAttribute(REQUIRES_NEW)
方法上指定calculateConnoteNumber
。
此方法由多个客户端调用,我希望它们也是EJB。调用链中的第一个EJB将启动事务,然后尝试提交事务,所有这些都在并发控制之外。