Spring Data不更新最新数据

时间:2018-11-13 06:07:01

标签: java mysql sql spring spring-data-jpa

我的SQL表中有一个字段,需要更新一个字段并返回唯一ID。但是看起来它并没有根据最新数据进行更新,尤其是当我发出很多请求时。

@Transactional
public interface CompanyRepository extends CrudRepository<Company, Integer> {    
    @Modifying(clearAutomatically = true, flushAutomatically = true)
    @Query(value = "update Company c set c.accessId = :accessId WHERE c.id = :companyId AND c.accessId = :oldAccessId", nativeQuery = true)
    int updateAccessId(@Param("companyId") Integer companyId, @Param("accessId") Integer accessId, @Param("oldAccessId") Integer oldAccessId);
}

即使将clearAutomatically和flushAutomatically都设置为true,它也不会处理最新数据。

我可以看到两个更新查询都成功了,并且oldAccessId相同。

表格设计应该更改吗?

PS:我也尝试了不使用nativeQuery = true。

1 个答案:

答案 0 :(得分:1)

What you have here is a classical race condition.

Two threads read the same entity, with identical accessId, increment it by one and then writing the result using the method you show in your question. Resulting in effectively only one update.

There are various ways how to fix this.

  1. Use JPA and optimistic locking.

    Assuming you have an attribute with @Version annotated you can do the following in a single transactional method:

    1. Load the entity.
    2. increment the accessId.
    3. persist the entity.

    If another transaction tries to do the same on the same entity one of the two will get an exception. In that case retry until the update goes through.

  2. Use the database.

    Make reading and updating atomic in the database. Instead of passing the new value as parameter use a query like this one:

    update Company c 
    set c.accessId = c.accessId + 1 
    WHERE c.id = :companyId 
    
  3. Make it a version attribute.

    As mentioned above JPA already has @Version attributes which get updated on every change. Depending on the exact requirements you might be able to make accessId simply that attribute and get it updated automatically.

  4. Check if any rows got updated.

    Based on your comment your intention was to basically reimplement what JPA does with version attributes. If you do this you are missing a crucial part: checking that the update actually updated anything, by comparing the return value against 1.