Spring,JPA和Hibernate - 如何在没有并发问题的情况下增加计数器

Joh*_*y19 23 java spring hibernate jpa transactions

我正在玩Spring和JPA/Hibernate,我对在表中递增计数器的正确方法感到困惑.

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

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

<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);
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样.save(),在能够测试并发场景之前,我已经在增量JUST上进行了20秒的睡眠.

初始标签计数器= 10;

1)用户调用increaseTag并且代码命中睡眠,因此entity = 11的值和DB中的值仍为10

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

3)睡眠结束并使用计数为11的实体命中.save然后命中.save()

当我检查数据库时,该标签的值现在等于11 ..实际上(至少我希望实现)它将等于10

这种行为是否正常?或者@Transactional注释不做是有效的吗?

Vla*_*cea 52

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

增量就像这样简单:

UPDATE Tag t set t.count = t.count + 1 WHERE t.id = :id;
Run Code Online (Sandbox Code Playgroud)

并且减量查询是:

UPDATE Tag t set t.count = t.count - 1 WHERE t.id = :id;
Run Code Online (Sandbox Code Playgroud)

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

  • (顺便说一下,在发布我的问题之后,我正在寻找一些关于隔离级别的文档,而且我只是意识到这是你的博客),它非常清楚并且得到了很好的解释.所以也为此+1! (3认同)