一尘不染

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

hibernate

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

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

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

如您所见,我故意在增加20秒的睡眠.save()时间之后才能够测试并发方案。

初始标签计数器= 10;

1)用户调用crementTag,代码进入hibernate状态,因此实体的值= 11,而数据库中的值仍为10

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

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

当我检查数据库时,该标记的值现在等于11。实际上(至少我想实现的目标)等于10。

这是正常现象吗?还是@Transactional注释不做是工作?


阅读 300

收藏
2020-06-20

共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)修改同一行。

2020-06-20