• 20201122 MyBatis-Plus


    MyBatis-Plus

    MyBatis-Plus 概念

    官网

    MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生

    特性

    • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
    • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

    对于 MyBatis 整合 MP 有常常有三种用法,分别是 MyBatis + MP、Spring + MyBatis + MP、Spring
    Boot + MyBatis + MP。

    SpringBoot 整合 MP

    1. 准备数据

      -- 创建测试表
      DROP TABLE IF EXISTS tb_user;
      CREATE TABLE user
      (
      id BIGINT(20) NOT NULL COMMENT '主键ID',
      name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
      age INT(11) NULL DEFAULT NULL COMMENT '年龄',
      email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
      PRIMARY KEY (id)
      );
      -- 插入测试数据
      INSERT INTO user (id, name, age, email) VALUES
      (1, 'Jone', 18, 'test1@baomidou.com'),
      (2, 'Jack', 20, 'test2@baomidou.com'),
      (3, 'Tom', 28, 'test3@baomidou.com'),
      (4, 'Sandy', 21, 'test4@baomidou.com'),
      (5, 'Billie', 24, 'test5@baomidou.com');
      
    2. 增加依赖

      		<dependency>
      			<groupId>com.baomidou</groupId>
      			<artifactId>mybatis-plus-boot-starter</artifactId>
      			<version>3.4.0</version>
      		</dependency>
      
    3. application.properties

      spring.datasource.driver-class-name=com.mysql.jdbc.Driver
      spring.datasource.url=jdbc:mysql://192.168.181.130:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
      spring.datasource.username=root
      spring.datasource.password=123456
      
    4. 实体类

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class User {
          private Long id;
          private String name;
          private Integer age;
          private String email;
      }
      
    5. Mapper 接口

      package com.lagou.mybatisplus.dao;
      
      public interface UserMapper extends BaseMapper<User> {
      }
      
    6. SpringBoot 启动类

      @MapperScan("com.lagou.mybatisplus.dao") //设置mapper接口的扫描包
      @SpringBootApplication
      public class MybatisplusApplication {
      
      	public static void main(String[] args) {
      		SpringApplication.run(MybatisplusApplication.class, args);
      	}
      
      }
      
    7. 测试类

      @SpringBootTest
      class MybatisplusApplicationTests {
      
          @Autowired
          private UserMapper userMapper;
      
          @Test
          public void testSelectList() {
              List<User> userList = userMapper.selectList(null);
              for (User user : userList) {
                  System.out.println(user);
              }
          }
      
          @Test
          public void testSelectById() {
              //根据id查询数据
              User user = this.userMapper.selectById(2L);
              System.out.println("result = " + user);
          }
      
          @Test
          public void testSelectBatchIds() {
              //根据id集合批量查询
              List<User> users = this.userMapper.selectBatchIds(Arrays.asList(2L, 3L, 10L));
              for (User user : users) {
                  System.out.println(user);
              }
          }
      
          @Test
          public void testSelectOne() {
              QueryWrapper<User> wrapper = new QueryWrapper<>();
              wrapper.eq("name", "jack");
              //根据条件查询一条数据,如果结果超过一条会报错
              User user = this.userMapper.selectOne(wrapper);
              System.out.println(user);
          }
      
          @Test
          public void testSelectCount() {
              QueryWrapper<User> wrapper = new QueryWrapper<>();
              wrapper.gt("age", 23); //年龄大于23岁
              //根据条件查询数据条数
              Integer count = this.userMapper.selectCount(wrapper);
              System.out.println("count = " + count);
          }
      
          @Test
          public void testSelectPage() {
              QueryWrapper<User> wrapper = new QueryWrapper<>();
              wrapper.gt("age", 20); //年龄大于20岁
              Page<User> page = new Page<>(1, 2);
              //根据条件查询数据
              IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
              System.out.println("数据总条数:" + iPage.getTotal());
              System.out.println("总页数:" + iPage.getPages());
              List<User> users = iPage.getRecords();
              for (User user : users) {
                  System.out.println("user = " + user);
              }
          }
      
          @Test
          public void testInsert() {
              User user = new User();
              user.setAge(18);
              user.setMail("test@lagou.cn");
              user.setName("子慕");
              //返回的result是受影响的行数,并不是自增后的id
              int result = userMapper.insert(user);
              System.out.println(result);
              System.out.println(user.getId());
          }
      
          /**
           * 根据id更新
           */
          @Test
          public void testUpdateById() {
              User user = new User();
              user.setId(6L); //主键
              user.setAge(21); //更新的字段
              //根据id更新,更新不为null的字段
              this.userMapper.updateById(user);
          }
      
          /**
           * 根据条件更新
           */
          @Test
          public void testUpdateByQueryWrapper() {
              User user = new User();
              user.setAge(22); //更新的字段
              //更新的条件
              QueryWrapper<User> wrapper = new QueryWrapper<>();
              wrapper.eq("id", 6);
      
              //执行更新操作
              int result = this.userMapper.update(user, wrapper);
              System.out.println("result = " + result);
          }
      
          @Test
          public void testUpdateByUpdateWrapper() {
              //更新的条件以及字段
              UpdateWrapper<User> wrapper = new UpdateWrapper<>();
              wrapper.eq("id", 6).set("age", 23);
              //执行更新操作
              int result = this.userMapper.update(null, wrapper);
              System.out.println("result = " + result);
          }
      
      
          @Test
          public void testDeleteById() {
              //执行删除操作
              int result = this.userMapper.deleteById(6L);
              System.out.println("result = " + result);
          }
      
          @Test
          public void testDeleteByMap() {
              Map<String, Object> columnMap = new HashMap<>();
              columnMap.put("age", 21);
              columnMap.put("name", "子慕");
              //将columnMap中的元素设置为删除的条件,多个之间为and关系
              int result = this.userMapper.deleteByMap(columnMap);
              System.out.println("result = " + result);
          }
      
      
          @Test
          public void testDeleteByQueryWrapper() {
              User user = new User();
              user.setAge(20);
              user.setName("子慕");
              //将实体对象进行包装,包装为操作条件
              QueryWrapper<User> wrapper = new QueryWrapper<>(user);
              int result = this.userMapper.delete(wrapper);
              System.out.println("result = " + result);
          }
      
          @Test
          public void testDeleteByBatchIds() {
              //根据id集合批量删除
              int result = this.userMapper.deleteBatchIds(Arrays.asList(1L, 10L, 20L));
              System.out.println("result = " + result);
          }
      }
      
      

    设置 id 的生成策略

    • 参考 com.baomidou.mybatisplus.annotation.IdType
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @TableName("tb_user") // 指定实体类对应的表名
    public class User {
        @TableId(type = IdType.AUTO) //指定id类型为自增长,需要表设置主键自增
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    

    @TableField

    在MP中通过 @TableField 注解可以指定字段的一些属性,常常解决的问题有2个:

    1. 对象中的属性名和字段名不一致的问题(非驼峰)
    2. 对象中的属性字段在表中不存在的问题
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @TableName("tb_user") // 指定实体类对应的表名
    public class User {
        @TableId(type = IdType.AUTO) //指定id类型为自增长
        private Long id;
    
        @TableField(select = false) // 查询时不返回
        private String name;
        private Integer age;
    
        @TableField("email") // 解决字段名称不一致
        private String mail;
    
        @TableField(exist = false) // 解决字段在表中不存在
        private String address;
    }
    
    

    分页功能

    增加配置:

    @Configuration
    @MapperScan("com.lagou.mybatisplus.dao") //设置mapper接口的扫描包
    public class MyBatisPlusConfig {
        /**
         * 分页插件
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            return mybatisPlusInterceptor;
        }
        
    }
    

    测试功能:

        @Test
        public void testSelectPage() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.gt("age", 20); //年龄大于20岁
            Page<User> page = new Page<>(1, 2);
            //根据条件查询数据
            IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
            System.out.println("数据总条数:" + iPage.getTotal());
            System.out.println("总页数:" + iPage.getPages());
            List<User> users = iPage.getRecords();
            for (User user : users) {
                System.out.println("user = " + user);
            }
        }
    

    SQL注入的原理

    • MP 在启动后会将 BaseMapper 中的一系列的方法注册到 mappedStatements
    • 在 MP 中,ISqlInjector 负责 SQL 的注入工作,它是一个接口,AbstractSqlInjector 是它的实现类

    配置

    ## MyBatis 配置文件位置,如果有单独的 MyBatis 配置,请将其路径配置到 configLocation 中
    mybatis-plus.config-location = classpath:mybatis-config.xml
    
    ## MyBatis Mapper 所对应的 XML 文件位置,如果您在 Mapper 中有自定义方法(XML 中有自定义实现),需要进行该配置,告诉 Mapper 所对应的 XML 文件位置
    mybatis-plus.mapper-locations = classpath*:mybatis/*.xml
    
    ## MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 文件中可以直接使用类名,而不用使用全限定的类名(即 XML 中调用的时候不用包含包名)
    mybatis-plus.type-aliases-package = com.lagou.mp.pojo
    
    ## 关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
    mybatis-plus.configuration.map-underscore-to-camel-case=false
    
    ## 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。
    mybatis-plus.configuration.cache-enabled=false
    
    ## 全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。
    mybatis-plus.global-config.db-config.id-type=auto
    
    ## 表名前缀,全局配置后可省略@TableName()配置。
    mybatis-plus.global-config.db-config.table-prefix=tb_
    

    条件构造器

    • com.baomidou.mybatisplus.core.conditions.Wrapper
    • QueryWrapper(LambdaQueryWrapper)UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件,注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
    @SpringBootTest
    public class MPWrapperTest {
    
        @Autowired
        private UserMapper userMapper;
    
    
        @Test
        public void testWrapperllEq() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            //设置条件
            Map<String, Object> params = new HashMap<>();
            params.put("name", "jack");
            params.put("age", "20");
            // wrapper.allEq(params);// SELECT id,age,email AS mail FROM tb_user WHERE (name = ? AND age = ?)
            wrapper.allEq(params, false); // SELECT id,age,email AS mail FROM tb_user WHERE (name = ? AND age = ?)
            wrapper.allEq((k, v) -> (k.equals("name") || k.equals("email")), params);// SELECT id,age,email AS mail FROM tb_user WHERE (name = ? AND age = ? AND name = ? AND age = ?)
            List<User> users = this.userMapper.selectList(wrapper);
            for (User user : users) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testWrapperCompare() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            // (email = ? AND age >= ? AND name IN (?,?,?))
            wrapper.eq("email", "test2@baomidou.com").ge("age", 20).in("name", "jack", "jone", "tom");
            List<User> users = this.userMapper.selectList(wrapper);
            for (User user : users) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testWrapperLike() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            // (name LIKE ?)
            // Parameters: %子%(String)
            wrapper.like("name", "子");
            List<User> users = this.userMapper.selectList(wrapper);
            for (User user : users) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testWrapperOrderBy() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            // ORDER BY age DESC
            wrapper.orderByDesc("age");
            List<User> users = this.userMapper.selectList(wrapper);
            for (User user : users) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testWrapperLogic() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            // (name = ? OR age = ?)
            wrapper.eq("name","jack").or().eq("age", 24);
            List<User> users = this.userMapper.selectList(wrapper);
            for (User user : users) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testWrapperSelect() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            // SELECT id,name,age FROM tb_user WHERE (name = ? OR age = ?)
            wrapper.eq("name", "jack")
                   .or()
                   .eq("age", 24)
                   .select("id", "name", "age");
            List<User> users = this.userMapper.selectList(wrapper);
            for (User user : users) {
                System.out.println(user);
            }
        }
    }
    

    ActiveRecord

    ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。

    ActiveRecord的主要思想是:

    • 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field;
    • ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即 CURD;
    • ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑;

    使用步骤

    1. 实体类,继承 com.baomidou.mybatisplus.extension.activerecord.Model

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      @TableName("tb_user")
      public class ARUser extends Model<ARUser> {
          private Long id;
          private String name;
          private Integer age;
          private String email;
      }
      
    2. Mapper 接口实现 com.baomidou.mybatisplus.core.mapper.BaseMapper

      public interface ARUserMapper extends BaseMapper<ARUser> {
      }
      
    3. 测试功能

      @SpringBootTest
      public class MPARTest {
      
          @Test
          public void testAR() {
              ARUser user = new ARUser();
              user.setId(2L);
      
              // SELECT id,name,age,email FROM tb_user WHERE id=?
              ARUser user2 = user.selectById();
              System.out.println(user2);
          }
      }
      

    插件

    MP 执行分析插件

    • 攻击 SQL 阻断解析器,防止全表更新与删除
    • 在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作
    • 注意:该插件仅适用于开发环境,不适用于生产环境

    配置插件:

    @Configuration
    @MapperScan("com.lagou.mybatisplus.dao") //设置mapper接口的扫描包
    public class MyBatisPlusConfig {
        /**
         * 分页插件
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            // 分页拦截器
            mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            // 攻击 SQL 阻断解析器,防止全表更新与删除
            mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
            return mybatisPlusInterceptor;
        }
    
    
    }
    

    测试功能:

        @Test
        public void testBlockAttackInnerInterceptor() {
            userMapper.delete(null);
    
            // org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
            // ### Error updating database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of full table deletion
        }
    

    性能分析插件

    官方文档

    • 性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常。
    • 该插件只用于开发环境,不建议生产环境使用。
    • MP 在 3.2 的版本已经移除了这个性能分析插件并推荐使用第三方插件。
    使用步骤
    1. 添加依赖

      		<dependency>
      			<groupId>p6spy</groupId>
      			<artifactId>p6spy</artifactId>
      			<version>3.9.1</version>
      		</dependency>
      
    2. 修改配置,数据库连接信息

      spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
      spring.datasource.url=jdbc:p6spy:mysql://192.168.181.130:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
      
    3. 增加 p6spy 配置,spy.properties

      #3.2.1以上使用
      modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
      #3.2.1以下使用或者不配置
      #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
      # 自定义日志打印
      logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
      #日志输出到控制台
      appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
      # 使用日志系统记录 sql
      #appender=com.p6spy.engine.spy.appender.Slf4JLogger
      # 设置 p6spy driver 代理
      deregisterdrivers=true
      # 取消JDBC URL前缀
      useprefix=true
      # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
      excludecategories=info,debug,result,commit,resultset
      # 日期格式
      dateformat=yyyy-MM-dd HH:mm:ss
      # 实际驱动可多个
      #driverlist=org.h2.Driver
      # 是否开启慢SQL记录
      outagedetection=true
      # 慢SQL记录标准 2 秒
      outagedetectioninterval=2
      
    4. 测试使用,打印日志如下

       Consume Time:2 ms 2020-11-22 20:40:54
       Execute SQL:SELECT id,age,email AS mail FROM tb_user
      

    乐观锁插件

    乐观锁实现方式:

    • 取出记录时,获取当前version
    • 更新时,带上这个version
    • 执行更新时, set version = newVersion where version = oldVersion
    • 如果version不对,就更新失败
    1. 表中增加字段 version
    ALTER TABLE `tb_user` ADD COLUMN `version` int(10) NULL AFTER `email`;
    UPDATE `tb_user` SET `version`='1';
    
    1. 实体类增加属性 version,注解 @Version
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @TableName("tb_user") // 指定实体类对应的表名
    public class User {
        @TableId(type = IdType.AUTO) //指定id类型为自增长
        private Long id;
    
        @TableField(select = false) // 查询时不返回
        private String name;
        private Integer age;
    
        @TableField("email") // 解决字段名称不一致
        private String mail;
    
        @TableField(exist = false) // 解决字段在表中不存在
        private String address;
    
        @Version
        private Integer version;
    }
    
    1. 测试功能
        /**
         * 测试乐观锁
         */
        @Test
        public void testUpdateByIdVersion() {
            User user = new User();
            user.setId(7L); //主键
            user.setAge(21); //更新的字段
            user.setVersion(1);
            // UPDATE tb_user SET age=21, version=2 WHERE id=7 AND version=1
            int rowCount = this.userMapper.updateById(user);
            System.out.println(rowCount);
        }
    

    Sql 注入器

    • 在 MP 中,通过 AbstractSqlInjectorBaseMapper 中的方法注入到了 MyBatis 容器
    • 扩充 BaseMapper 中的方法,以扩展 findAll 方法为例

    使用步骤

    1. MyBaseMapper ,扩展 BaseMapper

      public interface MyBaseMapper<T> extends BaseMapper<T> {
          List<T> findAll();
      }
      
    2. Mapper 继承 MyBaseMapper

      public interface UserMapper extends MyBaseMapper<User> {
      }
      
    3. 自定义 MySqlInjector ,扩展 DefaultSqlInjector,并注册到 Spring 容器

      public class MySqlInjector extends DefaultSqlInjector {
          @Override
          public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
              List<AbstractMethod> methodList = super.getMethodList(mapperClass);
              // 扩充自定义的方法
              methodList.add(new FindAll());
              return methodList;
          }
      }
      
          /**
           * 自定义SQL注入器
           */
          @Bean
          public MySqlInjector mySqlInjector() {
              return new MySqlInjector();
          }
      
    4. FindAll,自定义 AbstractMethod

      public class FindAll extends AbstractMethod {
          @Override
          public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
              String sqlMethod = "findAll";
              String sql = "select * from " + tableInfo.getTableName();
              // SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
              SqlSource sqlSource = new RawSqlSource(configuration, String.format(sql, sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true)), Object.class);
              return this.addSelectMappedStatementForTable(mapperClass, sqlMethod, sqlSource, tableInfo);
          }
      }
      
      
    5. 功能测试

          /**
           * 测试 SQL 注入器,findAll
           */
          @Test
          public void testFindAll() {
              List<User> userList = userMapper.findAll();
              for (User user : userList) {
                  System.out.println(user);
              }
          }
      

    自动填充功能

    • 有些时候我们可能会有这样的需求,插入或者更新数据时,希望有些字段可以自动填充数据,比如密码、version 等。在MP中提供了这样的功能,可以实现自动填充。
    1. 实体类添加注解,@TableField(fill = FieldFill.INSERT)

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      @TableName("tb_user") // 指定实体类对应的表名
      public class User {
          @TableId(type = IdType.AUTO) //指定id类型为自增长
          private Long id;
      
          @TableField(select = false) // 查询时不返回
          private String name;
      
          @TableField(fill = FieldFill.INSERT) // 插入时自动填充
          private Integer age;
      
          @TableField(value = "email") // 解决字段名称不一致
          private String mail;
      
          @TableField(exist = false) // 解决字段在表中不存在
          private String address;
      
          @Version
          private Integer version;
      }
      
    2. MyMetaObjectHandler,实现 MetaObjectHandler,并注册到 Spring 容器中

      public class MyMetaObjectHandler implements MetaObjectHandler {
          @Override
          public void insertFill(MetaObject metaObject) {
              Object password = getFieldValByName("age", metaObject);
              if (null == password) {
                  //字段为空,可以进行填充
                  setFieldValByName("age", 18, metaObject);
              }
          }
      
          @Override
          public void updateFill(MetaObject metaObject) {
      
          }
      
      }
      
          /**
           * 元对象字段填充控制器抽象类,实现公共字段自动写入
           * @return
           */
          @Bean
          public MyMetaObjectHandler myMetaObjectHandler() {
              return new MyMetaObjectHandler();
          }
      
    3. 测试功能

        /**
         * 测试自动填充功能
         */
        @Test
        public void testInsertAuto() {
            User user = new User();
            user.setMail("test@lagou.cn");
            user.setName("子慕xx");
            // INSERT INTO tb_user ( name, age, email ) VALUES ( '子慕xx', 18, 'test@lagou.cn' )
            int result = userMapper.insert(user);
            System.out.println(result);
            System.out.println(user.getId());
        }
    

    逻辑删除

    1. 修改表结构,增加字段 deleted
    ALTER TABLE `tb_user`
    ADD COLUMN `deleted` int(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER `version`;
    
    1. 增加配置

      # 逻辑已删除值(默认为 1)
      mybatis-plus.global-config.db-config.logic-delete-value=1
      # 逻辑未删除值(默认为 0)
      mybatis-plus.global-config.db-config.logic-not-delete-value=0
      
    2. 测试功能,打印日志如下

    删除日志:
    UPDATE tb_user SET deleted=1 WHERE id=2 AND deleted=0
    
    查询日志:
    SELECT id,age,email AS mail,version,deleted FROM tb_user WHERE deleted=0
    

    代码生成器

    1. 添加依赖

      		<dependency>
      			<groupId>com.baomidou</groupId>
      			<artifactId>mybatis-plus-generator</artifactId>
      			<version>3.4.0</version>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-freemarker</artifactId>
      		</dependency>
      
    2. 执行主类,生成文件

      import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
      import com.baomidou.mybatisplus.core.toolkit.StringPool;
      import com.baomidou.mybatisplus.core.toolkit.StringUtils;
      import com.baomidou.mybatisplus.generator.AutoGenerator;
      import com.baomidou.mybatisplus.generator.InjectionConfig;
      import com.baomidou.mybatisplus.generator.config.*;
      import com.baomidou.mybatisplus.generator.config.po.TableInfo;
      import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
      import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
      
      import java.io.File;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Scanner;
      
      public class MysqlGenerator {
          /**
           * <p>
           * 读取控制台内容
           * </p>
           */
          public static String scanner(String tip) {
              Scanner scanner = new Scanner(System.in);
              StringBuilder help = new StringBuilder();
              help.append("请输入" + tip + ":");
              System.out.println(help.toString());
              if (scanner.hasNext()) {
                  String ipt = scanner.next();
                  if (StringUtils.isNotBlank(ipt)) {
                      return ipt;
                  }
              }
              throw new MybatisPlusException("请输入正确的" + tip + "!");
          }
      
          /**
           * RUN THIS
           */
          public static void main(String[] args) {
              // 代码生成器
              AutoGenerator mpg = new AutoGenerator();
              // 全局配置
              GlobalConfig gc = new GlobalConfig();
              String moduleName = "mybatisplus";
              String projectPath = System.getProperty("user.dir") + File.separator + moduleName;
              gc.setOutputDir(projectPath + "/src/main/java");
              gc.setAuthor("lagou");
              gc.setOpen(false);
              mpg.setGlobalConfig(gc);
      
              // 数据源配置
              DataSourceConfig dsc = new DataSourceConfig();
              dsc.setUrl("jdbc:mysql://192.168.181.130:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false");
              // dsc.setSchemaName("public");
              dsc.setDriverName("com.mysql.jdbc.Driver");
              dsc.setUsername("root");
              dsc.setPassword("123456");
              mpg.setDataSource(dsc);
      
              // 包配置
              PackageConfig pc = new PackageConfig();
              pc.setModuleName(scanner("模块名"));
              pc.setParent("com.lagou.mp.generator");
              mpg.setPackageInfo(pc);
      
              // 自定义配置
              InjectionConfig cfg = new InjectionConfig() {
                  @Override
                  public void initMap() {
                      // to do nothing
                  }
              };
              List<FileOutConfig> focList = new ArrayList<>();
              focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
                  @Override
                  public String outputFile(TableInfo tableInfo) {
                      // 自定义输入文件名称
                      return projectPath + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
                  }
              });
              cfg.setFileOutConfigList(focList);
              mpg.setCfg(cfg);
              mpg.setTemplate(new TemplateConfig().setXml(null));
              // 策略配置
              StrategyConfig strategy = new StrategyConfig();
              strategy.setNaming(NamingStrategy.underline_to_camel);
              strategy.setColumnNaming(NamingStrategy.underline_to_camel);
              // 设置实体类的超类
              // strategy.setSuperEntityClass("com.baomidou.mybatisplus.samples.generator.common.BaseEntity");
              strategy.setEntityLombokModel(true);
              // 设置 Controller 的超类
              // strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.com mon.BaseController");
      
              strategy.setInclude(scanner("表名"));
              strategy.setSuperEntityColumns("id");
              strategy.setControllerMappingHyphenStyle(true);
              strategy.setTablePrefix(pc.getModuleName() + "_");
              mpg.setStrategy(strategy);
              // 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
              mpg.setTemplateEngine(new FreemarkerTemplateEngine());
              mpg.execute();
          }
      
      }
      
    3. 确认文件生成

      请输入模块名:
      user
      请输入表名:
      tb_user
      21:56:37.907 [main] DEBUG com.baomidou.mybatisplus.generator.AutoGenerator - ==========================准备生成文件...==========================
      创建目录: [E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratoruserentity]
      创建目录: [E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratorusercontroller]
      创建目录: [E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratorusermapper]
      创建目录: [E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratoruserserviceimpl]
      模板:/templates/entity.java.ftl;  文件:E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratoruserentityTbUser.java
      模板:/templates/mapper.java.ftl;  文件:E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratorusermapperTbUserMapper.java
      模板:/templates/service.java.ftl;  文件:E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratoruserserviceITbUserService.java
      模板:/templates/serviceImpl.java.ftl;  文件:E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratoruserserviceimplTbUserServiceImpl.java
      模板:/templates/controller.java.ftl;  文件:E:DevelopworkspaceIDEA_2020studyInLaGoumybatisplus/src/main/javacomlagoumpgeneratorusercontrollerTbUserController.java
      21:56:38.532 [main] DEBUG com.baomidou.mybatisplus.generator.AutoGenerator - ==========================文件生成完成!!!==========================
      
      

    参考资料

  • 相关阅读:
    让所有IE支持HTML5的解决方案
    MVC3实现多个按钮提交
    水平垂直居中
    模块化开发之sea.js实现原理总结
    express常见获取参数的方法
    使用nodemon提高nodejs调试效率
    Vue2 实践揭秘 错误列表
    全局安装 Vue cli3 和 继续使用 Vue-cli2.x
    使用VUECLI3
    npm ERR! code Z_BUF_ERROR
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/14021836.html
Copyright © 2020-2023  润新知