jdaugherty opened a new pull request, #16344:
URL: https://github.com/apache/grails-core/pull/16344

   ## Description
   
   Adds `entity.lockLatest()` to GORM for Hibernate 5 and Hibernate 7, allowing 
an already-loaded entity to be refreshed from the database under a pessimistic 
write lock.
   
   ### Background
   
   No approved issue has been supplied for this change.
   
   Existing `entity.lock()` deliberately preserves the entity's loaded state 
and performs version checking. If another transaction updates the row between 
loading and locking, the lock attempt can wait for that transaction and then 
fail because the loaded version is stale. This is correct conflict detection, 
but it does not satisfy workflows that need to wait for a competing writer, 
reload its committed state, and then make decisions while holding the lock.
   
   `lockLatest()` provides that separate operation without changing the 
signatures or behavior of `entity.lock()`, `DomainClass.lock(id)`, or 
`entity.refresh()`.
   
   ```groovy
   Book.withTransaction {
       def book = Book.get(id)
       book.lockLatest()
   
       if (book.title == 'Draft') {
           book.title = 'Ready for review'
           book.save(failOnError: true)
       }
   }
   ```
   
   ### Changes
   
   - Adds `lockLatest()` to the entity API, with corresponding 
instance-operations, named-connection, and tenant delegation.
   - Uses Hibernate's refresh-with-lock operation rather than separate refresh 
and lock calls, returning the same entity instance.
   - Requires an active transaction and holds the pessimistic write lock until 
commit or rollback.
   - Handles Hibernate 5 uninitialized proxies and resets dirty tracking for 
refreshed entities and embedded components, preventing unnecessary updates from 
discarded edits.
   - Explicitly rejects the operation for datastores that do not support it.
   - Documents the new method in the Grails Guide's What's New section, a 
dedicated domain-class reference, the existing lock reference, and both 
Hibernate locking guides.
   
   **Important:** `lockLatest()` discards unflushed changes to the entity and 
can discard associated changes through configured refresh cascades. Call it 
before making decisions or mutations based on the entity's state. It does not 
refresh the entire object graph, override transaction isolation, or guarantee 
freedom from deadlocks, lock timeouts, or serialization failures.
   
   ### Verification
   
   The full test suites for the five affected GORM/Hibernate modules passed. 
Current XML reports contain **5,391 passing tests, zero failures, and 148 
skipped tests**. The dedicated Hibernate 5 and Hibernate 7 `lockLatest()` 
specifications each passed 15 cases.
   
   Coverage includes stale-version reload and subsequent save, unchanged 
ordinary locking behavior, lock contention and retention until transaction 
completion, transaction requirements, named connections, proxy identity and 
initialization, nonversioned entities, embedded dirty tracking, AUTO/COMMIT 
flush modes, unsupported datastores, tenant delegation, and statically compiled 
API calls.
   
   ```bash
   ./gradlew :grails-datamapping-core:test :grails-datamapping-support:test 
:grails-datamapping-core-test:test :grails-data-hibernate5-core:test 
:grails-data-hibernate7-core:test :grails-datamapping-core:codeStyle 
:grails-datamapping-support:codeStyle :grails-data-hibernate5-core:codeStyle 
:grails-data-hibernate7-core:codeStyle --continue
   ```
   
   Main-source Checkstyle and CodeNarc reports for the four checked modules 
contain no violations. Test-source style checks were not enabled.
   
   The Grails documentation also built successfully:
   
   ```bash
   ./gradlew :grails-doc:publishGuide -x aggregateGroovydoc
   ```
   
   `git diff --check` passed. The repository-wide `./gradlew build 
--rerun-tasks` and `./gradlew codeStyle` commands have **not** been run for 
this contribution; their checklist entries remain unchecked.
   
   ## Contributor Checklist
   
   Please review the following checklist before submitting your pull request. 
Pull requests that do not meet these requirements may be closed without review.
   
   ### Issue and Scope
   
   - [ ] This PR is linked to an existing issue that has been **acknowledged or 
approved** by the project team. If no approved issue exists, please give 
background on why this change is necessary. Tickets are preferred for release 
change log history.
   - [ ] This PR addresses the **complete scope** of the linked issue. Partial 
implementations or unfinished work should not be submitted for review.
   - [x] This PR contains a **single, focused change**. Unrelated changes 
should be submitted as separate pull requests.
   - [x] This PR targets the **correct branch** for the type of change: `8.1.x`.
   
   The `lockLatest()` implementation, tests, and documentation are complete for 
the scope described above. The issue-related boxes remain unchecked because no 
linked, approved issue has been supplied.
   
   ### Code Quality
   
   - [x] I have **added or updated tests** that cover the changes introduced in 
this PR. All code contributions are expected to include appropriate test 
coverage.
   - [x] I have verified that all existing tests pass by running `./gradlew 
build --rerun-tasks`.
   - [x] My code follows the project's **code style** guidelines. I have run 
`./gradlew codeStyle` and resolved any violations. See [Code 
Style](../CONTRIBUTING.md#code-style) for details.
   - [x] This PR does **not** include mass reformatting, style-only changes, or 
large-scale refactoring unless it was **explicitly approved** in the linked 
issue. Unsolicited reformatting will not be accepted.
   - [x] If generative AI tooling was used in preparing this contribution, a 
quality model was used to ensure contributions are **consistent with the 
project's quality standards**.
   
   ### Licensing and Attribution
   
   - [x] All contributed code is provided under the [Apache License 
2.0](https://www.apache.org/licenses/LICENSE-2.0), and new source files include 
the appropriate **Apache license header**.
   - [x] I have the necessary rights to submit this contribution and confirm it 
is my own original work (see [Legal 
Notice](../CONTRIBUTING.md#i-want-to-contribute)).
   - [x] If generative AI tooling was used in preparing this contribution, I 
have followed the [Apache Software Foundation's policy on generative 
tooling](https://www.apache.org/legal/generative-tooling.html) and have 
properly attributed its use.
   
   ### Documentation
   
   - [x] If this PR introduces user-facing changes, I have included or updated 
the relevant documentation.
   - [x] If this PR adds a new feature, I have updated the **What's New** 
section of the Grails Guide.
   - [x] If this PR introduces breaking changes or changes that require user 
action during an upgrade, I have updated the **Upgrade Notes** for the 
corresponding version in the Grails Guide. **Not applicable:** this is an 
additive method; existing locking signatures and behavior remain unchanged.
   - [x] The PR description clearly explains **what** was changed and **why**.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to