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. 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. Use save mode INSERT_IF_ABSENT when the intended operation is to skip an existing row.

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.