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.
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
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);
}
}
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.
initial tag counter = 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) a user calls the decreaseTag and goes through all the code. the value is the database is now = 9
3) The sleeps finishes and hits the .save with the entity having a count of 11 and then hits .save()
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
Is this behaviour normal? Or the @Transactional
annotation is not doing is work?