These are hardest to detect and avoid in the case where business logic depends on no rows matching (count(*) == 0), eg. meeting room scheduling systems, because there is nothing for a transaction to lock!
Stored procedures can avoid this entirely, even under the weaker transaction isolation levels, by keeping all the logic in the DB and running it atomically. At Zipcar we took this approach to writing the web reservation system in the early 2000s. Later, it allowed us to easily add a telephone reservation system without duplicating business logic.
It worked great back then and it still works great, if you have good ways of managing the tradeoffs (version control, unit testing, etc).