@Transactional in this case is used to manage SQL transactions, it does not add any thread safety. Spring's Transaction Manager does nothing but ask the database to start a new transaction, so you need to consult your RDBMS documentation and read about its transaction semantics.
So, in the first example there will be a race condition, even if SELECT and UPDATE are part of the same transaction. There are two possible solutions to your problem:
1- Lock lines. Acquiring a lock on the row you are about to change will not allow any other SQL transaction to change its value.
2- Optimistic lock: Optimistic lock does not actually use locks. What you do is that you are using a value that, as you know, is sure to change whenever this line is updated. For example, you could re-write your update statement:
UPDATE foo SET votes =
If no rows are updated, this means that another process has already changed the value of this row, and you can either try again or throw an exception.
source share