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:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\entity]
    创建目录: [E:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\controller]
    创建目录: [E:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\mapper]
    创建目录: [E:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\service\impl]
    模板:/templates/entity.java.ftl;  文件:E:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\entity\TbUser.java
    模板:/templates/mapper.java.ftl;  文件:E:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\mapper\TbUserMapper.java
    模板:/templates/service.java.ftl;  文件:E:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\service\ITbUserService.java
    模板:/templates/serviceImpl.java.ftl;  文件:E:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\service\impl\TbUserServiceImpl.java
    模板:/templates/controller.java.ftl;  文件:E:\Develop\workspace\IDEA_2020\studyInLaGou\mybatisplus/src/main/java\com\lagou\mp\generator\user\controller\TbUserController.java
    21:56:38.532 [main] DEBUG com.baomidou.mybatisplus.generator.AutoGenerator - ==========================文件生成完成!!!==========================
    
    

参考资料

posted @ 2020-11-22 22:10  流星<。)#)))≦  阅读(138)  评论(0编辑  收藏  举报