How It Works: ETags
Optimistic locking on the web is implemented using ETags, a standard HTTP mechanism.
When a record is retrieved, the server includes an ETag in the response. This acts as a version identifier that changes whenever the record is updated.
When sending an update, the client includes this ETag in the If-Match header, effectively stating: apply this update only if the record is still on the same version I read.
The server then compares the provided ETag with the current version:
- Match → The record is unchanged, the update is applied, and a new ETag is generated
- Mismatch → The record has been modified by someone else, and the request is rejected with 412 Precondition Failed
This ensures updates are applied only to the latest version of the data, preventing silent overwrites.
How SAP CAP Java Does It
SAP CAP Java handles all of this automatically. We just need to tell the framework which field to use as the ETag.
We can do that with a single annotation in our CDS model:
The managed aspect automatically maintains the modifiedAt timestamp, updating it whenever the record is changed. By annotating this field with @odata.etag, SAP CAP uses it as the entity’s version identifier (ETag).
From that point onward, concurrency control is handled automatically. For every update request, SAP CAP validates the incoming If-Match header against the current value of modifiedAt. If the values differ, the framework rejects the update with an HTTP 412 Precondition Failed response, preventing stale data from overwriting newer changes.
No additional Java code is required.
Seeing It in Action
Let us start the application and create an employee record:
As we can see the response contains the ETag value: 2026-05-25T06:55:44.400909Z
Let us simulate Alice’s request of updating the email with the ETag we just received:
This succeeds. The record is updated and a new ETag is generated.
Now, simulating Bob's request (who is working with the older version) using the older ETag value.
Bob's request is rejected with 412 Precondition Failed.
Alice's change is safe.
What Happens Next: Resolving the Conflict
A 412 Precondition Failed response is not an error to suppress – it is a signal that the data has changed since it was last read.
At this point, the user can reload the latest version of the data, review the differences, and reapply their changes with full context before saving again.
Hence, Bob retrieves the latest version of the record and obtains the updated ETag value.
Bob resubmits his update using the fresh ETag: 2026-05-25T07:07:15.691711Z
This time it succeeds. Both changes are now in the record. Nothing was lost.
The key insight is that a 412 is not a failure – it is a safety net doing its job. The system caught a potential lost update before it happened and gave Bob the opportunity to resolve it cleanly.
Conclusion
Concurrency-related data integrity issues are particularly problematic because they occur silently. When two users update the same record, the later save succeeds and the earlier change is overwritten without any indication of a conflict.
Optimistic locking is the standard approach to preventing this class of issue, and SAP CAP Java provides built-in support. By adding a simple @odata.etag annotation, the framework can automatically detect stale updates and reject conflicting changes before any data is lost.
It is a minimal change to the data model, but one that significantly improves the consistency and reliability of enterprise applications.



