Hibernate:在选择实体后进行选择以进行更新

huangapple go评论71阅读模式
英文:

Hibernate: select for update after select entity

问题

Situation: 数据库存储URL和它们的点击次数信息。相应地,如果有大量负载(例如,同时有100个请求),存在竞态条件的可能性很大,实际上会记录一个不准确的“点击”数量。

解决方案如下:悲观锁定:

// 实体
@NamedQuery(
    name = "Link.findByIdForIncressClicks",
    query = "SELECT l FROM Link l WHERE l.id = :id",
    lockMode = LockModeType.PESSIMISTIC_WRITE
)

// Spring Data JPA 存储库
@Query(name = "Link.findByIdForIncressClicks")
Optional<Link> findByIdForIncressClicks(@Param("id") Long id);

// 服务层
@Override
@Transactional
public Link getLinkByShortCutURL(String shortUrl) {
    Long id = encoder.decode(shortUrl);
    // 在这里,我尝试使用本地的 entitymanager...
    // Link link = em.find(Link.class, id, PESSIMISTIC_WRITE);
    Link link = linkRepository.findByIdForIncressClicks(id).get();
    link.incressClicks();
    return linkRepository.save(link);
}

一切似乎都正常。READ_COMMITTED级别。但是错误的值仍然被写入数据库(通过JMeter进行的测试:40次点击,35次保存)。

Hibernate控制台中的查询:

Hibernate:
    select
        l1_0.id,
        l1_0.clicks_count,
        l1_0.created,
        l1_0.owner_id,
        l1_0.url
    from
        link_info.link l1_0
    where
        l1_0.id=?
Hibernate:
    select
        id
    from
        link_info.link
    where
        id=? for update
Hibernate:
    update
        link_info.link
    set
        clicks_count=?,
        created=?,
        owner_id=?
    where
        id=?

不知道,但Hibernate首先执行了“select entity”而不是在“id”上执行“select for update”。

如何修复这个问题?也许我在JPA中使用了错误的悲观锁?

更新:一些详细信息:Spring Boot v3.1(Hibernate - 6.2.1.Final)

英文:

Situation: the database stores URLs, and information on how many times they were clicked. Correspondingly, if there is a large load (for example, 100 requests at the same time), there is a great chance for a race condition, and we will, in fact, record a non-actual number of "clicks".
The solution was the following: pessimistic blocking:

// entity
@NamedQuery(
    name = &quot;Link.findByIdForIncressClicks&quot;,
    query = &quot;SELECT l FROM Link l WHERE l.id = :id&quot;,
    lockMode = LockModeType.PESSIMISTIC_WRITE
)

// spring data jpa repository
@Query(name = &quot;Link.findByIdForIncressClicks&quot;)
Optional&lt;Link&gt; findByIdForIncressClicks(@Param(&quot;id&quot;) Long id);

// service layer
@Override
@Transactional
public Link getLinkByShortCutURL(String shortUrl) {
    Long id = encoder.decode(shortUrl);
    // here i tried use native entitymanager...
    // Link link = em.find(Link.class, id, PESSIMISTIC_WRITE);
    Link link = linkRepository.findByIdForIncressClicks(id).get();
    link.incressClicks();
    return linkRepository.save(link);
}

Everything seems to be ok. READ_COMMITTED level. But the wrong value is still being written to the database (tests via JMeter: 40 clicks, 35 saved)
The select of hibernate in console:

Hibernate:
    select
        l1_0.id,
        l1_0.clicks_count,
        l1_0.created,
        l1_0.owner_id,
        l1_0.url
    from
        link_info.link l1_0
    where
        l1_0.id=?
Hibernate:
    select
        id
    from
        link_info.link
    where
        id=? for update
Hibernate:
    update
        link_info.link
    set
        clicks_count=?,
        created=?,
        owner_id=?
    where
        id=?

Idk, but hibernate doing first select entity,
instead of select for update on id.

How i can fix that? Maybe i have wrong using pessimistic locks in JPA?

UPD. some details: spring-boot v3.1 (hibernate - 6.2.1.Final)

答案1

得分: 1

通过更改 Hibernate 的版本来修复。我之前使用的是 6.2.1 版本,迁移到了 6.2.3 版本。
现在我有以下查询:

select
    l1_0.id,
    l1_0.clicks_count,
    l1_0.created,
    l1_0.owner_id,
    l1_0.url
from
    link_info.link l1_0
where
    l1_0.id=? for no key update
英文:

Fixed by changing version of hibernate. I had 6.2.1, migrated to 6.2.3.
And now i have query:

    select
        l1_0.id,
        l1_0.clicks_count,
        l1_0.created,
        l1_0.owner_id,
        l1_0.url
    from
        link_info.link l1_0
    where
        l1_0.id=? for no key update

huangapple
  • 本文由 发表于 2023年5月21日 19:20:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76299652.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定