当前位置: 首页 > news >正文

扩展和插件功能

生成代码

下载好之后要重启IDEA

连接好数据库

具体操作:

然后就可以直接生成代码了

静态工具

有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db,其中的一些静态方法与IService中方法签名基本一致,也可以实现CRUD功能:

案例;:

需求:

①改造根据id查询用户的接口,查询用户的同时,查询出用户对应的所有地址

②改造根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址

③实现根据用户id查询收货地址功能,需要验证用户状态,冻结用户抛出异常(练习)

操作:

UserMapper 接口

继承 MyBatis - Plus 的 BaseMapper,获得通用 CRUD 能力:

package com.itheima.mp.domain.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper<User> {
}

AddressMapper 接口

同样继承 BaseMapper,假设 Address 实体类有 userId 关联用户 ID:

package com.itheima.mp.domain.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.Address;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface AddressMapper extends BaseMapper<Address> {
}

UserService 接口

package com.itheima.mp.domain.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.po.Address;
import java.util.List;public interface UserService extends IService<User> {// 需求1:根据id查询用户及地址User getUserWithAddressesById(Long id);// 需求2:根据id批量查询用户及地址List<User> listUsersWithAddressesByIds(List<Long> ids);// 需求3:根据用户id查询收货地址,验证状态List<Address> getAddressesByUserIdWithStatusCheck(Long userId) throws UserFrozenException;
}// 自定义冻结异常
class UserFrozenException extends RuntimeException {public UserFrozenException(String message) {super(message);}
}

Service 实现类

package com.itheima.mp.domain.service.impl;import com.baomidou.mybatisplus.core.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.SqlHelper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.mapper.AddressMapper;
import com.itheima.mp.domain.mapper.UserMapper;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.service.UserService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {private final AddressMapper addressMapper;public UserServiceImpl(AddressMapper addressMapper) {this.addressMapper = addressMapper;}// 需求1:根据id查询用户及地址,用 MyBatis - Plus 静态工具 Wrappers 构造条件(这里查用户用 getById,也可演示 wrapper )@Overridepublic User getUserWithAddressesById(Long id) {// 方式1:直接用 IService 提供的 getById(底层已封装常用查询 )User user = getById(id);if (SqlHelper.retBool(user)) { // SqlHelper.retBool 辅助判断查询结果是否有效// 查地址:用 Wrappers.lambdaQuery 构造条件,根据 userId 关联List<Address> addresses = addressMapper.selectList(Wrappers.lambdaQuery(Address.class).eq(Address::getUserId, id));user.setAddresses(addresses);}return user;}// 需求2:批量查询用户及地址,结合 MyBatis - Plus 批量查询 + 地址关联@Overridepublic List<User> listUsersWithAddressesByIds(List<Long> ids) {// 批量查用户:用 IService 的 listByIdsList<User> userList = listByIds(ids);if (!SqlHelper.isEmpty(userList)) { // 辅助判断集合是否非空List<Long> userIds = userList.stream().map(User::getId).collect(Collectors.toList());// 批量查地址:用 Wrappers.lambdaQuery 构造 in 条件List<Address> addressList = addressMapper.selectList(Wrappers.lambdaQuery(Address.class).in(Address::getUserId, userIds));// 关联地址到用户:遍历用户,匹配地址userList.forEach(user -> {List<Address> userAddresses = addressList.stream().filter(address -> address.getUserId().equals(user.getId())).collect(Collectors.toList());user.setAddresses(userAddresses);});}return userList;}// 需求3:查询地址并校验用户状态,用 MyBatis - Plus 工具简化逻辑@Overridepublic List<Address> getAddressesByUserIdWithStatusCheck(Long userId) throws UserFrozenException {// 查用户:用 Wrappers.lambdaQuery 构造条件(也可用 getById,演示不同方式 )User user = getOne(Wrappers.lambdaQuery(User.class).eq(User::getId, userId));if (SqlHelper.retBool(user)) {if (user.getStatus() == 2) { // 状态2为冻结,抛异常throw new UserFrozenException("用户已冻结,无法查询地址");}// 查地址return addressMapper.selectList(Wrappers.lambdaQuery(Address.class).eq(Address::getUserId, userId));}return List.of();}
}

Controller 层

package com.itheima.mp.domain.controller;import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.service.UserService;
import com.itheima.mp.domain.service.UserFrozenException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;@RestController
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}// 需求1:根据id查询用户及地址@GetMapping("/user/{id}")public User getUserWithAddresses(@PathVariable Long id) {return userService.getUserWithAddressesById(id);}// 需求2:批量查询用户及地址@GetMapping("/users")public List<User> listUsersWithAddresses(@RequestParam List<Long> ids) {return userService.listUsersWithAddressesByIds(ids);}// 需求3:查询地址并校验状态@GetMapping("/user/address/{userId}")public List<Address> getAddressesWithStatusCheck(@PathVariable Long userId) {try {return userService.getAddressesByUserIdWithStatusCheck(userId);} catch (UserFrozenException e) {// 实际可返回统一错误响应,这里简单处理e.printStackTrace();return List.of();}}
}

逻辑删除

