mybatis-plus

概述

特性
  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

入门

依赖
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>....</version>
</dependency>
mapper接口

extends BaseMapper就行

public interface UserMapper extends BaseMapper<User> {
    // 所有的CRUD操作都已经编写完成了
	// 你不需要像以前的配置一大堆文件了!
}

注意点,我们需要在主启动类上去扫描我们的mapper包下的所有接口 @MapperScan("com.kuang.mapper")

测试
@SpringBootTest
class MybatisPlusApplicationTests {
    // 继承了BaseMapper,所有的方法都来自己父类
    // 我们也可以编写自己的扩展方法!
    @Autowired
    private UserMapper userMapper;
    @Test
    void contextLoads() {
    // 参数是一个 Wrapper ,条件构造器,这里我们先不用 null
    // 查询全部用户
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
    }
}

配置日志

mybatis-plus:
	configuration:
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

CRUD

插入
// 测试插入
@Test
public void testInsert(){
    User user = new User();
    user.setName("狂神说Java");
    user.setAge(3);
    user.setEmail("24736743@qq.com");
    int result = userMapper.insert(user); // 帮我们自动生成id
    System.out.println(result); // 受影响的行数
    System.out.println(user); // 发现,id会自动回填
}

数据库插入的id的默认值为:全局的唯一id

主键生成策略

默认 ID_WORKER 全局唯一id

分布式系统唯一id生成:https://www.cnblogs.com/haoxinyue/p/5208136.html

雪花算法: snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为 毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味 着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯 一!

主键自增

  1. 实体类字段上 @TableId(type = IdType.AUTO)

  2. 数据库字段一定要是自增!

其他

public enum IdType {
    AUTO(0), // 数据库id自增
    NONE(1), // 未设置主键
    INPUT(2), // 手动输入
    ID_WORKER(3), // 默认的全局唯一id
    UUID(4), // 全局唯一id uuid
    ID_WORKER_STR(5); //ID_WORKER 字符串表示法
}
更新
// 测试更新
@Test
public void testUpdate(){
    User user = new User();
    // 通过条件自动拼接动态sql
    user.setId(6L);
    user.setName("关注公众号:狂神说");
    user.setAge(18);
    // 注意:updateById 但是参数是一个 对象!
    userMapper.updateById(user);
}	
自动填充

创建时间、修改时间!这些个操作一遍都是自动化完成的,我们不希望手动更新!

方式一:数据库级别(工作中不允许你修改数据库)

1、在表中新增字段 create_time, update_time(设置为自动更新),两个字段默认值都为CURRENT_TIMESTAMP

方式二:代码级别

  1. 删除数据库的默认值、更新操作!

  2. 实体类字段属性上需要增加注解

    // 字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
  3. 编写处理器处理注解

    @Component // 一定不要忘记把处理器加到IOC容器中!
    public class MyMetaObjectHandler implements MetaObjectHandler {
    	// 插入时的填充策略
        @Override
        public void insertFill(MetaObject metaObject) {
            this.setFieldValByName("createTime",new Date(),metaObject);
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
        // 更新时的填充策略
        @Override
        public void updateFill(MetaObject metaObject) {
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    }
    
乐观锁

乐观锁:总是认为不会出现问题,无论干什么都不加锁!出现问题就更新值测试

悲观锁:总是认为会出现问题,无论干什么都加锁!

乐观锁实现方式:

  • 取出记录时,获取当前 version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败
乐观锁:1、先查询,获得版本号 version = 1
-- A
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
-- B 线程抢先完成,这个时候 version = 2,会导致 A 修改失败!
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
测试
  1. 给数据库中增加version字段!,可以设置一个默认值1,更新后数据库中version字段自动加1

  2. 我们实体类加对应的字段

    @Version //乐观锁Version注解
    private Integer version;
    
  3. 注册组件

    // 扫描我们的 mapper 文件夹
    @MapperScan("com.kuang.mapper")
    @EnableTransactionManagement
    @Configuration // 配置类
    public class MyBatisPlusConfig {
    	// 注册乐观锁插件
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        	return new OptimisticLockerInterceptor();
        }
    }
    
  4. 测试

    // 测试乐观锁成功!
    @Test
    public void testOptimisticLocker(){
        // 1、查询用户信息
        User user = userMapper.selectById(1L);
        // 2、修改用户信息
        user.setName("kuangshen");
        user.setEmail("24736743@qq.com");
        // 3、执行更新操作
        userMapper.updateById(user);
    }
    
    // 测试乐观锁失败!多线程下
    @Test
    public void testOptimisticLocker2(){
        // 线程 1
        User user = userMapper.selectById(1L);
        user.setName("kuangshen111");
        user.setEmail("24736743@qq.com");
        // 模拟另外一个线程执行了插队操作
        User user2 = userMapper.selectById(1L);
        user2.setName("kuangshen222");
        user2.setEmail("24736743@qq.com");
        userMapper.updateById(user2);
        // 通过自旋锁来多次尝试提交!
        userMapper.updateById(user); // 如果没有乐观锁就会覆盖插队线程的值!
    }
    
查询
    // 测试查询
    @Test
    public void testSelectById() {
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }

    // 测试批量查询!
    @Test
    public void testSelectByBatchId() {
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        users.forEach(System.out::println);
    }

    // 按条件查询之一使用map操作
    @Test
    public void testSelectByBatchIds() {
        HashMap<String, Object> map = new HashMap<>();
        // 自定义要查询
        map.put("name", "狂神说Java");
        map.put("age", 3);
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }
分页查询
  1. 配置分页拦截器

    // 扫描我们的 mapper 文件夹
    @MapperScan("com.kuang.mapper")
    @EnableTransactionManagement
    @Configuration // 配置类
    public class MyBatisPlusConfig {
    	// 注册乐观锁插件
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        	return new OptimisticLockerInterceptor();
        }
        
        //配置分页插件
        @Bean
    	public PaginationInterceptor paginationInterceptor() {
        	return new PaginationInterceptor();
    	}
    }
    
  2. 测试

    // 测试分页查询
    @Test
    public Page<User> testPage(){
        // 参数一:当前页
        // 参数二:页面大小
        // 使用了分页插件之后,所有的分页操作也变得简单的!
        Page<User> page = new Page<>(2,5);
        userMapper.selectPage(page,null);
        return page;
        //打印数据
        page.getRecords().forEach(System.out::println);
        System.out.println(page.getTotal());
    }
    
