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.