了解最新公司动态及行业资讯
从3天开发到10分钟交付,优雅应对复杂业务需求的架构实践。
深夜的办公室里,实习生小张面对满屏的SQL映射文件几近崩溃:
“第17个模糊查询接口,字段名又写错了...”
你接过键盘,看着 GoodsMapper.xml中重复的<select>、<insert>、<update>标签——
这些千篇一律的CRUD代码,正吞噬着团队70%的开发时间。
今天,你将用MyBatis Plus(MP)开启一场生产力革命。1.1 CRUD代码的重复炼狱
<!-- 典型MyBatis映射文件片段 --> <select id="selectById" resultType="Goods"> SELECT * FROM goods WHERE id=#{id} </select> <insert id="insert" parameterType="Goods"> INSERT INTO goods(name,price) VALUES(#{name},#{price}) </insert> <!-- 20个类似方法后... -->痛点清单:
字段硬编码:数据库变更需全局搜索替换。SQL错误:price拼成prcie导致生产事故。分页复杂:每个分页查询重复编写count语句。1.2 MP的降维打击能力
MP不是语法糖,而是将CRUD抽象为声明式编程,让开发者专注业务逻辑。
下面用厨房烹饪比喻,带你感受MP的高效魔法。
2.1 食材准备:依赖配置(2分钟)
// 关键依赖 dependencies { implementation com.baomidou:mybatis-plus-boot-starter:3.5.3 implementation com.baomidou:mybatis-plus-generator:3.5.3 implementation org.freemarker:freemarker:2.3.32 // 代码生成模板 }# application.yml 核心配置 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发期SQL日志 global-config: db-config: id-type: assign_id # 分布式ID策略 logic-delete-field: deleted # 逻辑删除字段2.2 切菜备料:实体类设计(3分钟)
@Data // Lombok自动生成getter/setter @TableName("goods") // 绑定表名 public class Goods { @TableId(type = IdType.ASSIGN_ID) // 分布式雪花ID private Long id; @TableField("goods_name") // 字段映射 @NotBlank(message = "商品名不能为空") private String name; @TableField @DecimalMin(value = "0.01", message = "价格必须≥0.01") private BigDecimal price; @TableField(fill = FieldFill.INSERT) // 自动填充 private LocalDateTime createTime; @TableLogic // 逻辑删除标记 private Integer deleted; }设计哲学:
@TableField解耦字段与列名,避免硬编码。校验注解直接嵌入实体,实现声明式数据验证。自动填充减少业务无关代码。2.3 烹饪核心:Mapper与Service(3分钟)
// ███ 只需声明接口 ███ public interface GoodsMapper extends BaseMapper<Goods> { // 复杂查询后期扩展 @Select("SELECT * FROM goods WHERE price > #{minPrice} ORDER BY sales DESC") List<Goods> selectHotGoods(BigDecimal minPrice); } // ███ 服务层继承增强接口 ███ @Service public class GoodsService extends ServiceImpl<GoodsMapper, Goods> { // 1. 自带全套CRUD方法 // save(), removeById(), updateById(), getById(), page()... // 2. 自定义业务方法 public List<Goods> searchGoods(String keyword) { return lambdaQuery() .like(Goods::getName, keyword) .or() .apply("JSON_CONTAINS(keywords, " + keyword + ")") // JSON字段查询 .list(); } }魔法解析:Service继承树提供150+开箱即用方法,覆盖90%业务场景。
2.4 出锅装盘:控制器暴露API(2分钟)
@RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; // ███ 分页查询 ███ @GetMapping("/page") public Page<Goods> page( @RequestParam(defaultValue = "1") int current, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String name) { return goodsService.page(new Page<>(current, size), Wrappers.<Goods>lambdaQuery() .like(StringUtils.isNotBlank(name), Goods::getName, name) ); } // ███ 逻辑删除 ███ @DeleteMapping("/{id}") public boolean delete(@PathVariable Long id) { return goodsService.removeById(id); // 自动触发逻辑删除 } }关键进步:相比传统Controller减少70%代码量。
基础CRUD只是起点,真实业务需要更强大的武器库。
3.1 条件构造器:动态SQL的终极方案
public Page<Goods> complexQuery(GoodsQuery query) { return goodsService.lambdaQuery() .like(StringUtils.isNotBlank(query.getKeyword()), Goods::getName, query.getKeyword()) .gt(query.getMinPrice() != null, Goods::getPrice, query.getMinPrice()) .lt(query.getMaxPrice() != null, Goods::getPrice, query.getMaxPrice()) .in(!CollectionUtils.isEmpty(query.getCategoryIds()), Goods::getCategoryId, query.getCategoryIds()) .eq(Goods::getStatus, 1) // 上架商品 .orderByDesc(Goods::getSales) // 销量排序 .page(new Page<>(query.getPage(), query.getSize())); }优势对比:
方案
可读性
防SQL注入
代码量
XML动态SQL
差
需手动防
多
MP条件构造器
优
自动防护
少60%
3.2 自定义SQL:应对极端复杂场景
<!-- 位于GoodsMapper.xml --> <select id="selectGoodsWithCategory" resultType="GoodsVO"> SELECT g.*, c.name as categoryName FROM goods g LEFT JOIN category c ON g.category_id = c.id WHERE g.id = #{id} </select>// Mapper接口增强 public interface GoodsMapper extends BaseMapper<Goods> { // ███ 注解方式 ███ @Select("SELECT * FROM goods WHERE category_id = #{categoryId}") List<Goods> selectByCategory(@Param("categoryId") Long categoryId); // ███ XML方式 ███ GoodsVO selectGoodsWithCategory(Long id); }设计平衡:80%简单操作用MP,20%复杂查询保留原生MyBatis灵活性。
3.3 多租户架构:SAAS系统必备
public class TenantInterceptor implements InnerInterceptor { @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 自动注入租户ID String sql = boundSql.getSql(); if (sql.contains("tenant_id")) return; String newSql = "SELECT * FROM (" + sql + ") tmp WHERE tenant_id = " + getCurrentTenantId(); ReflectionUtils.setFieldValue(boundSql, "sql", newSql); } }安全策略:在SQL解析层自动隔离租户数据,避免越权风险。
4.1 分层架构规范
src/main/java ├── controller # 控制层 ├── service # 业务层 │ ├── impl # 实现类 ├── mapper # 数据层 ├── entity # 实体类 ├── dto # 数据传输对象 │ ├── request # 入参DTO │ └── response # 出参DTO └── config # 配置类 └── MybatisPlusConfig.java4.2 全局异常处理
@RestControllerAdvice public class GlobalExceptionHandler { // 处理数据校验异常 @ExceptionHandler(MethodArgumentNotValidException.class) public Result handleValidException(MethodArgumentNotValidException e) { BindingResult result = e.getBindingResult(); String message = result.getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining("; ")); return Result.fail(400, message); } // 处理MyBatisPlus异常 @ExceptionHandler(MybatisPlusException.class) public Result handleMybatisPlusException(MybatisPlusException e) { return Result.fail(500, "数据操作失败: " + e.getMessage()); } }4.3 性能优化四板斧
优化点
方案
效果
分页插件
启用PageOptimize模式
减少1次count查询
二级缓存
整合Redis+注解@CacheNamespace
查询速度提升8倍
批量操作
saveBatch()设置批次大小1000
插入速度提升15倍
SQL打印
生产环境关闭log-impl
降低CPU消耗30%
5.1 生成器配置(核心代码)
FastAutoGenerator.create(dataSourceConfig) .globalConfig(builder -> builder.author("DevSquad") // 作者 .outputDir("D://gen-code") // 输出目录 .enableSwagger() // 开启swagger ) .packageConfig(builder -> builder.parent("com.example.mall") // 包名 .moduleName("product") // 模块名 ) .strategyConfig(builder -> builder.addInclude("goods", "category") // 表名 .entityBuilder().enableLombok() // 实体策略 .controllerBuilder().enableRestStyle() // REST风格 .mapperBuilder().enableMapperAnnotation() // @Mapper ) .templateEngine(new FreemarkerTemplateEngine()) // 模板引擎 .execute();5.2 生成效果
D://gen-code ├── goods │ ├── GoodsController.java │ ├── GoodsService.java │ ├── GoodsServiceImpl.java │ ├── GoodsMapper.java │ ├── Goods.java # 实体类 │ └── GoodsDTO.java # DTO对象 └── category # 相同结构效率对比:手动创建1个实体模块需30分钟 → 生成器10秒完成。
当小张看到自动生成的代码时,眼睛亮了起来:
“原来商品模块可以这么快完成!”
你指着屏幕上的分层代码说:
“记住,真正强大的不是框架本身,而是它让我们摆脱低级重复,
将创造力倾注在真正改变业务的复杂逻辑上”。下一篇:《SpringCloud alibaba:微服务架构生死局》。