With Spring Data REST, why is the @Version property becoming an ETag and not included in the representation?

No, there is not. The ETag is the HTTP equivalent to what’s expressed as @Value property in the backend. Spring Data REST turns all backend related properties that have a corresponding mechanism in the HTTP protocol into exactly those: ids become URIs (and shouldn’t be part of the payload either), @LastModifiedDate properties become headers, @Version properties, become ETags.

The reason is pretty simple: if you use HTTP, use the protocol means that are available to you to achieve things that are implemented on the data access level. That’s one aspect in which Spring Data REST is not simply exposing a database to the web but actually inspects your model and translates model characteristics into protocol specific means.

Long story short: with Spring Data REST you have two options for updates:

  1. Just PUT without an If-Match header — enforces overriding whatever is present on the server as the aggregate gets loaded, incoming data mapped onto it and it written back. You still get optimistic locking applied if another client changed the aggregate in the meantime (although an admittedly very short window). If that’s the case you’ll see a 409 Conflict.
  2. PUT with an If-Match header – Spring Data REST checks the ETag submitted against the current value of the version property of the aggregate and return a 412 Precondition Failed in case there’s a mismatch at that point. In that case clients can lookup the current state of the resource and decide how to proceed. They might just decide to override what’s on the server using PUT without an If-Match header.

Similar optimizations can made for GET requests:

  1. GET with If-None-Match (ETag) / If-Modified-Since (with Last-Modified header value) — You’ll see a 304 Not Modified in case the resource is still in the same state as before and you thus avoid spending bandwidth for the response.
  2. Plain GET will always return the representation.

Leave a Comment