Skip to main content

Assignment Expressions

By default, a save command updates each loaded property with the value from the saved object. For atomic operations such as incrementing a counter, the new column value must instead be calculated by the database.

Basic Usage

The following command adds the input price to the current database price:

Book book = Immutables.createBook(draft -> {
draft.setId(1L);
draft.setPrice(new BigDecimal("2.00"));
});

sqlClient
.getEntities()
.saveCommand(book)
.setMode(SaveMode.UPDATE_ONLY)
.set(
BookTable.class,
BookProps.PRICE,
(target, values) ->
target.price().plus(values.newNumber(BookProps.PRICE))
)
.execute();

It generates an assignment whose right-hand side is a SQL expression:

update BOOK
set PRICE = PRICE + ? /* 2.00 */
where ID = ? /* 1 */

target represents the existing database row. newNumber in Java, or newNonNull in Kotlin, represents a loaded value from the object being saved. For a nullable target, Kotlin uses setNullable; nullable saved values are referenced through newNullable:

setNullable(BookStore::website) {
newNullable(BookStore::website)
}
info

Java exposes newValue, newString, newNumber, and newComparable so the returned expression has the most useful static type for the SQL DSL.

Save Modes

Assignment expressions are supported by:

  • UPDATE_ONLY
  • The update branches of UPSERT and NON_IDEMPOTENT_UPSERT when the row already exists

They are not supported by INSERT_ONLY. An insert still takes its values from the saved object; set only customizes an update assignment.

Object Shape and Upsert Mask

set does not add a property to the save operation. It replaces the assignment for a property that has already been selected by the loaded object shape and, for an upsert, by its upsert mask.

Consequently:

  • The target property must be loaded in the saved object.
  • If an UpsertMask is present, the target must be updatable according to the mask.
  • Every saved-object property referenced through newValue, newNonNull, or another new* function must also be loaded.
  • An input property does not have to be an update target itself. It may only provide a value used to calculate another property.

Violating one of these rules is reported before the DML statement is executed.

tip

The same rules apply to nested entities in a graph save. The assignment is used whenever its entity type is reached during the save.

Restrictions

An assignment target must be a local physical scalar column. IDs, discriminators, logical-deletion properties, associations, calculated properties, and remote properties cannot be targets. A version property is allowed when the root command uses VersionMode.ASSIGNMENT. The target must still be loaded and selected for update.

The expression can read local physical scalar properties from target, but it cannot create joins. Configure each target at most once on a command.

Save Result

The result of an assignment expression is calculated by the database, so Jimmer does not assume that it equals any value from the saved object. When the updated property is needed by the save result or a transaction trigger, Jimmer reads the actual post-update value through DML returning where supported, or through a follow-up query.

Optimistic Locking

Assignment expressions and custom optimistic locking use the same saved-value expression API. In both APIs, the table expression is the current database row and the new* functions refer to the saved object. They can be used together in one command: the lock predicate decides whether the update is accepted, while set decides how an accepted row is updated.

Conditional Updates

Use setUpdateWhere to skip an ineligible update without an optimistic-lock exception. It controls whether the update happens; set controls the assigned value. Result acceptance distinguishes an accepted update from a rejected one.

For a query source, upsert from select provides update(target, expression) without an insert value, or three-argument merge with separate insert and update expressions. These query-based assignments do not require a loaded input entity property.