Skip to main content

Upsert Mask

A saved object's loaded shape determines which values are available. An UpsertMask further limits which of those properties may be inserted or updated during an upsert. For example, a complete incoming book can provide all insert values while allowing only its price to change on a conflict.

Select Updatable Properties​

sqlClient
.saveCommand(book)
.setUpsertMask(BookProps.PRICE)
.execute();

On insertion, normal loaded values are still available. On a conflict, the ordinary update set is the intersection of the loaded shape and the mask, excluding the conflict id/key. A mask does not load a missing value.

The mask also constrains targets of public save assignment expressions. Configuring a custom expression for a target excluded by the shape or mask is an error.

No Property Updates​

sqlClient
.saveCommand(book)
.forbidUpdate()
.execute();

forbidUpdate() selects no entity-property assignments for the upsert update branch. An explicitly empty array passed to the convenience setUpsertMask(...) overloads has the same meaning.

This does not turn the operation into INSERT_IF_ABSENT: an existing object can still be accepted and its associations saved. If no SQL update is needed, the object can be accepted with zero affected rows for its own table. The dialect may still emit a self-assignment to fetch an id, return a row, or preserve other save semantics. Such SQL can fire database triggers. An effective setUpdateWhere still decides whether the matched branch is accepted.

Neither acceptance nor an empty update mask guarantees a lock on the existing row. See save modes for the concurrency contract. Use INSERT_IF_ABSENT when a conflicting root object should be rejected.

Configure Both Branches​

Use an UpsertMask object when insert and update need different property sets:

UpsertMask<Book> mask = UpsertMask.of(Book.class)
.addInsertableProp(BookProps.NAME)
.addInsertableProp(BookProps.EDITION)
.addInsertableProp(BookProps.PRICE)
.addUpdatableProp(BookProps.PRICE);

sqlClient.saveCommand(book).setUpsertMask(mask).execute();

Each branch is unrestricted until a property list is specified for that branch. addInsertablePath and addUpdatablePath can select individual embedded members. Mask builder methods return a new mask, so keep their return value or chain them.

forbidInsert() starts an empty insert-property list; it does not disable the insert branch. Add the desired insertable properties after it. Conflict columns and framework-required columns are still supplied, and omitted columns use database defaults where available. A missing mandatory value without a default causes the normal database error.

Masks select assignments; they do not replace save modes, update conditions, or version mode.