You need to use PESSIMISTIC_WRITE during the request:
Query q = session .createQuery("from MyObject n where n.state = 'NEW'") .setLockOptions(new LockOptions(LockMode.PESSIMISTIC_WRITE)); List<MyObject> list = (List<MyObject>) q.list();
It is enough to lock the parent objects. Dead ends don't necessarily happen. You may receive a lock detection failure if the thread holding the lock does not release it before another thread waits.
Since you are using Oracle, this is how SELECT FOR UPDATE works:
SELECT ... FOR UPDATE blocks rows and any associated record index, just as if you issued an UPDATE statement for those rows. Other transactions are blocked from updating these rows, from executing SELECT ... LOCK IN SHARE MODE, or from reading data at specific transaction isolation levels. Consistent reads ignore any locks that are set on records that exist in read mode. (Older versions of a record cannot be locked; they are restored by applying pending logs to an in-memory copy of the record.)
So, if T1 has acquired an exclusive lock on some lines, T2 will not be able to read these records until T1 completes or rolls back. If T2 used the READ_UNCOMMITTED isolation level , then T2 will never block lock records, as it simply uses undo logs to restore data, as if it were when the request started. Unlike the SQL standard, Oracke READ_UNCOMMITTED will be:
To provide a consistent or correct answer, Oracle Database will create a copy of the block containing this row as it existed when the query started ... Effectively, Oracle Database goes around the changed data - it reads around it, restoring it from cancellation (also known as rollback). A consistent and correct response is returned without waiting for the transaction.
source share