MyBatisPlus 扩展知识(增删改查除外)

MyBatis-Plus 笔记

1. 主键生成策略

在 MyBatis-Plus 中,主键 ID 生成策略由 IdType 枚举类控制,常见的 ID 生成策略如下:

public enum IdType {
    AUTO(0),         // 数据库ID自增
    NONE(1),         // 未设置主键类型
    INPUT(2),        // 手动输入ID
    ASSIGN_ID(3),    // 默认使用雪花算法生成ID
    ASSIGN_UUID(4);  // UUID

    private final int key;

    IdType(int key) {
        this.key = key;
    }
}

默认策略说明

  • AUTO:数据库 ID 自增,适用于 MySQL 主键自增。
  • ASSIGN_ID(默认):MyBatis-Plus 提供的雪花算法(Snowflake)自动生成唯一 ID,适用于分布式系统。
  • ASSIGN_UUID:使用 UUID 作为主键。
  • 默认 ID 生成策略时,数据库表的主键 不需要设置自增

2. 字段自动填充

1. 在实体类字段上添加注解

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

@Data
public class User {

    @TableId(type = IdType.ASSIGN_ID) // 主键 ID 采用 MP 默认的雪花算法
    private Long id;

    private String name;
    private String phone;

    @TableField(fill = FieldFill.INSERT) // 仅在插入时填充
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时都会填充
    private LocalDateTime updateTime;
}

2. 自定义 MetaObjectHandler 处理填充逻辑

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    // 插入时填充(createTime、updateTime)
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    // 更新时填充(仅 updateTime)
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

说明

  • strictInsertFill → 只有 null 时才填充字段,避免手动赋值被覆盖。
  • strictUpdateFill → 仅在 update 时填充 updateTime,不会影响 createTime
  • LocalDateTime.now() → 采用当前系统时间。

3. 乐观锁机制

乐观锁原理

  • 乐观锁假设不会发生并发冲突,仅在更新数据时进行版本号校验。
  • 具体实现:
    1. 读取数据时获取当前 version
    2. 更新时,带上 version 作为条件。
    3. UPDATE table SET ... WHERE id = ? AND version = ?,如果 version 仍然匹配,则更新并 version+1
    4. version 发生变化,说明数据被修改,更新失败。

数据库表添加 version 字段

ALTER TABLE user ADD COLUMN version INT DEFAULT 1;

实体类增加 version 字段

import com.baomidou.mybatisplus.annotation.Version;

public class User {
    @Version
    private Integer version;
}

MyBatis-Plus 乐观锁插件

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

测试多线程环境下的乐观锁

@Test
public void testOptimisticLock(){
    // 线程 1
    User user1 = userMapper.selectById(1L);
    user1.setName("zhangshan");
    userMapper.updateById(user1);

    // 线程 2
    User user2 = userMapper.selectById(1L);
    user2.setName("lisi");
    userMapper.updateById(user2);
}

加锁结果:

  • 加锁后:第二个线程更新 version+1,第一个线程因 version 不匹配更新失败。
  • 不加锁:线程 1 最后执行,覆盖了线程 2 更新的数据。

4. MyBatis-Plus 常用插件

插件配置

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());  // 分页
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 防止全表更新或删除
        interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());  // 防止 SQL 注入
        return interceptor;
    }
}

5. P6Spy 日志格式化

spy.properties 配置

logMessageFormat=warren.reggie.common.P6SpyLogger  
appender=com.p6spy.engine.spy.appender.StdoutLogger  
dateformat=yyyy-MM-dd HH:mm:ss  
outagedetection=true  
outagedetectioninterval=2

自定义日志格式

import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class P6SpyLogger implements MessageFormattingStrategy {
    @Override
    public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) {
        if (sql == null || sql.trim().isEmpty()) return "";
        String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        return String.format("[%s] 耗时: %d ms | 连接: %d | 分类: %s\n执行 SQL: %s;\n",
                currentTime, elapsed, connectionId, category, sql.trim());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值