删除
// 测试根据 id 删除记录
@Test
public void testDeleteById(){
	userMapper.deleteById(1240620674645544965L);
}

// 通过id批量删除
@Test
public void testDeleteBatchId(){
	userMapper.deleteBatchIds(Arrays.asList(1240620674645544961L,1240620674645544962L));
}

// 通过map删除
@Test
public void testDeleteMap(){
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","狂神说Java");
    userMapper.deleteByMap(map);
}
逻辑删除

物理删除 :从数据库中直接移除

逻辑删除 :再数据库中没有被移除,而是通过一个变量来让他失效! deleted = 0 => deleted = 1

  1. 数据库增加deleted字段,默认为0(未删除)

  2. 实体类中增加属性

    @TableLogic //逻辑删除
    private Integer deleted;
    
  3. 配置

    • 配置bean

      // 逻辑删除组件!
      @Bean
      public ISqlInjector sqlInjector() {
      	return new LogicSqlInjector();
      }
      
    • 配置逻辑删除

      mybatis-plus:
        global-config:
          db-config:
            logic-delete-value: 1 # 逻辑已删除值(默认为 1)
            logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
      
  4. 测试

    //删除之后数据库记录还在,只是deleted字段值变为1,查询是时候就会过滤被默认删除的值
    userMapper.deleteById(1);
    

性能分析插件

作用:性能分析拦截器,用于输出每条 SQL 语句及其执行时间

  1. 导入插件

    //sql执行效率插件
    @Bean
    @Profile({"dev","test"})// 设置 dev test 环境开启,保证我们的效率
    public PerformanceInterceptor performanceInterceptor() {
    	PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        // 设置sql执行的最大时间(毫秒),如果超过了则不执行
    	performanceInterceptor.setMaxTime(100);
        // 是否格式化代码
    	performanceInterceptor.setFormat(true); 
    	return performanceInterceptor;
    }
    

    注意:需在yml或properties中配置环境为dev或者test,这样就不会在正正式环境中启动此插件,以免降低效率

    spring:
      profiles:
        active: dev
    
  2. 测试。sql执行时间超过规定时间就会抛出异常

条件构造器Wrapper

//示例
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("account", user.getAccount()).eq("is_delete", Constants.NOT_DELETE);

代码生成器

public class MyCode {
    public static void main(String[] args) {
        //代码自动生成器对象
        AutoGenerator mpg = new AutoGenerator();

        //配置策略

        //1.全局配置
        GlobalConfig gc = new GlobalConfig();
        //项目路径,可以自己看本项目路径,手动写入
        String projectPath = System.getProperty("user.id");
        gc.setOutputDir(projectPath + "src/main/java");
        gc.setAuthor("jpy");//设置作者
        gc.setOpen(false);
        gc.setFileOverride(false);//是否覆盖原来生成的
        gc.setServiceName("%sService");//去除service的I前缀
        gc.setServiceImplName("%sServiceImpl");
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");

        gc.setIdType(IdType.AUTO);//主键生成策略
        gc.setDateType(DateType.ONLY_DATE);//日期类型
        gc.setSwagger2(true);//swagger文档
        mpg.setGlobalConfig(gc);

        //2.设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("");
        dsc.setDriverName("");
        dsc.setUsername("");
        dsc.setPassword("");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        //3.包的配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("");
        pc.setParent("");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setController("controller");
        mpg.setPackageInfo(pc);

        //4.策略配置
        StrategyConfig sc = new StrategyConfig();
        sc.setInclude("user");//设置要映射的表名,可变长参数
        sc.setNaming(NamingStrategy.underline_to_camel);//表名转驼峰
        sc.setColumnNaming(NamingStrategy.underline_to_camel);//列名转驼峰
        sc.setEntityLombokModel(true);//自动生成lombok
        sc.setLogicDeleteFieldName("deleted");//设置逻辑删除字段

        //自动填充
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
        List<TableFill> tableFills = new ArrayList<>();
        tableFills.add(createTime);
        tableFills.add(updateTime);
        sc.setTableFillList(tableFills);

        //乐观锁
        sc.setVersionFieldName("version");
        sc.setRestControllerStyle(true);
        sc.setControllerMappingHyphenStyle(true);//url下划线,localhost:8080/hello_id_2

        mpg.setStrategy(sc);

        mpg.execute();//执行
    }
}
posted @   jpy  阅读(20)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示