When you call
you get a Query with a LockOptions object that has lockMode = LockMode.NONE and a aliasSpecificLockModes-map with an entry "alias" -> LockMode.PESSIMISTIC_WRITE
When calling query.list() you get to the QueryLoader which calls dialect.applyLocksToSql(...)
The LockOptions-Object has still lockMode=LockMode.NONE and a aliasSpecificLockModes-map with the translated alias and -> LockMode.PESSIMISTIC_WRITE
As we use PostgreSQL dialect is an instance of PostgreSQLDialect.
So you get a ForUpdateFragement-object with the same LockOptions-object. The aliases-String contains the translated "alias".
In toFragmentString() getForUpdateString(String aliases, LockOptions lockOptions) is called because we have a LockOptions-object. The aliases are ignored in this case, as you can see in the comment:'by default we simply return the getForUpdateString() result since the default is to say no support for "FOR UPDATE OF ..."'
But the getForUpdateString(LockOptions lockOptions) is only looking at the the lockMode-Attribute of the LockOptions-object what has still the value "LockMode.NONE". It results in an empty String as the return value. But it should be " for update" because there's a LockMode.PESSIMISTIC_WRITE in the aliasSpecificLockModes which is ignored in getForUpdateString(LockOptions lockOptions)
Applied your pull request Bradley. Thanks!
Requesting to re-open this issue since the fix breaks use without aliases.
Tested with Hibernate 5.0.0.Final, PostgreSQL 9.4.4 and dialect PostgreSQL94Dialect, queries created through JPA and then unwrapped.
When calling query.setLockMode("this",LockMode.PESSIMISTIC_WRITE) the alias on the LockOptions is correctly set.
When QueryLoader.applyLocks is called the LockOptions get copied but the sqlAliasByEntityAlias is empty so no aliases are set on the copied LockOptions.
Later on ForUpdateFragement gets created with the copied LockOptions that have no aliases, therefore ForUpdateFragment.aliases is empty and when dialect.getForUpdateString( aliases.toString(), lockOptions ) gets called in #toFragmentString() the empty String is passed to #getForUpdateString(String,LockOptions).
I think the error could be in one of two places.
when the LockOptions gets copied
If the correct way to patch this is in ForUpdateFragment:
The patched PostgreSQL81Dialect doesn't distinguish between empty and non-empty aliases.
Therefore ForUpdateFragment#toFragmentString() needs to be changed to account for empty aliases even though lockOptions != null.
Adding to the previous comment:
The generated query looks like this and thus fails to run on PostgreSQL:
Noticed that the equivalent problem exists when running