Appearance
MyBatis-Plus 高级特性
涵盖防全表更新/删除、枚举处理、JSON 序列化、字段安全等高级特性。
一、防全表更新/删除
问题: 不小心执行了不带 WHERE 条件的 UPDATE 或 DELETE,导致全表被修改或清空。
java
// 危险操作!
userMapper.delete(null); // 没有 WHERE 条件 → 全表删除
userMapper.update(user, null); // 没有 WHERE 条件 → 全表更新解决方案:
java
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防全表更新/删除(生产环境强烈建议开启)
BlockAttackInnerInterceptor blockAttack = new BlockAttackInnerInterceptor();
interceptor.addInnerInterceptor(blockAttack);
return interceptor;
}
}java
// 开启后,不带 WHERE 条件的操作会报错
userMapper.delete(null);
// 抛异常:Prohibition of table update operation
userMapper.update(user, null);
// 抛异常:Prohibition of full table deletion
// 必须带条件
userMapper.delete(new LambdaQueryWrapper<User>().eq(User::getId, 1L)); // OK二、枚举处理
2.1 通用枚举
java
// 定义枚举
public enum GenderEnum implements IEnum<Integer> {
MALE(1, "男"),
FEMALE(2, "女");
private final int value;
private final String desc;
GenderEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return this.value;
}
}java
// 实体类
@Data
public class User {
private Long id;
private String name;
private GenderEnum gender; // 枚举字段
}yaml
# 配置枚举包扫描
mybatis-plus:
type-enums-package: com.example.enums2.2 @EnumValue 注解
java
public enum GenderEnum {
MALE(1, "男"),
FEMALE(2, "女");
@EnumValue // 标记数据库存的值
private final int code;
@JsonValue // 标记 JSON 序列化的值
private final String desc;
GenderEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}三、JSON 字段处理
java
@Data
@TableName(value = "user", autoResultMap = true) // 必须开启 autoResultMap
public class User {
private Long id;
private String name;
// JSON 字段
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> hobbies; // 数据库存 JSON 数组:["篮球","足球"]
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> extra; // 数据库存 JSON 对象:{"key":"value"}
}java
// 使用
User user = new User();
user.setName("张三");
user.setHobbies(Arrays.asList("篮球", "足球"));
Map<String, Object> extra = new HashMap<>();
extra.put("height", 180);
extra.put("weight", 75);
user.setExtra(extra);
userMapper.insert(user);
// 数据库:hobbies = ["篮球","足球"], extra = {"height":180,"weight":75}四、字段安全策略
java
// FieldStrategy 控制字段的 WHERE 和 SET 行为
public enum FieldStrategy {
IGNORED, // 忽略判断(null 也更新)
NOT_NULL, // 非 null 判断(默认)
NOT_EMPTY, // 非空判断(null 和 "" 都不更新)
NEVER, // 不更新
DEFAULT // 跟随全局
}java
@Data
public class User {
@TableField(strategy = FieldStrategy.NOT_NULL) // 默认
private String name;
@TableField(strategy = FieldStrategy.NOT_EMPTY) // 空字符串也不更新
private String email;
@TableField(strategy = FieldStrategy.IGNORED) // null 也更新
private Integer age;
@TableField(updateStrategy = FieldStrategy.NEVER) // 永不更新
private String createBy;
}五、自定义 SQL 注入器
java
// 自定义全局方法
public class DeleteAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass,
Class<?> modelClass, TableInfo tableInfo) {
String sql = "DELETE FROM " + tableInfo.getTableName();
String method = "deleteAll";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addDeleteMappedStatement(mapperClass, method, sqlSource);
}
}java
@Component
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Configuration configuration,
Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> list = super.getMethodList(configuration, mapperClass, tableInfo);
list.add(new DeleteAll());
return list;
}
}
// 自定义 BaseMapper
public interface MyBaseMapper<T> extends BaseMapper<T> {
int deleteAll();
}六、数据权限插件
java
@Component
public class DataPermissionInterceptor extends JsqlParserSupport
implements InnerInterceptor {
@Override
public void beforeQuery(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
// 获取当前用户
LoginUser currentUser = SecurityUtils.getCurrentUser();
if (currentUser == null || currentUser.isAdmin()) {
return; // 管理员不拦截
}
// 解析 SQL
CCJSqlParserUtil.parse(boundSql.getSql());
// 在 WHERE 条件中添加数据权限过滤
// 例如:AND create_by = 'currentUserId'
}
}七、速记总结
高级特性速记:
防全表更新 → BlockAttackInnerInterceptor
枚举处理 → IEnum 接口 + @EnumValue
JSON 字段 → JacksonTypeHandler + autoResultMap
字段安全策略 → FieldStrategy.NOT_NULL/NOT_EMPTY
自定义方法 → 继承 DefaultSqlInjector
数据权限 → 自定义 InnerInterceptor
速记口诀:
防全表更新加拦截,枚举实现 IEnum,
JSON 字段 TypeHandler,安全策略控更新,
自定义方法注入器,数据权限拦截里。八、面试要点
| 问题 | 答案要点 |
|---|---|
| 怎么防止全表更新/删除? | BlockAttackInnerInterceptor 拦截不带 WHERE 的操作 |
| 枚举怎么处理? | IEnum 接口 + @EnumValue 注解 |
| JSON 字段怎么存? | JacksonTypeHandler + autoResultMap = true |
| 字段安全策略? | FieldStrategy 控制 null 值是否更新 |
