框架 - mybatis plus(尚硅谷)

1、mybatis plus简介

  1. MyBatis-Plus(简称MP),是一个MyBatis的增强工具包,只做增强不做改变。为简化开发工作、提高生产率而生。是一个由中国团队开发的工具包。

    MyBatis-Plus提供了通用的mapper和service,可以在不编写任何SQL语句的情况下,快速的实现对单表的CRUD、批量、逻辑删除、分页等操作。

  2. MP核心功能:代码生成器、通用CRUD、条件构造器

  3. springboot集成mybatis-plus

    • 需要导入依赖:其它依赖跟集成mybatis时一样

      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-boot-starter</artifactId>
          <version>3.5.1</version>
      </dependency>
      

      需要注意的是,要根据springboot的版本来选择合适的mybatis_plus版本,否则会有问题。

    • 编写配置:

      # 只需要数据库配置
      spring:
        datasource:
          driver-class-name: com.mysql.cj.jdbc.Driver
          password: ''
          username: 'root'
          url: 'jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC'
      
      # 加入日志:加入以下默认使用的配置,就会使用mybatis默认使用的日志框架,实现日志记录。
      mybatis-plus:
        configuration:
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      
    • 之后就可以直接使用了

  4. springboot中使用MP的相关配置示例

    mybatis-plus:
      # 设置MyBatis-Plus的全局配置
      global-config:
        db-config:
          # 设置实体类所对应的表的统一前缀
          table-prefix: t_
          # 也可以接着设置主键生成策略、字段映射规则等。也就是类似@TableName,@Fieldname等注解的配置
    
  5. spring集成mybatis-plus

    • 需要导入依赖:其它依赖跟集成mybatis时一样

      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus</artifactId>
          <version>3.4.3</version>
      </dependency>
      
    • 先在spring中集成mybatis,在做其它操作

    • Mybatis-Plus 的集成非常简单,对于Spring,我们仅仅需要把Mybatis自带的MybatisSqlSessionFactoryBean替换为 MP 自带的即可。

