EJB Singleton和JPA乐观锁定抛出OptimisticLockException

时间:2016-12-09 08:26:40

标签: hibernate jpa java-ee singleton optimistic-locking

我遇到与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();
    ....
}

1 个答案:

答案 0 :(得分:1)

您收到此效果是因为您未在@ TransactionAttribute(REQUIRES_NEW)方法上指定calculateConnoteNumber

此方法由多个客户端调用,我希望它们也是EJB。调用链中的第一个EJB将启动事务,然后尝试提交事务,所有这些都在并发控制之外。