一、核心概念与区别
方法 | 适用场景 | 特点 |
---|---|---|
updateById |
按主键更新单条记录 | 实体对象必须包含主键值,更新非空字段 |
update |
按条件更新一条或多条记录 | 需配合 UpdateWrapper ,可灵活指定更新字段和条件,支持无实体更新 |
二、详细操作步骤
1. 基础实体类与 Mapper
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
@Version // 乐观锁字段
private Integer version;
}
@Mapper
public interface UserMapper extends BaseMapper<User> {}
2. updateById
使用示例
// 1. 先查询要修改的实体
User user = userMapper.selectById(1L);
// 2. 修改字段值(非空字段会被更新)
user.setName("UpdatedName");
user.setAge(25);
// 3. 执行更新
int rows = userMapper.updateById(user); // 返回受影响行数
3. update
使用示例
场景1:按条件更新部分字段
// 创建 UpdateWrapper
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper
.set("age", 30) // 直接设置字段值
.set("email", "new@example.com")
.eq("name", "John"); // WHERE name = 'John'
// 执行更新(实体对象传null)
int rows = userMapper.update(null, wrapper);
生成 SQL:
UPDATE user SET age=30, email='new@example.com' WHERE name='John'
场景2:实体对象 + 条件更新
User user = new User();
user.setAge(35); // 只设置需要更新的字段
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getName, "John"); // WHERE name = 'John'
int rows = userMapper.update(user, wrapper);
生成 SQL:
UPDATE user SET age=35 WHERE name='John'
场景3:链式 Lambda 写法
boolean success = new LambdaUpdateChainWrapper<>(userMapper)
.set(User::getAge, 40)
.set(User::getEmail, "lambda@example.com")
.eq(User::getId, 2L)
.update(); // 返回布尔值表示是否更新成功
三、常见错误与解决方案
错误现象 | 原因 | 解决方案 |
---|---|---|
字段更新为 null 失效 |
默认更新策略忽略 null 值 |
1. 字段加 @TableField(updateStrategy = FieldStrategy.IGNORED) 2. 用 UpdateWrapper.set() 强制更新 |
乐观锁版本号不匹配 | 并发更新时版本号已被修改 | 捕获 OptimisticLockException 重试或提示用户 |
update 无 WHERE 条件导致全表更新 |
未设置条件构造器条件 | 启用防全表更新插件 BlockAttackInnerInterceptor |
更新后实体对象版本号未刷新 | 乐观锁字段需手动刷新 | 更新后重新 selectById 获取最新版本号 |
四、注意事项
字段更新策略
- 默认行为(
FieldStrategy.NOT_NULL
):忽略实体对象中null
字段 - 强制更新:
@TableField(updateStrategy = FieldStrategy.IGNORED)
- 默认行为(
乐观锁使用规范
// 必须查询最新版本号 User user = userMapper.selectById(1L); user.setName("NewName"); userMapper.updateById(user); // 自动校验版本号
全表更新防护
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 阻止无 WHERE 的更新 return interceptor; }
五、使用技巧
1. 动态字段更新
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.set(User::getAge, 30);
// 根据条件动态添加更新字段
if (updateEmail) {
wrapper.set(User::getEmail, "dynamic@example.com");
}
userMapper.update(null, wrapper);
2. 字段自增/自减
new LambdaUpdateWrapper<User>()
.setSql("age = age + 1") // 直接写 SQL 片段
.eq(User::getId, 1L)
.update();
3. 批量更新处理
List<User> users = userMapper.selectList(...);
// 方案1:循环 updateById(带乐观锁)
users.forEach(user -> {
user.setStatus(1);
userMapper.updateById(user);
});
// 方案2:一次 update + IN 条件(无乐观锁)
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.set(User::getStatus, 1)
.in(User::getId, ids); // WHERE id IN (1,2,3)
userMapper.update(null, wrapper);
六、最佳实践与性能优化
更新性能优化
- 批量更新:优先用
IN
条件单次更新(无乐观锁时),减少 SQL 执行次数 - 索引优化:确保
WHERE
条件字段有索引,避免全表扫描
- 批量更新:优先用
事务控制
@Transactional(rollbackFor = Exception.class) public void batchUpdateUsers(List<Long> ids) { // 批量更新操作 }
更新字段选择
- 避免
SELECT *
后全字段更新:只查询和更新必要字段// 仅查询 id 和 version User user = userMapper.selectOne( new LambdaQueryWrapper<User>() .select(User::getId, User::getVersion) .eq(User::getId, 1L) );
- 避免
逻辑删除兼容
- 自动过滤已删除数据:配置逻辑删除后,更新操作默认忽略
deleted=1
的记录
- 自动过滤已删除数据:配置逻辑删除后,更新操作默认忽略
七、复杂场景处理
联表更新
// 1. 自定义 Mapper 方法
@Update("UPDATE user u, dept d " +
"SET u.dept_name=d.name " +
"WHERE u.dept_id=d.id AND d.id=#{deptId}")
void updateDeptName(@Param("deptId") Long deptId);
// 2. 通过 Service 调用
userService.updateDeptName(101L);
条件更新 + 子查询
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.set(User::getVipLevel, 2)
.inSql(User::getId,
"SELECT user_id FROM order WHERE amount > 1000"); // 子查询
userMapper.update(null, wrapper);
总结
updateById
:主键更新首选,自动处理乐观锁update
:灵活条件更新,配合UpdateWrapper
动态控制字段- 避坑指南:
- 全表更新防护插件 必须启用
- 字段更新
null
值需特殊配置 - 批量更新优先选
IN
条件
- 性能关键:
- 更新字段最小化
WHERE
条件必须走索引- 大数量更新分批处理
按此规范使用,可兼顾开发效率与系统安全性,避免 90% 的更新操作陷阱。