2、通用CRUD

  1. 提出问题:假设我们已存在一张tbl employee表,且已有对应的实体类Employee。实现employee表的 CRUD操作我们需要做什么呢?

    • 基于Mybatis:需要编写EmployeeMapper接口,并手动编写CRUD 方法,提供 EmployeeMapper.xml映射文件,并手动编写每个方法对应的SQL语句.

    • 基于 MP:只需要创建EmployeeMapper接口,并继承 BaseMapper接口。这就是使用 MP需要完成的所有操作,甚至不需要创建SQL映射文件。

  2. 核心接口:BaseMapper<T> ,泛型指定的就是当前Mapper接口所操作的实体类类型。接口中定义了基本的CRUD操作、通过主键查询、分页查询等等。

  3. 基本的insert操作

    • 定义dao层接口

      // 这里BaseMapper的泛型表示Mapper所操作的实体类类型,之后会根据实体类类名去数据库中找对应的表。
      @Repository("studentDao")
      public interface StudentDao extends BaseMapper<Student> {}
      
    • 实体类

      /**
       * 会根据类名去数据库中找到表,这里如果类名跟对应的数据库表表名不一致的话,
       * 需要使用@TableName来指定表名映射。
       */
      @TableName("students")
      public class Student  {
          private Integer sid;
          private String sname;
          private String cid;
      }
      
    • 然后直接使用StudentDao接口,调用insert方法就可以了

      StudentDao studentDao = applicationContext.getBean("studentDao", StudentDao.class);
      Student student = new Student();
      student.setCid("1001");
      student.setSname("MP");
      
      int insert = studentDao.insert(student);
      
    • @TableName注解:MP默认使用实体类的类名去数据库中查找对应的表,这个注解可以用于指定实体类与数据库表的映射关系

    • @TableId注解:用于指定表主键和实体类字段的映射关系(value属性),以及配置表的主键的更新策略(type参数)。MP默认会将实体类中的id属性映射为表主键字段。下图为type的可选值,也就是可选的主键更新策略。

    • 数据库字段映射:通过实体类的属性名自动映射的,默认开启驼峰匹配等模糊匹配机制。同时也可以使用@TableField注解来指定字段与数据库中表字段的映射关系

    • 获取新插入的数据的主键值(mybatis中也有类似的操作):MP会自动将主键值回显到实体类中,如上述的示例中,主键值就会被存到student对象中。

  4. 基本的update操作

    • 首先依然是要定义dao层接口、以及对应要操作的实体类

    • 然后直接使用StudentDao接口,调用updateById(实体类对象)方法就可以了,没啥打不了的,这里不示例了哈,哈哈哈!!!

      需要注意的是:修改哪些表项是根据实体类的属性值是否为null来判断的,实体类的字段值为null则不修改对应的表项。

  5. 基本的select操作

    • 2.X 版本有以下方法可以用来查询数据:

    • 3.X版本不太一样了:大多数需要使用条件构造器来查询数据了

    • 需要注意的是:这里面的分页查询实际上不是真实的分页查询,依然是查询全部然后再去做相关的操作,效率是不行的,一般分页查询的操作还是需要使用分页插件例如mybatis plus提供的,或者PageHelper插件。

  6. 基本的delete操作:通过id删除、通过Map集合构造删除条件、通过id列表批量删除数据

    • 逻辑删除示例:

      需要在表中添加一个字段,例如isDelete int,表示是否被删除,默认值为0表示未删除。然后在实体类中也定义相同的属性,并使用@TableLogin标注,之后调用delete方法,就会自动变更为update操作,去修改isDelete的值。

  7. MP启动注入SQL原理分析

  8. 如果mybatis_plus提供的操作无法满足需求,就需要自定义一些操作,其实就是使用原来的mybatis,建立mapper.xml文件,编写sql来实现。

    • 以下示例是在springboot中实现的

    • 首先需要配置mapper文件扫描:

      # 如果不配置id话,默认的文件扫描路径也是这个。所以可以选择不配置,使用这个路径即可。
      mybatis-plus:
        mapper-locations: /mapper/**/*.xml
      
    • 然后在/mapper路径下创建mapper.xml文件,然后像原始的mybatis那样去使用即可。

  9. 通用IService

    • IService <实体类> 是 MyBatis-Plus 提供的一个通用 Service 层接口,它封装了常见的 CRUD 操作,包括插入、删除、查询和分页等。通过继承 IService 接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。

    • ServiceImpl是MP提供的Iservice的实现类,实现了部分基本CRUD功能,可以直接使用。

  10. IService使用方式:用于构建service层功能。

    • 首先创建一个service接口,实现IService接口,注意泛型是要操作的实体类对象。

      public interface CarService extends IService<Car> {}
      
    • 创建service接口的实现类,实现前面创建的service接口,并继承ServiceImpl<E, T>类,注意第一个泛型E表示BaseMapper接口,第二个泛型表示要操作的实体类。

      @Service
      public class CarServiceImpl extends ServiceImpl<CarMapper, Car> implements CarService {}
      
    • 然后就可以直接使用service实现类来实现基本的CRUD操作了。

  11. 在java中,一些常量通常使用枚举来表示,例如性别的男和女。

    • 假如数据库中,性别为int类型,1表示男,2表示女

    • 在实体类中,性别字段的值使用枚举来表示

    • 这个时候,如果要插入数据,直接使用insert方法插入数据,由于存在枚举类型的数据,插入之后会报错!!!

    • 修改:

      • 在枚举类中,使用@EnumValue,将注解所标识的属性的值存储到数据库中

        @Getter
        public enum SexEnum {
            MALE(sex:1,sexName:"男"),
        
            FEMALE(sex:2, sexName:"女");
        
            @EnumValue //将注解所标识的属性的值存储到数据库中
            private Integer sex;
            private string sexName;
        }
        
      • 同时还需要在MP的全局配置中,加入枚举的扫描包配置

        # 扫描通用枚举的包
        type-enums-package: com.atguigu.mybatisplus.enums
        
      • 这样,就可以将枚举值存入到数据库中,不会报错。

