我们再次切换到“SQL工匠”模式,用 MyBatis-Plus 来实现这个同样复杂的 updateSolutionBrand
接口。
使用 MyBatis-Plus 实现这个接口,将再次凸显它与 JPA 在处理事务、部分更新和关联更新方面的巨大差异。这篇博客将重点展示如何通过手写 SQL 和精巧的逻辑编排,来完成这次“外科手术”。
你好,我是坚持哥!在之前的文章中,我们已经领略了 MyBatis-Plus 在查询和简单创建方面的强大。今天,挑战再次升级!我们将面对一个后端开发中最复杂、也最考验基本功的场景之一——带有“部分更新”和“关联更新”的复杂修改接口。
我们将以 updateSolutionBrand
(修改品牌并更新其关联分类) 为例,深入探索:
UPDATE
语句?如果你想看看如何用 MyBatis-Plus 像一位钟表匠一样,精密地组装出一个复杂的更新接口,那么,请准备好你的“放大镜”和“镊子”,我们的 SQL 精密工程,现在开始!️
环节 | 需求描述 / 实现方案 |
---|---|
核心需求 | 修改一个“方案品牌” (SolutionBrand ) 的基本信息,并能覆盖式地更新它所关联的“品牌分类” (SolutionBrandCategory )。 |
⚙️ 技术栈 | Spring Boot, MyBatis-Plus, MySQL |
关键逻辑 | 手动三步走:在一个 @Transactional 方法中,按顺序执行:1. UPDATE 主表:调用自定义的动态 UPDATE 方法,实现部分更新。2. DELETE 中间表:删除该品牌在中间表的所有旧关联。3. INSERT 中间表:批量插入该品牌在中间表的新关联。 |
✍️ 部分更新 | 在 XML Mapper 中编写自定义 UPDATE 语句,使用 和 标签,只更新 Payload 中非 null 的字段。 |
关联更新 | 先删后增。先调用 relationMapper.delete() 删除所有旧关联,再调用 relationMapper.batchInsert() 插入新关联。 |
️ 架构模式 | Controller -> Service -> Mapper 分层架构 + Payload/VO 数据传输模式。 |
️ 数据校验 | 在 Service 层进行权限校验(确保只能修改自己的数据)和业务校验。 |
使用 MyBatis-Plus,我们需要像外科医生一样,清晰地规划每一步操作。
在这个场景下,Service 层就像一位运筹帷幄的将军,指挥着不同的 Mapper 在一个事务中协同作战。
我们需要为 SolutionBrand
编写一个自定义的动态 UPDATE
方法。
SolutionBrandMapper.java
@Mapper
public interface SolutionBrandMapper extends BaseMapper<SolutionBrand> {
int updatePartially(
@Param("id") Integer id,
@Param("payload") SolutionBrandUpdatePayload payload
);
}
SolutionBrandMapper.xml
<mapper namespace="...SolutionBrandMapper">
<update id="updatePartially">
UPDATE solution_brand
<set>
last_modified_date = NOW(),
<if test="payload.solutionIntroduction != null">
solution_introduction = #{payload.solutionIntroduction},
if>
<if test="payload.ranks != null">
ranks = #{payload.ranks},
if>
<if test="payload.status != null">
status = #{payload.status},
if>
set>
WHERE id = #{id}
update>
mapper>
Service 层是所有逻辑的编排者,它需要精确地控制操作的顺序。
@Service
public class SolutionBrandService {
@Autowired
private SolutionBrandMapper solutionBrandMapper;
@Autowired
private SolutionBrandCategoryRelationMapper relationMapper;
// ...
@Transactional
public SolutionBrandVO updateSolutionBrand(Integer adminId, Integer solutionBrandId, SolutionBrandUpdatePayload payload) {
// 1. 权限和业务校验...
// 2. 步骤一:部分更新主表
solutionBrandMapper.updatePartially(solutionBrandId, payload);
// 3. 步骤二:删除所有旧的关联
LambdaUpdateWrapper<SolutionBrandCategoryRelation> deleteWrapper = new LambdaUpdateWrapper<>();
deleteWrapper.eq(SolutionBrandCategoryRelation::getSolutionBrandId, solutionBrandId);
relationMapper.delete(deleteWrapper);
// 4. 步骤三:插入新的关联
if (payload.getCategoryIds() != null && !payload.getCategoryIds().isEmpty()) {
// ... 校验 categoryIds 的合法性 ...
List<SolutionBrandCategoryRelation> relations = payload.getCategoryIds().stream()
.map(categoryId -> new SolutionBrandCategoryRelation(solutionBrandId, categoryId))
.collect(Collectors.toList());
relationMapper.batchInsert(relations);
}
// 5. 查询最新数据并转换为 VO 返回
return findAndConvertToVO(solutionBrandId);
}
}
最后,我们用一张思维导图来总结 MyBatis-Plus 实现复杂更新接口的完整思想。
通过这个案例,我们能深刻体会到,当业务逻辑变得复杂,需要精细控制数据库操作时,MyBatis-Plus 提供的“手术刀”是多么锋利。虽然需要更多的手动编码,但换来的是无与伦比的控制力和性能优化的可能性。希望这次的实战分享,能让你在面对复杂更新时,更加胸有成竹!✨