A mapped @Version property detects competing writes to the same node when both use the versioned mapping path.
Spring Data Neo4j version conflicts: reject a stale graph update
A last write can erase a decision
Two dispatchers load shipment 47 at the same version. One marks it held; the other assigns it for departure. A mapped @Version property lets a stale save fail instead of replacing the newer state. That does not resolve the business conflict. Reload the node, apply the current policy, and ask the caller to retry if the decision changed.
Keep other writers honest
A custom Cypher SET that ignores the version can bypass a mapped version check. Either route all competing writes through the versioned mapping path or include a version predicate and increment in custom queries. Relationship writes also need careful aggregate ownership; a node version is not a global lock over every related node.
Run a two-writer test
Load two copies, commit one, then require the second save to fail. Repeat with each custom query used by the service. Assert the winning status and version remain unchanged after the rejected write.
Implementation sketch
@Node("Shipment")
class Shipment {
@Id String shipmentId;
String tenantId;
String status;
@Version Long version;
}Cost and verification
Version checks add conflict handling and retries. Hot shipment nodes may need a command queue or narrower state transitions instead of unlimited retry loops.
Common Mistakes
- Do not auto-retry a stale decision without re-evaluating its preconditions.
- Do not bypass version checks in a custom Cypher update.
- Do not assume one node version protects all related nodes.
Read next
Spring Data Neo4j relationships: save only the graph you own, Spring JPA optimistic locking: reject a stale stock update, Spring Data MongoDB optimistic locking: save the version you read.
Related boundary
Spring Data Neo4j tenant lookup: constrain before traversing