3、条件构造器EntityWrapper

  1. 提出需求:分页查询 tbl employee表中,年龄在18~50之间性别为男且姓名为 xx 的所有用户,这时候我们该如何实现上述需求呢?

    • MyBatis : 需要在SQL 映射文件中编写带条件查询的SQL,并基于 PageHelper插件完成分页。实现以上一个简单的需求,往往需要我们做很多重复单调的工作。

    • MP:依旧不用编写SQL 语句,MP提供了功能强大的条件构造器EntityWrappert

  2. EntityWrapper简介

    • Mybatis-Plus 通过EntityWrapper(简称EW,MP封装的一个查询条件构造器)或者Condition(与EW类似)来让用户自由的构建查询条件,简单便捷,没有额外的负担,能够有效提高开发效率

    • 实体包装器,主要用于处理sgl拼接,排序,实体参数查询等

    • 注意:使用的是数据库字段,不是Java 属性!

  3. 使用示例:

    // 分页查询 car表中,价格在0~500之间且类型为好车 的所有车
    // 构造条件
    QueryWrapper<Car> queryWrapper = new QueryWrapper<Car>().between("guide_price", 0, 500).eq("car_type", "好车");
    
     // 查询数据:由于暂时没有分页插件,所以其实没有得到分页效果
    // 加入分页插件之后,才能真正实现分页。
    Page page = carDao.selectPage(new Page(2, 2), queryWrapper);
    
    List records = page.getRecords(); //获取查询到的数据
    

    由于暂时没有分页插件,所以其实没有得到分页效果。加入分页插件之后,才能真正实现分页。

  4. 其它的使用构造条件的查询或者修改、删除的操作也是类似的用法,此处略过,基本方法就是使用QueryWrapper构造查询条件,然后将QueryWrapper对象作为方法参数即可。

    注意一个常用方法:last(String sql); 作用是将参数sql拼接在sql语句的最后面。

  5. 条件优先级:体会以下例子

    //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
    QueryWrapper<User>queryWrapper = new QueryWrapper<>();
    
    queryWrapper.like("user_name", "a")
            .and(       //lambda中的条件优先执行
                i->i.gt("age",20).or().isNull("email")
              );
    user user = new User();
    user.setName("小红");
    user.setEmail("test@atguigu.com");
    // 执行修改操作
    int result = userMapper.update(user,queryWrapper);
    
  6. 子查询条件

    //select * from t_car where id in (select id from t_car where id>=50 and id <=100);
    QueryWrapper<Car> queryWrapper = new QueryWrapper<>();
    // 构建子查询条件
    queryWrapper.inSql("id","select id from t_car where id>=50 and id <=100");
    List<Car> cars = carMapper.selectList(queryWrapper);
    System.out.println(cars);
    
  7. 现实的开发中,前端传送过来的数据,需要进一步的判断是否满足条件,例如是否为空,长度是否合适等,这就需要使用条件构造器的另一批方法:

    在带condition参数的方法中,对前端传送的数据进行判断即可

    String username = "";
    // 通过username查询数据
    QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
    // 添加condition条件判断,判断username字段是否非空。
    queryWrapper.eq(StringUtils.isNotBlank(username),"username",username);
    List<Account> accountsccount = carMapper.selectList(queryWrapper);
    
  8. LambdaUpdateWrapperLambdaQueryWrapper,可以防止条件构造器中写字段名的时候写错,使用函数式接口来访问响应的字段

  9. Condition类可以实现与Wrapper一样的效果,用法也差不多啦

4、ActiveRecord(活动记录)

  1. Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。

    ActiveRecord一直广受动态语言(PHP、Ruby等)的喜爱,而Java作为准静态语言,对于 ActiveRecord往往只能感叹其优雅,所以MP也在 AR 道路上进行了一定的探索。

  2. 如何使用AR模式?仅仅需要让实体类继承Model类且实现主键指定方法,即可开启AR 之旅

  3. AR插入操作

    • 创建实体类student:注意同样可以使用@TableName等注解,如果需要的话,实现与之前一样的效果。

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @TableName("students")
      public class Student  extends Model<Student> {
      
          // TableId用于指定主键映射,以及指定主键的更新策略
          private Integer sid;
          private String sname;
          private String cid;
      
          // 重写这个方法,指定哪个值为主键
          @Override
          public Serializable pkVal() {
              return sid;
          }
      }
      
    • 插入操作:

      Student student = new Student(null, "老二","1001");
      boolean insert = student.insert();
      System.out.println(insert);
      
  4. 查询操作、删除操作、修改操作类似,此处略。

  5. AR 模式提供了一种更加便捷的方式实现CRUD操作,其本质还是调用的Mybatis 对应的方法,类似于语法糖。

