MyBatis-Plus Batch Update Strategies for Multiple Records by ID

Uniform Field Updates Across Multiple Identifiers

When applying identical modifications to several records based on their primary keys, the UpdateWrapper approach provides precise control over update conditions:

List<Long> orderIds = Arrays.asList(1001L, 1002L, 1003L, 1004L);
Product updatePayload = new Product();
updatePayload.setStatus(2);
updatePayload.setLastModified(LocalDateTime.now());

UpdateWrapper<Product> updateCondition = new UpdateWrapper<>();
updateCondition.in("id", orderIds)
               .setSql("revision = revision + 1")
               .ge("stock_quantity", 0);
               
int affectedRows = productMapper.update(updatePayload, updateCondition);

This pattern constructs a template object containing the new values, then defines selection criteria through the wrapper. The setSql method enables direct SQL expressions for atomic operations like incrementing version counters. Fields set to null in the template are ignored by default; use wrapper.set("column_name", null) when explicit nullification is required.

Type-Safe Lambda Wrapper Alternative

For compile-time validation and avoidance of hardcoded column strings, LambdaUpdateWrapper offers a robust alternative:

Set<Long> productIds = new HashSet<>(Arrays.asList(50L, 51L, 52L));
LambdaUpdateWrapper<Product> lambdaWrapper = new LambdaUpdateWrapper<>();
lambdaWrapper.in(Product::getId, productIds)
             .set(Product::getStock, 0)
             .set(Product::getSyncStatus, "PROCESSED")
             .between(Product::getPrice, 100, 10000);

int updateCount = productMapper.update(null, lambdaWrapper);

Passing null as the entity parameter ensures only the set() values within the wrapper are applied, preventing unintended overwrites from a template object.

Heterogeneous Updates for Distinct Records

When each record requires unique values, assemble a collection of fully-configured entities and leverage updateBatchById:

List<Order> pendingUpdates = new ArrayList<>(2);

Order firstShipment = new Order();
firstShipment.setId(2048L);
firstShipment.setFulfillmentStatus("SHIPPED");
firstShipment.setCarrierTracking("FED-987654321");
pendingUpdates.add(firstShipment);

Order secondShipment = new Order();
secondShipment.setId(2049L);
secondShipment.setFulfillmentStatus("PROCESSING");
secondShipment.setPriorityLevel(1);
pendingUpdates.add(secondShipment);

// Execute with batch size of 100 for optimal JDBC performance
boolean allSucceeded = orderService.updateBatchById(pendingUpdates, 100);

This approach generates individual UPDATE statements for each entity, efficiently batched by MyBatis-Plus. The batch size parameter controls how many statements are sent per database roundtrip.

Performance and Transactional Considerations

Batch Size Tuning: Default batch size of 1000 may exceed database driver limits. Reduce to 100-500 for stability:

// Application.yml configuration
mybatis-plus:
  global-config:
    db-config:
      update-batch-size: 200

Transaction Boundaries: Wrap batch operations in @Transactional to ensure atomicity:

@Transactional(rollbackFor = Exception.class)
public void processInventoryClearance(List<Long> skuIds) {
    LambdaUpdateWrapper<Inventory> wrapper = new LambdaUpdateWrapper<>();
    wrapper.in(Inventory::getSkuId, skuIds)
           .set(Inventory::getQuantity, 0);
    inventoryMapper.update(null, wrapper);
    
    // Additional related operations
    logClearanceEvent(skuIds);
}

Connection String Requirements: For MySQL, apppend &rewriteBatchedStatements=true to the JDBC URL to enable true batch execution. Without this, statements execute individually despite the batch API.

Large-Scale Update Alternative

For updating millions of records, custom XML mapper with <foreach> yields better performance:

<update id="bulkUpdateStatuses">
    <foreach collection="items" item="item" separator=";">
        UPDATE order_table 
        SET status = #{item.status},
            updated_at = NOW()
        WHERE id = #{item.id}
        AND version = #{item.version}
    </foreach>
</update>

Invoke this mapper method directly for scenarios where wrapper-based updates become inefficient due to extreme data volumes.

Tags: mybatis-plus batch-update java Persistence performance-optimization

Posted on Sat, 17 Oct 2026 16:48:48 +0000 by JetJagger