CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-mybatis-flex--mybatis-flex-core

MyBatis-Flex is an elegant enhancement framework for MyBatis providing type-safe query building, active record patterns, and comprehensive ORM capabilities

Pending
Overview
Eval results
Files

update-chain.mddocs/

UpdateChain Fluent Update API

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.

Capabilities

UpdateChain Class

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);
}

Field Setting Operations

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);

Raw SQL Setting Operations

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);

Condition Building Operations

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);

Execution Operations

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();

Usage Examples

Basic Field Updates

// 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();

Conditional Field Setting

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();

Raw SQL Expressions

// 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();

Complex Conditions

// 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();

Batch-Style Updates

// 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();

Delete Operations

// 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();

SQL Generation and Debugging

// 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();

Types

Supporting Classes and Interfaces

/**
 * 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
}

Integration Patterns

With BaseMapper Operations

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();
    }
}

Comparison with Traditional Updates

ApproachUpdateChainTraditional Entity UpdateBaseMapper updateByQuery
ReadabilityHigh - fluent, self-documentingMedium - requires entity manipulationLow - separate wrapper creation
Type SafetyHigh - lambda expressionsHigh - entity propertiesMedium - QueryWrapper conditions
FlexibilityHigh - raw SQL, conditionalsLow - entity constraintsHigh - full query control
PerformanceOptimal - direct SQLGood - entity mappingOptimal - direct SQL
MaintenanceEasy - method chainingEasy - familiar patternComplex - 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

docs

active-record.md

bootstrap-config.md

crud-operations.md

database-support.md

index.md

pagination.md

query-builder.md

row-operations.md

service-layer.md

update-chain.md

tile.json