I want to have an getOrCreateFoobar(id) method that either gets the already existing Foobar by id or creates/inserts a new Foobar with id.
This method would be called in a highly concurrent fashion, so I have to serialize/synchronize access on this atomic get-or-create so that not two concurrent requests create the Foobar with the same id twice.
Since the entity may not exist yet, there is no row yet on which I could lock to synchronize. additionally, a table-wide lock seems to be a bad idea either - potential recipe for deadlock-disaster(?)
There is this pattern to create artificial entities like a LockEntity which may have an property like Name. For each UseCase, I provide a row/instance like Name=GET_OR_CREATE_FOOBAR. Which is then used in a repository with a PESSIMISTIC_WRITE lock:
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT e FROM LockEntity e WHERE name = :name")
LockEntity obtainLock(@Param("name") String name);
which can then be used like:
obtainLock("GET_OR_CREATE_FOOBAR")
// now we hold the lock and are synchronized
get()
if (doesNotExistYet) create()
commit / release-lock
Is there a better / more lightweight way?