• 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 - ==========================文件生成完成!!!==========================
      
      

    参考资料

  • 相关阅读:
    VysorPro助手
    Play 2D games on Pixel running Android Nougat (N7.1.2) with Daydream View VR headset
    Play 2D games on Nexus 6P running Android N7.1.1 with Daydream View VR headset
    Native SBS for Android
    ADB和Fastboot最新版的谷歌官方下载链接
    How do I install Daydream on my phone?
    Daydream Controller手柄数据的解析
    蓝牙BLE传输性能及延迟分析
    VR(虚拟现实)开发资源汇总
    Android(Java)控制GPIO的方法及耗时分析
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/14021836.html
Copyright © 2020-2023  润新知