MyBatis-Flex is an elegant enhancement framework for MyBatis providing type-safe query building, active record patterns, and comprehensive ORM capabilities
—
Fluent, type-safe update operations with method chaining for readable and maintainable database updates. The UpdateChain provides a modern alternative to traditional update methods, combining field setting, condition building, and execution in a single fluent interface.
Fluent update builder that combines field setting capabilities with query condition support. Provides type-safe field updates with lambda expressions and comprehensive condition building.
/**
* Fluent update chain for type-safe field updates and condition building
* @param <T> Entity type
*/
public class UpdateChain<T> extends QueryWrapperAdapter<UpdateChain<T>> implements PropertySetter<UpdateChain<T>> {
/**
* Create UpdateChain from entity class
* Automatically resolves BaseMapper from entity class
* @param entityClass entity class type
* @return new UpdateChain instance
*/
public static <T> UpdateChain<T> of(Class<T> entityClass);
/**
* Create UpdateChain from BaseMapper
* @param baseMapper mapper instance for the entity
* @return new UpdateChain instance
*/
public static <T> UpdateChain<T> of(BaseMapper<T> baseMapper);
/**
* Create UpdateChain from entity object
* Uses object values as update data and resolves mapper automatically
* @param entityObject entity instance with update values
* @return new UpdateChain instance
*/
public static <T> UpdateChain<T> of(T entityObject);
/**
* Create UpdateChain from entity object with specific mapper
* @param entityObject entity instance with update values
* @param baseMapper mapper instance for the entity
* @return new UpdateChain instance
*/
public static <T> UpdateChain<T> of(T entityObject, BaseMapper<T> baseMapper);
}Set field values for update operations with support for conditional setting and raw SQL expressions.
// Basic field setting
/**
* Set field value using property name
* @param property field/property name
* @param value value to set
* @return UpdateChain for method chaining
*/
public UpdateChain<T> set(String property, Object value);
/**
* Set field value using QueryColumn
* @param queryColumn column reference
* @param value value to set
* @return UpdateChain for method chaining
*/
public UpdateChain<T> set(QueryColumn queryColumn, Object value);
/**
* Set field value using lambda getter (type-safe)
* @param getter lambda expression referencing getter method
* @param value value to set
* @return UpdateChain for method chaining
*/
public <L> UpdateChain<T> set(LambdaGetter<L> getter, Object value);
// Conditional field setting
/**
* Set field value conditionally using property name
* @param property field/property name
* @param value value to set
* @param isEffective whether to include this set operation
* @return UpdateChain for method chaining
*/
public UpdateChain<T> set(String property, Object value, boolean isEffective);
/**
* Set field value conditionally using QueryColumn
* @param queryColumn column reference
* @param value value to set
* @param isEffective whether to include this set operation
* @return UpdateChain for method chaining
*/
public UpdateChain<T> set(QueryColumn queryColumn, Object value, boolean isEffective);
/**
* Set field value conditionally using lambda getter
* @param getter lambda expression referencing getter method
* @param value value to set
* @param isEffective whether to include this set operation
* @return UpdateChain for method chaining
*/
public <L> UpdateChain<T> set(LambdaGetter<L> getter, Object value, boolean isEffective);
/**
* Set field value with BooleanSupplier condition
* @param property field/property name
* @param value value to set
* @param isEffective supplier providing condition evaluation
* @return UpdateChain for method chaining
*/
public UpdateChain<T> set(String property, Object value, BooleanSupplier isEffective);
/**
* Set field value with Predicate condition
* @param property field/property name
* @param value value to set
* @param isEffective predicate to test the value
* @return UpdateChain for method chaining
*/
public <V> UpdateChain<T> set(String property, V value, Predicate<V> isEffective);Set fields using raw SQL expressions for complex update operations, calculations, and database functions.
// Raw SQL field setting
/**
* Set field to raw SQL expression using property name
* @param property field/property name
* @param value raw SQL expression or database function
* @return UpdateChain for method chaining
*/
public UpdateChain<T> setRaw(String property, Object value);
/**
* Set field to raw SQL expression using QueryColumn
* @param queryColumn column reference
* @param value raw SQL expression or database function
* @return UpdateChain for method chaining
*/
public UpdateChain<T> setRaw(QueryColumn queryColumn, Object value);
/**
* Set field to raw SQL expression using lambda getter
* @param getter lambda expression referencing getter method
* @param value raw SQL expression or database function
* @return UpdateChain for method chaining
*/
public <L> UpdateChain<T> setRaw(LambdaGetter<L> getter, Object value);
// Conditional raw SQL setting
/**
* Set field to raw SQL expression conditionally using property name
* @param property field/property name
* @param value raw SQL expression
* @param isEffective whether to include this set operation
* @return UpdateChain for method chaining
*/
public UpdateChain<T> setRaw(String property, Object value, boolean isEffective);
/**
* Set field to raw SQL expression conditionally using QueryColumn
* @param queryColumn column reference
* @param value raw SQL expression
* @param isEffective whether to include this set operation
* @return UpdateChain for method chaining
*/
public UpdateChain<T> setRaw(QueryColumn queryColumn, Object value, boolean isEffective);
/**
* Set field to raw SQL expression conditionally using lambda getter
* @param getter lambda expression referencing getter method
* @param value raw SQL expression
* @param isEffective whether to include this set operation
* @return UpdateChain for method chaining
*/
public <L> UpdateChain<T> setRaw(LambdaGetter<L> getter, Object value, boolean isEffective);
/**
* Set field to raw SQL with BooleanSupplier condition
* @param property field/property name
* @param value raw SQL expression
* @param isEffective supplier providing condition evaluation
* @return UpdateChain for method chaining
*/
public UpdateChain<T> setRaw(String property, Object value, BooleanSupplier isEffective);
/**
* Set field to raw SQL with Predicate condition
* @param property field/property name
* @param value raw SQL expression
* @param isEffective predicate to test the value
* @return UpdateChain for method chaining
*/
public <V> UpdateChain<T> setRaw(String property, V value, Predicate<V> isEffective);Build WHERE conditions using the same powerful condition system as QueryWrapper with full support for complex logical expressions.
// WHERE conditions
/**
* Add WHERE condition using QueryCondition
* @param queryCondition condition to add
* @return UpdateChain for method chaining
*/
public UpdateChain<T> where(QueryCondition queryCondition);
/**
* Add WHERE condition using raw SQL
* @param sql raw SQL condition
* @return UpdateChain for method chaining
*/
public UpdateChain<T> where(String sql);
/**
* Add WHERE condition using parameterized SQL
* @param sql SQL with parameter placeholders
* @param params parameter values
* @return UpdateChain for method chaining
*/
public UpdateChain<T> where(String sql, Object... params);
/**
* Add WHERE conditions from Map (key=value)
* @param whereConditions map of field names to values
* @return UpdateChain for method chaining
*/
public UpdateChain<T> where(Map<String, Object> whereConditions);
/**
* Add WHERE condition using Consumer for complex logic
* @param consumer consumer that configures nested conditions
* @return UpdateChain for method chaining
*/
public UpdateChain<T> where(Consumer<QueryWrapper> consumer);
// AND conditions
/**
* Add AND condition using QueryCondition
* @param queryCondition condition to add with AND logic
* @return UpdateChain for method chaining
*/
public UpdateChain<T> and(QueryCondition queryCondition);
/**
* Add AND condition using raw SQL
* @param sql raw SQL condition
* @return UpdateChain for method chaining
*/
public UpdateChain<T> and(String sql);
/**
* Add AND condition using parameterized SQL
* @param sql SQL with parameter placeholders
* @param params parameter values
* @return UpdateChain for method chaining
*/
public UpdateChain<T> and(String sql, Object... params);
/**
* Add AND conditions from Map
* @param whereConditions map of field names to values
* @return UpdateChain for method chaining
*/
public UpdateChain<T> and(Map<String, Object> whereConditions);
/**
* Add AND condition using Consumer for nested logic
* @param consumer consumer that configures nested conditions
* @return UpdateChain for method chaining
*/
public UpdateChain<T> and(Consumer<QueryWrapper> consumer);
// OR conditions
/**
* Add OR condition using QueryCondition
* @param queryCondition condition to add with OR logic
* @return UpdateChain for method chaining
*/
public UpdateChain<T> or(QueryCondition queryCondition);
/**
* Add OR condition using raw SQL
* @param sql raw SQL condition
* @return UpdateChain for method chaining
*/
public UpdateChain<T> or(String sql);
/**
* Add OR condition using parameterized SQL
* @param sql SQL with parameter placeholders
* @param params parameter values
* @return UpdateChain for method chaining
*/
public UpdateChain<T> or(String sql, Object... params);
/**
* Add OR conditions from Map
* @param whereConditions map of field names to values
* @return UpdateChain for method chaining
*/
public UpdateChain<T> or(Map<String, Object> whereConditions);
/**
* Add OR condition using Consumer for nested logic
* @param consumer consumer that configures nested conditions
* @return UpdateChain for method chaining
*/
public UpdateChain<T> or(Consumer<QueryWrapper> consumer);Execute the constructed update or delete operations and return results.
/**
* Execute update operation with configured field changes and conditions
* @return true if update affected at least one row, false otherwise
*/
public boolean update();
/**
* Execute delete operation using configured conditions
* @return true if delete affected at least one row, false otherwise
*/
public boolean remove();
/**
* Generate SQL string for debugging and logging purposes
* Includes parameter substitution for complete SQL visualization
* @return complete SQL string with parameters substituted
*/
public String toSQL();// Simple field updates using different approaches
UpdateChain.of(User.class)
.set("name", "John Doe")
.set("email", "john@example.com")
.where("id = ?", 1)
.update();
// Type-safe updates with lambda expressions
UpdateChain.of(User.class)
.set(User::getName, "Jane Smith")
.set(User::getEmail, "jane@example.com")
.where(USER.ID.eq(1))
.update();
// Using QueryColumn references
UpdateChain.of(User.class)
.set(USER.NAME, "Bob Wilson")
.set(USER.EMAIL, "bob@example.com")
.where(USER.ID.eq(1))
.update();String newEmail = getNewEmail();
boolean updateStatus = shouldUpdateStatus();
UpdateChain.of(User.class)
.set("name", "Updated Name")
.set("email", newEmail, StringUtil.isNotBlank(newEmail)) // Only set if not blank
.set("status", "ACTIVE", updateStatus) // Only set if condition is true
.set("lastModified", LocalDateTime.now(), () -> shouldUpdateTimestamp()) // BooleanSupplier
.where(USER.ID.eq(userId))
.update();
// Using Predicate for value validation
UpdateChain.of(Product.class)
.set("price", newPrice, price -> price > 0) // Only set if price is positive
.set("category", newCategory, Objects::nonNull) // Only set if not null
.where(PRODUCT.ID.eq(productId))
.update();// Using database functions and calculations
UpdateChain.of(Account.class)
.setRaw("balance", "balance + ?", 100.0) // Add to existing balance
.setRaw("lastUpdated", "NOW()") // Use database function
.setRaw("version", "version + 1") // Increment version
.where(ACCOUNT.ID.eq(accountId))
.update();
// Complex calculations
UpdateChain.of(Order.class)
.setRaw("totalAmount", "quantity * unitPrice * (1 - discount)")
.setRaw("tax", "totalAmount * 0.08")
.where(ORDER.ID.eq(orderId))
.update();
// Conditional raw SQL
boolean applyDiscount = checkDiscountEligibility();
UpdateChain.of(Product.class)
.set("name", newName)
.setRaw("price", "price * 0.9", applyDiscount) // Apply 10% discount conditionally
.where(PRODUCT.CATEGORY.eq("ELECTRONICS"))
.update();// Multiple AND conditions
UpdateChain.of(User.class)
.set("status", "INACTIVE")
.set("lastLogin", null)
.where(USER.LAST_LOGIN.lt(LocalDateTime.now().minusMonths(6)))
.and(USER.STATUS.eq("ACTIVE"))
.and(USER.EMAIL_VERIFIED.eq(false))
.update();
// OR conditions with nested logic
UpdateChain.of(Product.class)
.set("onSale", true)
.set("salePrice", "price * 0.8")
.where(PRODUCT.CATEGORY.eq("CLOTHING"))
.or(wrapper -> wrapper
.and(PRODUCT.INVENTORY.gt(100))
.and(PRODUCT.CREATED_AT.lt(LocalDate.now().minusMonths(3)))
)
.update();
// Map-based conditions
Map<String, Object> conditions = new HashMap<>();
conditions.put("status", "PENDING");
conditions.put("priority", "HIGH");
UpdateChain.of(Task.class)
.set("assignee", newAssignee)
.set("updatedAt", LocalDateTime.now())
.where(conditions)
.update();// Update multiple fields based on entity state
User user = getUserById(userId);
UpdateChain.of(User.class)
.set(User::getName, user.getName())
.set(User::getEmail, user.getEmail())
.set(User::getPhone, user.getPhone())
.set(User::getUpdatedAt, LocalDateTime.now())
.where(USER.ID.eq(userId))
.update();
// Update with entity object as source
User updateData = new User();
updateData.setName("Updated Name");
updateData.setEmail("updated@example.com");
UpdateChain.of(updateData) // Use entity as update source
.where(USER.ID.eq(userId))
.update();// Simple delete with conditions
UpdateChain.of(User.class)
.where(USER.STATUS.eq("DELETED"))
.and(USER.LAST_LOGIN.lt(LocalDateTime.now().minusYears(1)))
.remove();
// Conditional delete
boolean shouldCleanup = isCleanupScheduled();
UpdateChain.of(LogEntry.class)
.where(LOG_ENTRY.CREATED_AT.lt(LocalDate.now().minusDays(30)), shouldCleanup)
.remove();
// Delete with complex conditions
UpdateChain.of(Session.class)
.where(wrapper -> wrapper
.and(SESSION.EXPIRED_AT.lt(LocalDateTime.now()))
.or(SESSION.LAST_ACTIVITY.lt(LocalDateTime.now().minusHours(24)))
)
.remove();// Generate SQL for debugging
String sql = UpdateChain.of(User.class)
.set("name", "Debug User")
.set("status", "ACTIVE")
.where(USER.ID.eq(123))
.toSQL();
System.out.println("Generated SQL: " + sql);
// Output: UPDATE user SET name = 'Debug User', status = 'ACTIVE' WHERE id = 123
// Complex update SQL generation
String complexSql = UpdateChain.of(Order.class)
.setRaw("totalAmount", "quantity * unitPrice")
.set("status", "CALCULATED")
.where(ORDER.STATUS.eq("PENDING"))
.and(ORDER.CREATED_AT.between(startDate, endDate))
.toSQL();/**
* Interface for setting entity properties with fluent API
* @param <R> Return type for method chaining
*/
public interface PropertySetter<R> {
// Field setting methods with various parameter types
R set(String property, Object value);
R set(QueryColumn property, Object value);
<T> R set(LambdaGetter<T> property, Object value);
// Conditional field setting methods
R set(String property, Object value, boolean isEffective);
R set(String property, Object value, BooleanSupplier isEffective);
<V> R set(String property, V value, Predicate<V> isEffective);
// Raw SQL field setting methods
R setRaw(String property, Object value);
R setRaw(QueryColumn property, Object value);
<T> R setRaw(LambdaGetter<T> property, Object value);
// Conditional raw SQL setting methods
R setRaw(String property, Object value, boolean isEffective);
R setRaw(String property, Object value, BooleanSupplier isEffective);
<V> R setRaw(String property, V value, Predicate<V> isEffective);
}
/**
* Update wrapper interface for entity modification tracking
* @param <T> Entity type
*/
public interface UpdateWrapper<T> extends PropertySetter<UpdateWrapper<T>>, Serializable {
/**
* Get map of field modifications
* @return map containing field names and their update values
*/
Map<String, Object> getUpdates();
/**
* Create UpdateWrapper from entity object
* @param entity entity instance
* @return UpdateWrapper wrapping the entity
*/
static <T> UpdateWrapper<T> of(T entity);
/**
* Create UpdateWrapper from entity class
* @param tClass entity class
* @return UpdateWrapper for the entity type
*/
static <T> UpdateWrapper<T> of(Class<T> tClass);
/**
* Convert back to entity object
* @return entity instance with modifications
*/
T toEntity();
}
/**
* Query wrapper adapter providing condition building capabilities
* @param <R> Return type for method chaining
*/
public class QueryWrapperAdapter<R extends QueryWrapperAdapter<R>> extends QueryWrapper {
// Inherits all condition building methods from QueryWrapper
// Returns parameterized type R for proper method chaining
}UpdateChain integrates seamlessly with existing BaseMapper operations and can be mixed with traditional CRUD methods:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void updateUserStatus(Long userId, String newStatus) {
// Traditional approach
User user = userMapper.selectOneById(userId);
user.setStatus(newStatus);
user.setUpdatedAt(LocalDateTime.now());
userMapper.update(user);
// UpdateChain approach - more readable and explicit
UpdateChain.of(User.class)
.set(User::getStatus, newStatus)
.set(User::getUpdatedAt, LocalDateTime.now())
.where(USER.ID.eq(userId))
.update();
}
public void bulkUpdatePrices(String category, BigDecimal multiplier) {
// Complex update that would be difficult with entity-based updates
UpdateChain.of(Product.class)
.setRaw("price", "price * ?", multiplier)
.setRaw("updatedAt", "NOW()")
.set("modifiedBy", getCurrentUser())
.where(PRODUCT.CATEGORY.eq(category))
.and(PRODUCT.STATUS.eq("ACTIVE"))
.update();
}
}| Approach | UpdateChain | Traditional Entity Update | BaseMapper updateByQuery |
|---|---|---|---|
| Readability | High - fluent, self-documenting | Medium - requires entity manipulation | Low - separate wrapper creation |
| Type Safety | High - lambda expressions | High - entity properties | Medium - QueryWrapper conditions |
| Flexibility | High - raw SQL, conditionals | Low - entity constraints | High - full query control |
| Performance | Optimal - direct SQL | Good - entity mapping | Optimal - direct SQL |
| Maintenance | Easy - method chaining | Easy - familiar pattern | Complex - wrapper management |
// Traditional entity-based update
User user = userMapper.selectOneById(userId);
user.setName("New Name");
user.setStatus("ACTIVE");
userMapper.update(user); // Updates ALL fields
// UpdateChain - selective field updates
UpdateChain.of(User.class)
.set("name", "New Name")
.set("status", "ACTIVE") // Only updates specified fields
.where(USER.ID.eq(userId))
.update();
// Traditional BaseMapper with QueryWrapper
QueryWrapper queryWrapper = QueryWrapper.create()
.where(USER.ID.eq(userId));
User updateEntity = new User();
updateEntity.setName("New Name");
updateEntity.setStatus("ACTIVE");
userMapper.updateByQuery(updateEntity, queryWrapper);
// UpdateChain - combined in single fluent interface
UpdateChain.of(User.class)
.set("name", "New Name")
.set("status", "ACTIVE")
.where(USER.ID.eq(userId))
.update();The UpdateChain provides a modern, fluent alternative to traditional update patterns while maintaining full compatibility with existing MyBatis-Flex functionality. It excels at selective field updates, conditional modifications, and complex update scenarios that would be cumbersome with entity-based approaches.
Install with Tessl CLI
npx tessl i tessl/maven-com-mybatis-flex--mybatis-flex-core