MyBatis‑Plus Service 层的 更改 方法
点赞 的处理逻辑下面2中写法效果一样这两种写法SQL 完全一样update blog set liked liked 1 where id ?点赞自增也是数据库原子操作并发安全。PutMapping(/like/{id})publicResultlikeBlog(PathVariable(id)Longid){UpdateWrapperBlogwrappernewUpdateWrapper();wrapper.setSql(liked liked 1);// wrapper.setSql(liked liked 1, view_count view_count 1);// 不要写多个 setSql()后写的会覆盖前面的// 多个set()没问题// 但是setSql()只能有一个多个数据库运算写到同一个 setSql字符串用逗号隔开即可。.set(title,新标题)wrapper.eq(id,id);blogService.update(wrapper);returnResult.ok();}PutMapping(/like/{id})publicResultlikeBlog(PathVariable(id)Longid){// 修改点赞数量blogService.update().setSql(liked liked 1).eq(id,id).update();returnResult.ok();}⚠️ 最重要避坑点赞千万不要这么写❌ 错误高并发点赞会出现点赞丢失BlogblogblogService.getById(id);blog.setLiked(blog.getLiked()1);blogService.updateById(blog);原因 先查出来的 liked 值是内存里的 多个用户同时点赞读到同一个旧值1之后覆盖回去就会少点赞。而上面的正确写法setSql(liked liked 1)在 MySQL 层面执行 1是原子操作并发安全这个优点和「链式 / 非链式」没关系两种写法只要用了setSql就都安全。什么时候推荐不用链式如果你后面需要动态条件if 判断链式反而不方便这时就适合拆开写非链式链式写法很难中途插入if判断。UpdateWrapperBlogwrappernewUpdateWrapper();wrapper.setSql(liked liked 1);wrapper.eq(id,id);// 动态追加条件if(需要限制可见){wrapper.eq(status,1);}blogService.update(wrapper);补充对于 IService 层面blogService.update(wrapper)底层源码就是直接调用update(null, wrapper)运行效果完全一模一样。源码IService 默认实现// 单参数版本defaultbooleanupdate(WrapperTupdateWrapper){returnthis.update(null,updateWrapper);}也就是说blogService.update(wrapper);等价于blogService.update(null,wrapper);⚠️但是Mapper层(BaseMapper)没有这个单参数重载blogMapper.update(wrapper);❌ 报错Mapper只能写mapper.update(null,wrapper)两个参数方法到底什么意思update(Tentity,WrapperTwrapper)entity实体对象 → 它里面非null的字段会拼到 SET 后面wrapper条件构造器 → 拼到 WHERE 后面更新的set来源有两处会合并entity 实体里面不为 null 的字段wrapper里的.set()/.setSql()分3种场景演示场景1entity null只用 wrapper 设置字段UpdateWrapperBlogwrappernewUpdateWrapper();wrapper.setSql(liked liked 1);wrapper.eq(id,id);blogService.update(null,wrapper);SET 部分liked liked 1完全来自wrapper。✅ 这时update(wrapper)和update(null,wrapper)没有任何区别。场景2entity有值 wrapper条件最容易踩坑BlogblognewBlog();blog.setTitle(新标题);UpdateWrapperBlogwrappernewUpdateWrapper();wrapper.eq(id,id);blogService.update(blog,wrapper);最终SQLupdateblogsettitle新标题whereid?这里 set 值来自第一个参数blog实体wrapper只负责where条件场景3实体 wrapper都设置了set会合并BlogblognewBlog();blog.setTitle(新标题);UpdateWrapperBlogwrappernewUpdateWrapper();wrapper.set(content,新内容);wrapper.eq(id,id);blogService.update(blog,wrapper);set 同时包含 title 和 content。一个大坑自动填充失效blogService.update(wrapper);因为entity nullMy‑Plus 的 TableField(fill FieldFill.UPDATE) 更新自动填充不会执行如果你需要更新时自动填充updateTime这类字段就不能只靠wrapper。一句话记忆Service层update(wrapper)≡update(null,wrapper)效果相等Mapper层(BaseMapper)没有单参数版本只能写mapper.update(null,wrapper)核心set 子句 实体非 null 字段 wrapper.set / setSql 字段二者合并一起生效而where 子句 wrapper 里所有条件eq、gt…1、工程上推荐的 3 条避坑原则二选一原则不要两边同时set字段不要 entity、wrapper 两边都去 set 更新字段极易出现覆盖bug。方案A只靠 entity 设置要更新的字段wrapper只放where条件不要写set/setSql方案Bentity null所有更新字段全部放到 wrapper.set / setSql自增运算liked liked 1这类数据库计算只能放在 wrapper.setSql不要放到实体// ❌ 错误先查再加1并发丢失BlogblogblogService.getById(id);blog.setLiked(blog.getLiked()1);blogService.updateById(blog);// ✅ 正确原子更新UpdateWrapperBlogwrappernewUpdateWrapper();wrapper.setSql(liked liked 1);wrapper.eq(id,id);blogService.update(null,wrapper);需要自动填充(updateTime) → 必须传实体对象entity不要传null。2、两种标准、安全的写法模板模板1普通字段更新需要自动填充推荐用entityBlogblognewBlog();blog.setTitle(新标题);UpdateWrapperBlogwrappernewUpdateWrapper();wrapper.eq(id,id);// wrapper这里不要再写 .set()blogService.update(blog,wrapper);模板2数据库运算1、加减不需要自动填充entity传nullUpdateWrapperBlogwrappernewUpdateWrapper();wrapper.setSql(liked liked 1);wrapper.eq(id,id);blogService.update(null,wrapper);