4、代码生成器

  1. 代码生成器MP提供了大量的自定义设置,生成的代码完全能够满足各类型的需求。MP 的代码生成器和Mybatis MBG代码生成器的区别:

    • MP 的代码生成器都是基于java代码来生成。MBG基于xml文件进行代码生成。

    • MyBatis的代码生成器可生成:实体类、Mapper接口、Mapper 映射文件

    • MP 的代码生成器可生成:实体类(可以选择是否支持AR)、Mapper 接口、Mapper映射
      文件、Service 层、Controller层.

  2. 注意:表及字段命名策略选择

    • 在MP中,建议数据库表名和表字段名采用驼峰命名方式,如果采用下划线命名方式,需要开启全局下划线开关,如果表名字段名命名方式不一致使用注解指定,建议最好保持一致,有助于提高执行效率。

    • 这么做的原因是为了避免在对应实体类时产生的性能损耗,这样字段不用做映射就能直接和实体类对应。当然如果项目里不用考虑这点性能损耗,那么你采用下滑线也是没问题的,只需要在生成代码时配置dbColumnUnderline属性就可以

  3. 集成代码生成器需要的依赖

    模板引擎:MP 的代码生成器默认使用的是Apache的Velocity模板,当然也可以更换为别的模板技术,例如freemarker。需要引入模板引擎依赖,此处使用Velocity模板。

    <!--        需要加入Apache Velocity的依赖-->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.0</version>
    </dependency>
    <!--        加入 slf4j,查看日志输出信息-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.7</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.7</version>
    </dependency>
    
  4. 生成代码:MP2.X版本

    • 代码生成器代码:

      GlobalConfig config = new GlobalConfig();
      //1.全局配置
      GlobalConfig config = new GlobalConfig();
      config.setActiveRecord(true) // 是否支持AR模式
              .setAuthor("weiyunhui") // 作者
              .setOutputDir("D:\\workspace_mp\\mp03\\src\\main\\java") // 生成路径
              .setFile0verride(true) // 文件覆盖
              .setIdType(IdType.AUTO) // 主键策略
              //设置生成的servvice接口的名字的首字母是否为I,默认是IXxxxxService
              .setServiceName("%sService")
              .setBaseResultMap(true)
              .setBaseColumnList(true);
      
      //2.数据源配置
      DataSourceConfig dsConfig = new DataSourceConfig();
      dsConfig.setDbType(DbType.MYSQL) //设置数据库类型
              .setDriverName("com.mysql.jdbc.Driver")
              .setUrl("jdbc:mysql://localhost:3306/mp")
              .setUsername("root")
              .setPassword("1234");
      
      //3.策略配置
      StrategyConfig stConfig = new StrategyConfig();
      stConfig.setCapitalMode(true) //全局大写命名
              .setDbColumnUnderline(true) //指定表名字段名是否使用下划线
              .setNaming(NamingStrategy.underline_to_camnel) // 数据库表映射到实体的命名策略
              .setTablePrefix("tb1_")
              .setInclude("tbl_employee"); //生成的表
      
      //4.包名策略配置
      PackageConfig pkConfig = new PackageConfig();
      pkConfig.setParent("com.atguigu.mp")
              .setMapper("mapper")
              .setService("service")
              .setController("controller")
              .setEntity("beans")
              .setXml("mapper");
      
      //5.整合配置
      AutoGenerator ag = new AutoGenerator();
      ag.setGlobalConfig(config)
          .setDataSource(dsConfig)
          .setStrategy(stConfig)
          .setPackageInfo(pkConfig);
      
      //6.执行
      ag.execute();
      
    • 生成了service层代码,可以看到EmployeeServiceImpl继承了ServiceImpl

      • 在ServiceImpl中已经完成Mapper对象的注入,直接在EmployeeServiceImpl中进行使用

      • 在ServiceImpl中也帮我们提供了常用的CRUD方法,基本的一些CRUD方法在Service中不需要我们自己定义。

  5. 多数据源的配置:包括数据库集群,不同数据库服务器,主从复制等。

