即使受锁定保护,两个更新操作也会相互覆盖

时间:2016-12-17 08:34:50

标签: java spring transactions spring-data spring-data-jpa

我有一个非常强烈的并行更新操作,我试图避免重叠但没有成功。我想问一下是否有人知道为什么这可能会失败。

上下文:Spring Data,JPA实体,同一实体的2个字段通过不同的调用并行更新到同一操作。

以下是代码的一部分:

@Override @PreAuthorize("isAuthenticated()")
@Transactional(readOnly = true)
public void processTransaction(PurchaseTransaction newTransaction)
    throws UnableToDetermineCompanyForPurchaseException {
    try {
        ... one slow network i/o operation that takes time    


        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                ...
                // Forcing the locks
                Company company = companyRepository.findOneAndLockForWrite(company.getId());

                if (expirationDate.isAfter(company.getFeatureExpiration(transaction.getFeature()))) {
                    switch (transaction.getFeature()) {
                        case IMAGES: company.setImagesFeatureExpiration(expirationDate); break;
                        case EMPLOYEES: company.setEmployeesFeatureExpiration(expirationDate); break;
                        default:
                            throw new RuntimeException("Unexpected feature " + transaction.getFeature());
                    }
                    companyRepository.save(company);
                    ...
                }
                ...
            }
        }
    } catch (...) {
        ...
    }
}

具体问题是一个事务来到并更新一个字段(假设例如imagesFeatureExpiration)和紧接在(100ms之后)更新另一个字段的事务(假设例如employeesFeatureExpiration)但是也覆盖了所做的更改通过上一个事务(即覆盖imagesFeatureExpiration保留前一个值)。

目前我转而尝试避免此问题直接更新:

@Modifying
@Query("UPDATE Company c SET c.imagesFeatureExpiration = :expiration WHERE c.id = :id")
void setImagesFeatureExpirationById(@Param("id") long id, @Param("expiration") LocalDateTime expiration);

但仍在考虑为什么会在事务性的实体锁定上下文中发生这种情况,因为其中一个读取应该被另一个读取锁定,以防止这种情况发生。

1 个答案:

答案 0 :(得分:0)

我的不好,我在ISOLATION_SERIALIZABLE时设置了ISOLATION_REPEATABLE_READ