逻辑删除是基于代码逻辑模拟删除效果,不会真正删除数据,核心思路:

  1. 表中添加字段(如 deleted )标记数据是否被删除
  2. 删除操作时,将标记字段置为“已删除值”(如 1
  3. 查询操作时,自动过滤已删除数据(只查标记为“未删除值”,如 0 的数据 )

例如:

  • 删除操作:执行更新语句,标记数据为已删除
UPDATE user SET deleted = 1 WHERE id = 1 AND deleted = 0;

(通过 AND deleted = 0 避免重复标记,保证幂等性 )

  • 查询操作:自动附加条件,只查未删除数据
SELECT * FROM user WHERE deleted = 0;

MyBatis - Plus 可自动实现逻辑删除,自动增强 CRUD 语句,无需修改业务代码,只需在配置中声明逻辑删除规则:

1. 配置(application.yml 示例 )

mybatis-plus:global-config:db-config:logic-delete-field: flag  # 全局逻辑删除实体字段名(需与实体类、数据库字段一致)logic-delete-value: 1    # 逻辑已删除值(如 1 代表“已删除”)logic-not-delete-value: 0# 逻辑未删除值(如 0 代表“未删除”)

2. 效果

  • 删除方法:调用 removeById(1) 时,MyBatis - Plus 自动转为更新语句
UPDATE user SET flag = 1 WHERE id = 1 AND flag = 0;
  • 查询方法:调用 list() 时,自动附加条件
SELECT * FROM user WHERE flag = 0;
  • 无需修改业务代码:Service、Mapper 层调用原方法(如 removelist )即可,底层自动处理逻辑删除规则

虽然 MyBatis - Plus 简化了逻辑删除实现,但存在以下缺陷:

  1. 数据膨胀:未真正删除数据,表中“垃圾数据”会持续积累,长期影响查询效率
  2. 查询性能:所有查询需附加 WHERE deleted = 0 条件,增加 SQL 复杂度

替代方案:若数据不能物理删除,可考虑

  • 数据归档:定期迁移历史数据到归档表
  • 软删除 + 定期清理:结合定时任务,物理删除超期的逻辑删除数据

枚举处理器

像这种字段一般会定义一个枚举,做业务判断的时候就可以直接基于枚举做比较。但是数据库采用的是int类型,对应的PO也是Integer。因此业务操作时必须手动把枚举与Integer转换,非常麻烦;

因此,MybatisPlus提供了一个处理枚举的类型转换器,可以把枚举类型与数据库类型自动转换。

定义状态枚举类

定义一个枚举类来表示 status 字段的含义,清晰管理状态值和对应的描述。比如:

import lombok.Getter;// 定义用户状态枚举
@Getter
public enum UserStatusEnum {NORMAL(1, "正常"),FROZEN(2, "冻结");@EnumValueprivate final int value;@JsonValueprivate final String desc;UserStatus(int value, String desc) {this.value = value;this.desc = desc;}
}

配置枚举类型处理器

mybatis:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler 

然后把类型改为UserStatusEnum

这样就可以实现转换了

后端给前端默认返回枚举变量的名称,即NORMALFREEZE。可以在字段上加上@JsonValue注解,使后端返回对应的枚举变量的字段,此处返回正常冻结

JASON处理器

MybatisPlus提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理JSON就可以使用JacksonTypeHandler处理器来转换实体类

实体类上面要加上:

@TableName(value = "user", autoResultMap = true)

字段上面要加上:

@TableField(typeHandler = JacksonTypeHandler.class)

作用:将 JSON 数据结构化,方便业务代码操作,避免直接处理 JSON 字符串

插件功能

MybatisPlus提供了很多的插件功能,进一步拓展其功能。目前已有的插件有:

  • PaginationInnerInterceptor:自动分页
  • TenantLineInnerInterceptor:多租户
  • DynamicTableNameInnerInterceptor:动态表名
  • OptimisticLockerInnerInterceptor:乐观锁
  • IllegalSQLInnerInterceptor:sql 性能规范
  • BlockAttackInnerInterceptor:防止全表更新与删除

注意: 使用多个分页插件的时候需要注意插件定义顺序,建议使用顺序如下:

  • 多租户,动态表名
  • 分页,乐观锁
  • sql 性能规范,防止全表更新与删除
http://www.dtcms.com/a/302907.html

相关文章:

  • 解决Echarts设置宽度为100%发现宽度变为100px的问题
  • 5 分钟上手 Firecrawl
  • [免费]【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts)【论文+源码+SQL脚本】
  • Android中PID与UID的区别和联系(2)
  • Effective C++ 条款07:为多态基类声明virtual析构函数
  • 《深入理解priority_queue:的使用与模拟实现》
  • 教资科三【信息技术】— 学科知识: 第四章(计算机网络技术)
  • 如何在FastAPI中玩转Schema版本管理和灰度发布?
  • 【深度学习】SOFT Top-k:用最优传输解锁可微的 Top-k 操作
  • (二)Eshop(RabbitMQ手动)
  • 如何 5 分钟给英语视频加上中文字幕?
  • 2025.7.28总结
  • 学术论文写作心得笔记:如何避免“论文像实验报告”
  • 关于sql面试积累
  • [Linux]线程池
  • 【深度学习新浪潮】基于文字生成3D城市景观的算法有哪些?
  • 前端实现PDF在线预览的8种技术方案对比与实战
  • 软件设计师-知识点记录
  • WAIC 2025深度解析:当“养虎”警示遇上机器人拳击赛
  • 构建你的专属区块链:深入了解 Polkadot SDK
  • Java序列化与反序列化
  • 从零开始学习Dify-基于MCP的智能旅行规划助手下(九)
  • 02_FOC学习之-闭环位置控制
  • #Datawhale 组队学习#强化学习Task5
  • C# 基于halcon的视觉工作流-章24-矩形查找
  • SpringBoot数学实例:高等数学实战
  • 学习嵌入式的第三十四天-数据结构-(2025.7.28)数据库
  • Linux选择题2
  • Leaflet简介、初步了解
  • 分布式IO详解:2025年分布式无线远程IO采集控制方案选型指南