5、插件扩展

  1. 插件机制

    • Mybatis 通过插件(Interceptor)可以做到拦截四大对象相关方法的执行,根据需求,完成相关数据的动态改变。

    • 四大对象:

      • Executor
      • StatementHandler
      • ParameterHandler
      • ResultSetHandler
    • 插件原理:

      四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理,从而实现对操作的增强(例如对查询进行分页,就是一种增强 )

  2. 使用分页插件

    • 首先需要将插件注册进来,有三种方式

      • 方式一:在mybatis配置文件中配置

        <plugins>
            <plugin interceptor="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></plugin>
        </plugins>
        
      • 方式二:在spring配置文件中配置

        <!--SqlSessionFactoryBean配置-->
        <bean class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" id="factoryBean">
            <!--        添加插件-->
            <property name="plugins">
                <list>
                    <bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"/>
                </list>
            </property>
            <!--注入mybatis核心配置文件路径-->
            <property name="configLocation" value="mybatis-config.xml"/>
            <!--指定别名包-->
            <property name="typeAliasesPackage" value="shh.pojo"/>
            <!--注入数据源-->
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
      • 方式三:使用配置类注册组件,在配置类中加入以下配置(springboot项目)

        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interieptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
        

      理解:引入插件之后,插件就会去拦截四大对象,对四大对象进行代理,从而达到增强功能的目的。例如这里就是去代理Statament对象,增加分页的功能。

    • 使用插件:直接调用selectPage方法,会发现成功实现了分页。

  3. 执行分析插件

    • SQL 执行分析拦截器,只支持MySQL5.63以上版本。该插件的作用是分析DELETE UPDATE 语句,防止小白+或者恶意进行DELETEUPDATE 全表操作。只建议在开发环境中使用,不建议在生产环境使用。

    • 在插件的底层通过SQL语句分析命令:Explain分析当前的SQL语句,根据结果集中的Extra列来断定当前是否全表操作。

    • 首先需要注册插件:combaomidoumybatisplus plugins SqlExplainInterceptorw

    • 之后执行sql操作的时候,就会自动检测了。

  4. 性能分析插件.

    • 性能分析拦截器,用于输出每条SQL语句项其执行时间,SQL性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题

    • 注册插件:com.baomidou.mybatisplus.plugins.Performanceinterceptor。可以指定超时时间等属性,用来限制sql执行。

    • 之后执行sql操作,就会自动分析sql操作的执行时间,写在日志中。

  5. 乐观锁插件

    • 如果想实现如下需求:当要更新一条记录的时候,希望这条记录没有被别人更新

    • 乐观锁的实现原理:

      • 取出记录时,获取当前version

      • 更新时,带上这个version(2

      • 执行更新时,set version =yourVersion+1 where version = yourVersion,如果 version 不对,就更新失败

    • @Version 用于注解实体字段,必须要有。

      在实体类中必须有一个version版本号属性,并使用@Version标注。数据库表中也需要有这个属性。

    • 首先是注册插件:com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor

    • 之后进行更新等操作的时候,就会自动加锁,也就是去判断version的值,从而保证并发安全。

6、自定义全局操作

  1. 根据Mybatisplus 的AutoSalinjector可以自定义各种你想要的sgl,注入到全局中,相当于自定义Mybatisplus自动注入的方法。

    之前需要在xml 中进行配置的sQL 语句,现在通过扩展AutoSqlinjector在加载mybatis环境时就注入。

    其实说白了就是mybatis-plus支持自定义一些sql操作,而不是只能使用它已经定义好的那些。

  2. 自定义全局操作示例(MP2.X版本):略过

    • 在 Mapper接口中定义相关的CRUD 方法

    • 扩展 AutoSqllnjectorinject方法,实现Mapper接口中方法要注入的 SQL

    • 在MP全局策略中,配置自定义注入器

  3. 自定义全局SQL注入器应用之---逻辑删除(MP2.X版本):略

7、公共字段自动填充

  1. 公共字段自动填充的作用:在进行插入或者更新操作的时候,有一些表字段没有插入或者修改数据,那么希望它被插入默认值,就可以使用公共字段自动填充功能。

  2. 元数据处理器接口:com.baomidou.mybatisplus.mapper.MetaObjectHandler ,实现数据填充的接口

    • insertFill(MetaObject metaObject)方法:用于处理插入操作的字段填充

    • updateFill(MetaObject metaObject)方法;:用于处理更新操作的字段填充

    • metaobject:元对象,是Mybatis提供的一个用于更加方便,更加优雅的访问对象的属性,给对象的属性设置值的一个对象.还会用于包装对象.支持对object 、Map、Collection等对象进行包装。

    • 本质上 metaObject获取对象的属性值或者是给对象的属性设置值,最终是要通过 Reflector反射机制获取到属性的对应方法的Invoker,最终invoke.

  3. 开发步骤

    • 注解标注填充字段:需要在实体类中使用@TableFile(fill = FieldFill.INSERT)来标注需要填充的字段,查看FieldFill

      public enum FieldFill{
          DEFAULT(0,"默认不处理"),
          INSERT(1,"插入填充字段"),
          UPDATE(2,"更新填充字段"),
          INSERT_UPDATE(3,"插入和更新填充字段");
      }
      
    • 自定义公共字段填充处理器:定义要填充的数据。其实就是定义一个类去继承MetaObjectHandler接口,实现insertFill和updateFill方法即可。

    • MP全局注入自定义公共字段填充处理器

    • 之后,对表进行更新和插入的时候,就会依据定义的填充规则,对字段数据进行填充

posted @ 2026-03-10 18:46  哈哈嗨  阅读(47)  评论(0)    收藏  举报