当前位置: 首页 > 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.lryc.cn/news/602903.html

相关文章:

  • 网络 编程
  • C#_运算符重载 operator
  • Joint.cpp - OpenExo
  • Windows 11 下 Anaconda 命令修复指南及常见问题解决
  • MCP error -32000: Connection closed
  • ESP32学习-按键中断
  • 【unitrix】 6.20 非零整数特质(non_zero.rs)
  • Laravel 分页方案整理
  • 小智源码分析——音频部分(二)
  • 数据开源 | “白虎”数据集首批开源,迈出百万数据征途第一步
  • 阿里云正式开源 LoongSuite:打造 AI 时代的高性能低成本可观测采集套件
  • 自学嵌入式 day36 数据库
  • Java面试宝典:MySQL事务底层和高可用原理
  • MR-link-2:多效性顺式孟德尔随机化分析!
  • <PLC><西门子><modbusTCP>在西门子S7-1200系列PLC中,如何设置modbusTCP通讯?
  • 介绍一下static关键字
  • Go 原理之 GMP 并发调度模型
  • 未授权访问漏洞靶场(redis,MongoDB,Memcached...)
  • 从0到1学PHP(一):PHP 基础入门:开启后端开发之旅
  • 境外期货Level2高频Tick历史行情数据获取与应用指南
  • 墨者:SQL注入实战-MySQL
  • 中兴云电脑W101D2-晶晨S905L3A-2G+8G-安卓9-线刷固件包
  • **线程与进程的区别与联系**
  • Redis 部署模式详解
  • NBIOT模块 BC28通过MQTT协议连接到电信云
  • 【大模型LLM】梯度累积(Gradient Accumulation)原理详解
  • 微服务架构中 gRPC 的应用
  • Rust 最短路径、Tide、Partial、Yew、Leptos、数独实践案例
  • Hugging Face-环境配置
  • 洛谷 P10448 组合型枚举-普及-