Spring、JPA 和 Hibernate - 如何在没有并发问题的情况下增加计数器 [英] Spring, JPA, and Hibernate - how to increment a counter without concurrency issues

查看:15
本文介绍了Spring、JPA 和 Hibernate - 如何在没有并发问题的情况下增加计数器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Spring 和 JPA/Hibernate,但我对在表中增加计数器的正确方法有些困惑.

I'm playing around a bit with Spring and JPA/Hibernate and I'm a bit confused on the right way to increment a counter in a table.

我的 REST API 需要根据用户操作增加和减少数据库中的某些值(在下面的示例中,喜欢或不喜欢标签将使标签表中的计数器增加或减少 1)

My REST API needs to increment and decrement some value in the database depending on the user action (in the example bellow, liking or disliking a tag will make the counter increment or decrement by one in the Tag Table)

tagRepository 是一个 JpaRepository (Spring-data)我已经像这样配置了事务

tagRepository is a JpaRepository (Spring-data) and I have configured the transaction like this

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>

@Controller
public class TestController {

    @Autowired
    TagService tagService

    public void increaseTag() {
        tagService.increaseTagcount();
    }
    public void decreaseTag() {
        tagService.decreaseTagcount();

    }
}

@Transactional
@Service
public class TagServiceImpl implements TagService {


    public void decreaseTagcount() {
        Tag tag = tagRepository.findOne(tagId);
        decrement(tag)
    }

    public void increaseTagcount() {
        Tag tag = tagRepository.findOne(tagId);
        increment(tag)
    }

    private void increment(Tag tag) {
        tag.setCount(tag.getCount() + 1); 
        Thread.sleep(20000);
        tagRepository.save(tag);
    }

    private void decrement(Tag tag) {
        tag.setCount(tag.getCount() - 1); 
        tagRepository.save(tag);
    }
}

如您所见,我特意在 .save() 之前以增量方式休眠 20 秒,以便能够测试并发场景.

As you can see I have put on purpose a sleep of 20 second on increment JUST before the .save() to be able to test a concurrency scenario.

初始标签计数器 = 10;

initial tag counter = 10;

1) 用户调用 increaseTag 并且代码进入睡眠状态,因此值实体的 = 11 并且数据库中的值仍然是 10

1) A user calls increaseTag and the code hits the sleep so the value of the entity = 11 and the value in the DB is still 10

2) 用户调用reduceTag 并遍历所有代码.这值是数据库现在是 = 9

2) a user calls the decreaseTag and goes through all the code. the value is the database is now = 9

3) sleeps 完成并点击 .save 实体具有计数 11 然后点击 .save()

3) The sleeps finishes and hits the .save with the entity having a count of 11 and then hits .save()

当我检查数据库时,该标签的值现在等于 11.. 而在现实中(至少是我想要实现的)它将等于 10

When I check the database, the value for that tag is now equal to 11.. when in reality (at least what I would like to achieve) it would be equal to 10

这种行为正常吗?或者 @Transactional 注释没有做是工作?

Is this behaviour normal? Or the @Transactional annotation is not doing is work?

推荐答案

最简单的解决方案是将并发委托给你的数据库,简单地依赖当前修改行上的数据库隔离级别锁:

The simplest solution is to delegate the concurrency to your database and simply rely on the database isolation level lock on the currently modified rows:

增量就这么简单:

UPDATE Tag t set t.count = t.count + 1 WHERE t.id = :id;

递减查询是:

UPDATE Tag t set t.count = t.count - 1 WHERE t.id = :id;

UPDATE 查询对修改的行进行锁定,防止其他事务在当前事务提交之前修改同一行(只要您不使用 READ_UNCOMMITTED).

The UPDATE query takes a lock on the modified rows, preventing other transactions from modifying the same row before the current transaction commits (as long as you don't use READ_UNCOMMITTED).

这篇关于Spring、JPA 和 Hibernate - 如何在没有并发问题的情况下增加计数器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