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

2022-09-01 05:48:10

我正在玩Spring和JPA / Hibernate,我对在表格中增加计数器的正确方法有点困惑。

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

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

<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);
    }
}

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

初始标记计数器 = 10;

1) 用户调用 riseTag,代码命中睡眠,因此实体的值 = 11,DB 中的值仍为 10

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

3) 睡眠完成并命中 .save,其中实体的计数为 11,然后单击 .save()

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

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


答案 1

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

增量就这么简单:

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


答案 2

例如,使用乐观锁定。这应该是解决问题的最简单解决方案。有关更多详细信息,请参阅 -> https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html


推荐