mybatis-plus

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。这是官方给的定义,关于mybatis-plus的更多介绍及特性,可以参考mybatis-plus官网。那么它是怎么增强的呢?其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。

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

image-20200618113129282

1.2 特性

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

1.3 支持数据库

  • mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver 、 presto
  • 达梦数据库 、 虚谷数据库 、 人大金仓数据库

1.4 框架结构

image-20200618113156151


2. 快速入门

创建数据库mybatis_plus,创建user


 
 
  1. DROP TABLE IF EXISTS user;
  2. CREATE TABLE user
  3. (
  4. id BIGINT(20) NOT NULL COMMENT '主键ID',
  5. name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
  6. age INT(11) NULL DEFAULT NULL COMMENT '年龄',
  7. email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
  8. PRIMARY KEY (id)
  9. );
  10. DELETE FROM user;
  11. INSERT INTO user (id, name, age, email) VALUES
  12. (1, 'Jone', 18, 'test1@baomidou.com'),
  13. (2, 'Jack', 20, 'test2@baomidou.com'),
  14. (3, 'Tom', 28, 'test3@baomidou.com'),
  15. (4, 'Sandy', 21, 'test4@baomidou.com'),
  16. (5, 'Billie', 24, 'test5@baomidou.com');

真实开发中,version(乐观锁),deleted(逻辑删除),gmt_create(创建时间),gmt_modified(修改时间)

初始化项目

创建一个springboot项目,导入依赖


 
 
  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.projectlombok</groupId>
  7. <artifactId>lombok</artifactId>
  8. </dependency>
  9. <!-- mybatis-plus -->
  10. <dependency>
  11. <groupId>com.baomidou</groupId>
  12. <artifactId>mybatis-plus-boot-starter</artifactId>
  13. <version>3.0.5</version>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.springframework.boot</groupId>
  17. <artifactId>spring-boot-starter-web</artifactId>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-test</artifactId>
  22. <scope>test</scope>
  23. <exclusions>
  24. <exclusion>
  25. <groupId>org.junit.vintage</groupId>
  26. <artifactId>junit-vintage-engine</artifactId>
  27. </exclusion>
  28. </exclusions>
  29. </dependency>

使用mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus,只要导入mybatis-plus就可以了

编写配置文件,连接数据库


 
 
  1. spring:
  2. datasource:
  3. url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
  4. driver-class-name: com.mysql.cj.jdbc.Driver
  5. username: root
  6. password: shw123zxc

传统方式:pojo-dao(连接mybatis,配置mapper.xml文件)-service-controller

使用了mybatis-plus之后

  • pojo
  • mapper接口
  • 使用

创建实体类


 
 
  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class User implements Serializable {
  5. private Long id;
  6. private String name;
  7. private Integer age;
  8. private String email;
  9. }

编写mapper接口


 
 
  1. /**
  2. * 在对应的mapper上面,继承基本的接口BaseMapper
  3. * 继承的接口的泛型,是要操作的实体类
  4. */
  5. @Repository
  6. public interface UserMapper extends BaseMapper<User> {
  7. }

在主启动类上要添加一个注解@MapperScan("cn.codewei.mapper"),来扫描mapper文件夹


 
 
  1. @SpringBootApplication
  2. @MapperScan("cn.codewei.mapper")
  3. public class MybatisPlusApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(MybatisPlusApplication.class, args);
  6. }
  7. }

测试

image-20200618112800710

UserMapper继承了BaseMapper所有的方法,我们也可以编写自己的扩展方法


 
 
  1. @Autowired
  2. private UserMapper userMapper;
  3. @Test
  4. void contextLoads() {
  5. /**
  6. * 查询全部用户
  7. * 参数是一个Wrapper,条件构造器,这里我们先不用
  8. */
  9. List<User> users = userMapper.selectList(null);
  10. System.out.println(users.size());
  11. for (User user : users) {
  12. System.out.println(user);
  13. }
  14. }

3. 配置日志

我们所有的sql现在是不可见的,我们希望知道它是怎么执行了,所以我们必须要看日志

如果使用log4j或者slf4j,还需要导入其相关的依赖,这里我们使用默认的StdOut控制台输出


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

这样日志就配置好了

image-20200618112931951


4. 插入测试


 
 
  1. @Test
  2. public void addTest(){
  3. User user = new User();
  4. user.setName("codewei");
  5. user.setAge(21);
  6. user.setEmail("634498594@qq.com");
  7. int result = userMapper.insert(user);
  8. System.out.println(result); // 受影响的行数
  9. }

image-20200618112347263

我们并没有指定user的id,发现会自动给我们生成id


5. 主键生成策略

  • UUID,自增ID,雪花算法,redis,zookeeper

5.1 雪花算法

分布式id生成算法的有很多种,Twitter的SnowFlake就是其中经典的一种。

SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

image-20200618112404196

特点

  • 时间位:可以根据时间进行排序,有助于提高查询速度。
  • 机器id位:适用于分布式环境下对多节点的各个节点进行标识,可以具体根据节点数和部署情况设计划分机器位10位长度,如划分5位表示进程位等。
  • 序列号位:是一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号

5.2 主键生成策略简介

在我们的实体类上,对主键可以加上注解@TableId(type = xxx)全局唯一,如


 
 
  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class User implements Serializable {
  5. @TableId(type = IdType.ID_WORKER)
  6. private Long id;
  7. private String name;
  8. private Integer age;
  9. private String email;
  10. }

@TableId(type = IdType.ID_WORKER):默认,全局唯一ID

@TableId(type = IdType.AUTO):主键自增,使用该主键生成策略,数据库对应的字段一定是自增的!!

@TableId(type = IdType.NONE):未设置主键

@TableId(type = IdType.INPUT):手动输入

@TableId(type = IdType.ID_WORKER_STR):ID_WORKER的字符串形式

@TableId(type = IdType.UUID):全局唯一ID


6. 更新操作


 
 
  1. @Test
  2. public void updateTest(){
  3. User user = new User();
  4. // 通过条件自动拼接动态sql
  5. user.setId(1L);
  6. user.setName("啊伟");
  7. int reslut = userMapper.updateById(user); // 参数是一个对象!!
  8. System.out.println(reslut);
  9. }

image-20200618113539132


7. 自动填充处理

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

阿里巴巴开发手册:所有的数据库表都要包含gmt_create,和gmt_modified字段,而且需要自动化

方式一:数据库级别(不建议,工作中不允许修改数据库的)

  1. 在表中新增字段gmt_create,和gmt_modified

    image-20200618114614296

  2. 再次测试插入方法,测试前先把实体类同步

    
       
       
    1. private Date gmtCreate;
    2. private Date gmtModified;
    
       
       
    1. @Test
    2. public void updateTest(){
    3. User user = new User();
    4. user.setId(2L);
    5. user.setName("唐嫣");
    6. user.setAge(19);
    7. int reslut = userMapper.updateById(user);
    8. System.out.println(reslut);
    9. }

    image-20200618115009106

    image-20200618115059198

方式二:代码级别

  1. 删除数据库默认值,和自动更新

    image-20200618115239714

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

    @TableField(fill = FieldFill.INSERT_UPDATE) 在插入和更新的时候,自动填充内容

    @TableField(fill = FieldFill.INSERT) 在插入的时候,自动填充内容

    
       
       
    1. @Data
    2. @AllArgsConstructor
    3. @NoArgsConstructor
    4. public class User implements Serializable {
    5. @TableId(type = IdType.INPUT)
    6. private Long id;
    7. private String name;
    8. private Integer age;
    9. private String email;
    10. @TableField(fill = FieldFill.INSERT) // 在插入的时候,自动填充内容
    11. private Date gmtCreate;
    12. @TableField(fill = FieldFill.INSERT_UPDATE) // 在插入和更新的时候,自动填充内容
    13. private Date gmtModified;
    14. }
  3. 创建一个类,编写处理器,来处理注解

    实现接口MetaObjectHandler,实现其方法

    
       
       
    1. @Component // 一定放入IOC中!!
    2. @Slf4j // 日志
    3. public class MyMetaObjectHandler implements MetaObjectHandler {
    4. @Override
    5. public void insertFill(MetaObject metaObject) {
    6. // 插入时的填充策略
    7. log.info("start insert fill....");
    8. this.setFieldValByName("gmtCreate",new Date(),metaObject); // 在插入时,自动将时间插入进去
    9. this.setFieldValByName("gmtModified",new Date(),metaObject); // 在插入时,自动将更新时间填充
    10. }
    11. @Override
    12. public void updateFill(MetaObject metaObject) {
    13. // 更新时的填充策略
    14. log.info("start update fill....");
    15. this.setFieldValByName("gmtModified",new Date(),metaObject); // 在更新数据时,自动将更新时间填充
    16. }
    17. }
  4. 测试

    插入

    
       
       
    1. @Test
    2. public void addTest(){
    3. User user = new User();
    4. user.setName("喜羊羊");
    5. user.setAge(21);
    6. user.setEmail("634498594@qq.com");
    7. int result = userMapper.insert(user);
    8. System.out.println(result);
    9. }

    image-20200618120924730

    image-20200618120946608

    更新

    
       
       
    1. @Test
    2. public void updateTest(){
    3. User user = new User();
    4. user.setId(1L);
    5. user.setName("codewei");
    6. user.setAge(26);
    7. int reslut = userMapper.updateById(user);
    8. System.out.println(reslut);
    9. }

    image-20200618121146060


8. 乐观锁

悲观锁,当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制【又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”】。

悲观锁,顾名思义,十分悲观,它总是会认为会出现问题,无论干什么都会上锁!

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。

乐观锁,顾名思义,十分乐观,它总是认为不会出现问题,无论干什么都不去上锁,如果出现了问题,就再次更新值测试!

意图:

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

 
 
  1. 乐观锁:1. 先查询,获得版本号 version = 1
  2. -- A线程
  3. update user set name = "codewei",version = version +1
  4. where id =2 and version = 1
  5. -- B线程抢先完成,这个时候 version=2,会导致A修改失败!
  6. update user set name = "codewei" ,version = version+1
  7. where id =2 and version = 1

测试一下MP的乐观锁插件

  1. 给数据库添加version字段

    image-20200618123310451

    image-20200618123350043

  2. 同步实体类,并添加注解@Version

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

    我们可以把主启动类上的包扫描,移动到该配置类上

    
       
       
    1. @Configuration
    2. @MapperScan("cn.codewei.mapper")
    3. @EnableTransactionManagement // 开启自动管理事务
    4. public class MyBatisPlusConfig {
    5. // 注册乐观锁插件
    6. @Bean
    7. public OptimisticLockerInterceptor optimisticLockerInterceptor(){
    8. return new OptimisticLockerInterceptor();
    9. }
    10. }
  4. 测试

    测试乐观锁成功

    
       
       
    1. // 测试乐观锁成功
    2. @Test
    3. public void optimisticLockerTest(){
    4. // 查询用户的信息
    5. User user = userMapper.selectById(1L);
    6. // 修改用户信息
    7. user.setName("陈乔恩");
    8. user.setEmail("156748115@qq.com");
    9. // 执行更新操作
    10. userMapper.updateById(user);
    11. }

    image-20200618124549515

    多线程下,测试乐观苏失败!

    如果没有乐观锁,就会覆盖插队线程的值

    
       
       
    1. @Test
    2. public void optimisticLockerTest(){
    3. // 线程1
    4. User user = userMapper.selectById(1L);
    5. user.setName("陈乔恩111");
    6. user.setEmail("156748115@qq.com");
    7. // 模拟另一个线程执行了插队操作
    8. User user2 = userMapper.selectById(1L);
    9. user2.setName("陈乔恩222");
    10. user2.setEmail("156748115@qq.com");
    11. // 可以使用自旋锁来尝试多次提交
    12. userMapper.updateById(user); // 如果没有乐观锁,就会覆盖插队线程的值
    13. }

    image-20200618125119164

    image-20200618125130817


9. 查询操作

通过ID查询用户


 
 
  1. // 测试查询
  2. @Test
  3. public void testSelectById(){
  4. User user = userMapper.selectById(1L);
  5. System.out.println(user);
  6. }

image-20200618125554362

根据多个ID查询多个用户


 
 
  1. // 测试查询
  2. @Test
  3. public void testSelectById(){
  4. List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3)); // 查询ID为1,2,3的用户
  5. for (User user : users) {
  6. System.out.println(user);
  7. }
  8. }

image-20200618125944270

条件查询,使用map操作


 
 
  1. // 条件查询
  2. @Test
  3. public void testSelectByBatchIds(){
  4. Map<String,Object> map = new HashMap<>();
  5. // 自定义要查询
  6. map.put("name","唐嫣"); // 条件 where name = '唐嫣'
  7. List<User> users = userMapper.selectByMap(map);
  8. for (User user : users) {
  9. System.out.println(user);
  10. }
  11. }

image-20200618130509406


10. 分页查询

原始:使用limit分页

MyBatisPlus内置了分页插件

使用

  1. 在我们的mybatisplus配置类中,配置拦截器组件即可

    
       
       
    1. @Configuration
    2. @MapperScan("cn.codewei.mapper")
    3. @EnableTransactionManagement // 开启自动管理事务
    4. public class MyBatisPlusConfig {
    5. // 注册乐观锁插件
    6. @Bean
    7. public OptimisticLockerInterceptor optimisticLockerInterceptor(){
    8. return new OptimisticLockerInterceptor();
    9. }
    10. // 配置分页
    11. @Bean
    12. public PaginationInterceptor paginationInterceptor() {
    13. return new PaginationInterceptor();
    14. }
    15. }
  2. 使用Page对象即可

测试分页查询


 
 
  1. // 测试分页查询
  2. @Test
  3. public void pageSelectTest(){
  4. // 参数1:当前页 参数二:页面显示条数
  5. Page<User> page = new Page<>(1,5);
  6. userMapper.selectPage(page, null);
  7. List<User> users = page.getRecords();
  8. for (User user : users) {
  9. System.out.println(user);
  10. }
  11. }

image-20200618134105129

使用了分页插件之后,所有的分页操作也变得简单了!


11. 删除操作

根据ID删除记录


 
 
  1. // 测试删除
  2. @Test
  3. public void deleteDelete(){
  4. userMapper.deleteById(2L); // 删除ID为2的用户
  5. }

image-20200618134504595

通过ID批量删除


 
 
  1. // 批量删除
  2. @Test
  3. public void deleteBatchId(){
  4. userMapper.deleteBatchIds(Arrays.asList(2L,3L,4L));
  5. }

image-20200618134724683

通过Map条件删除


 
 
  1. // 通过Map删除
  2. @Test
  3. public void deleteMap(){
  4. Map<String,Object> map = new HashMap<>();
  5. map.put("name","Billie"); // 删除name为Billie的
  6. userMapper.deleteByMap(map);
  7. }

image-20200618135058301


12. 逻辑删除

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

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

管理员可以查看被删除的记录,方式数据的丢失!类似于回收站!

测试

  1. 在表中增加一个deleted字段,默认值0

    image-20200618135541360

  2. 在实体类中增加属性

    
       
       
    1. @TableLogic // 逻辑删除
    2. private Integer deleted;
  3. 在我们的MybatisPlus配置类中,进行配置(高版本的mybatis-plus不需要这一步)

    
       
       
    1. @Bean
    2. public ISqlInjector sqlInjector(){
    3. return new LogicSqlInjector();
    4. }
  4. 在yaml中进行配置

    
       
       
    1. # 配置日志
    2. mybatis-plus:
    3. configuration:
    4. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    5. # 配置逻辑删除
    6. global-config:
    7. db-config:
    8. logic-delete-value: 1 # 删除了的值的 delete 为 1
    9. logic-not-delete-value: 0 # 没有删除的值的 delete 为 0
  5. 测试

    image-20200618140939713

    
       
       
    1. @Test
    2. public void deleteDelete(){
    3. userMapper.deleteById(1L); // 删除ID为1的用户
    4. }

    image-20200618141121724

    数据库中的数据还存在

    image-20200618141155872

    但是,逻辑删除以后,我们就无法查询到被逻辑删除的数据了

    执行查询全部

    image-20200618141356748


13. 性能分析插件

我们在平时的开发中,会遇到一些慢SQL

MyBatisPlus也提供了性能分析插件,如果超过了这个时间就停止运行,性能分析拦截器,用于输出每条 SQL 语句及其执行时间

  1. 在我们的MyBatisPlus配置类中配置

    
       
       
    1. /**
    2. * SQL执行效率插件
    3. */
    4. @Bean
    5. @Profile({"dev","test"})// 设置 dev test 环境开启
    6. public PerformanceInterceptor performanceInterceptor() {
    7. PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    8. performanceInterceptor.setMaxTime(1); // 设置sql最大执行时间,单位ms,如果超过了,则不执行
    9. performanceInterceptor.setFormat(true); // 开启sql格式化
    10. return performanceInterceptor;
    11. }
  2. 在application.yaml中进行配置现在的环境

    
       
       
    1. spring:
    2. profiles:
    3. active: dev
  3. 测试

    
       
       
    1. // 查询全部
    2. @Test
    3. public void selectAll(){
    4. List<User> users = userMapper.selectList(null);
    5. for (User user : users) {
    6. System.out.println(user);
    7. }
    8. }

    image-20200618180306672

    如图,会出现一个错误,但我这并不是我们代码的错误,而是sql执行超出了我们设定的最大时间,所以会报错提示!!

    当我们把最大时间改成了1000时,就没有再出现错误!

    
       
       
    1. @Bean
    2. @Profile({"dev","test"})// 设置 dev test 环境开启
    3. public PerformanceInterceptor performanceInterceptor() {
    4. PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    5. performanceInterceptor.setMaxTime(1000); // 设置sql最大执行时间,单位ms,如果超过了,则不执行
    6. performanceInterceptor.setFormat(true); // 开启sql格式化
    7. return performanceInterceptor;
    8. }

    image-20200618180650491

14. 添加查询器Wrapper

十分重要!!

我们写一些复杂的SQL就可以使用它来代替

image-20200618200433126

测试一:查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12岁的


 
 
  1. @Test
  2. void contextLoads(){
  3. // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12岁的
  4. QueryWrapper<User> wrapper = new QueryWrapper<>();
  5. wrapper.isNotNull("name") // name不为空
  6. .isNotNull("email") // email不为空
  7. .ge("age",12); // age大于等于12
  8. List<User> users = userMapper.selectList(wrapper);
  9. for (User user : users) {
  10. System.out.println(user);
  11. }
  12. }

image-20200618200532249

测试二:查询名字为喜羊羊的


 
 
  1. @Test
  2. public void test2(){
  3. // 查询名字为喜羊羊的
  4. QueryWrapper<User> wrapper = new QueryWrapper<>();
  5. wrapper.eq("name","喜羊羊");
  6. User user = userMapper.selectOne(wrapper); // 查询一个数据
  7. System.out.println(user);
  8. }

image-20200618200959280

测试三:查询年龄在20到30之间的用户数量


 
 
  1. @Test
  2. public void test3(){
  3. // 查询年龄在20到30之间的用户数量
  4. QueryWrapper<User> wrapper = new QueryWrapper<>();
  5. wrapper.between("age",20,30); // 区间
  6. Integer count = userMapper.selectCount(wrapper);
  7. System.out.println(count);
  8. }

image-20200618201428483

测试四:模糊查询! 查询名字里面包含 ‘陈’ 的

.likeRight:xxx%

likeLeft:%xxx

like:%xxx%


 
 
  1. @Test
  2. public void test4(){
  3. // 查询名字里面包含 ‘陈’ 的
  4. QueryWrapper<User> wrapper = new QueryWrapper<>();
  5. wrapper.like("name","陈");
  6. List<Map<String, Object>> users = userMapper.selectMaps(wrapper);
  7. for (Map<String, Object> user : users) {
  8. System.out.println(user);
  9. }
  10. }

image-20200618202456086

测试五:子查询


 
 
  1. @Test
  2. public void test5(){
  3. QueryWrapper<User> wrapper = new QueryWrapper<>();
  4. // id 在子查询中查出
  5. wrapper.inSql("id","select id from user where id<3");
  6. List<Object> objects = userMapper.selectObjs(wrapper);
  7. for (Object object : objects) {
  8. System.out.println(object);
  9. }
  10. }

image-20200618202908989

测试六:通过ID进行排序


 
 
  1. @Test
  2. public void test6(){
  3. // 通过id进行排序
  4. QueryWrapper<User> wrapper = new QueryWrapper<>();
  5. wrapper.orderByAsc("id"); // 升序
  6. List<User> users = userMapper.selectList(wrapper);
  7. for (User user : users) {
  8. System.out.println(user);
  9. }
  10. }

image-20200618203350991


15. 代码自动生成器

MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:


 
 
  1. <dependency>
  2. <groupId>com.baomidou</groupId>
  3. <artifactId>mybatis-plus-generator</artifactId>
  4. <version>3.3.2</version>
  5. </dependency>

官方文档:https://mp.baomidou.com/guide/generator.html


 
 
  1. // 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
  2. public class CodeGenerator {
  3. /**
  4. * <p>
  5. * 读取控制台内容
  6. * </p>
  7. */
  8. public static String scanner(String tip) {
  9. Scanner scanner = new Scanner(System.in);
  10. StringBuilder help = new StringBuilder();
  11. help.append("请输入" + tip + ":");
  12. System.out.println(help.toString());
  13. if (scanner.hasNext()) {
  14. String ipt = scanner.next();
  15. if (StringUtils.isNotEmpty(ipt)) {
  16. return ipt;
  17. }
  18. }
  19. throw new MybatisPlusException("请输入正确的" + tip + "!");
  20. }
  21. public static void main(String[] args) {
  22. // 代码生成器
  23. AutoGenerator mpg = new AutoGenerator();
  24. // 全局配置
  25. GlobalConfig gc = new GlobalConfig();
  26. String projectPath = System.getProperty("user.dir");
  27. gc.setOutputDir(projectPath + "/src/main/java");
  28. gc.setAuthor("jobob");
  29. gc.setOpen(false);
  30. // gc.setSwagger2(true); 实体属性 Swagger2 注解
  31. mpg.setGlobalConfig(gc);
  32. // 数据源配置
  33. DataSourceConfig dsc = new DataSourceConfig();
  34. dsc.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
  35. // dsc.setSchemaName("public");
  36. dsc.setDriverName("com.mysql.jdbc.Driver");
  37. dsc.setUsername("root");
  38. dsc.setPassword("密码");
  39. mpg.setDataSource(dsc);
  40. // 包配置
  41. PackageConfig pc = new PackageConfig();
  42. pc.setModuleName(scanner("模块名"));
  43. pc.setParent("com.baomidou.ant");
  44. mpg.setPackageInfo(pc);
  45. // 自定义配置
  46. InjectionConfig cfg = new InjectionConfig() {
  47. @Override
  48. public void initMap() {
  49. // to do nothing
  50. }
  51. };
  52. // 如果模板引擎是 freemarker
  53. String templatePath = "/templates/mapper.xml.ftl";
  54. // 如果模板引擎是 velocity
  55. // String templatePath = "/templates/mapper.xml.vm";
  56. // 自定义输出配置
  57. List<FileOutConfig> focList = new ArrayList<>();
  58. // 自定义配置会被优先输出
  59. focList.add(new FileOutConfig(templatePath) {
  60. @Override
  61. public String outputFile(TableInfo tableInfo) {
  62. // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
  63. return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
  64. + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
  65. }
  66. });
  67. /*
  68. cfg.setFileCreate(new IFileCreate() {
  69. @Override
  70. public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
  71. // 判断自定义文件夹是否需要创建
  72. checkDir("调用默认方法创建的目录,自定义目录用");
  73. if (fileType == FileType.MAPPER) {
  74. // 已经生成 mapper 文件判断存在,不想重新生成返回 false
  75. return !new File(filePath).exists();
  76. }
  77. // 允许生成模板文件
  78. return true;
  79. }
  80. });
  81. */
  82. cfg.setFileOutConfigList(focList);
  83. mpg.setCfg(cfg);
  84. // 配置模板
  85. TemplateConfig templateConfig = new TemplateConfig();
  86. // 配置自定义输出模板
  87. //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
  88. // templateConfig.setEntity("templates/entity2.java");
  89. // templateConfig.setService();
  90. // templateConfig.setController();
  91. templateConfig.setXml(null);
  92. mpg.setTemplate(templateConfig);
  93. // 策略配置
  94. StrategyConfig strategy = new StrategyConfig();
  95. strategy.setNaming(NamingStrategy.underline_to_camel);
  96. strategy.setColumnNaming(NamingStrategy.underline_to_camel);
  97. strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
  98. strategy.setEntityLombokModel(true);
  99. strategy.setRestControllerStyle(true);
  100. // 公共父类
  101. strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
  102. // 写于父类中的公共字段
  103. strategy.setSuperEntityColumns("id");
  104. strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
  105. strategy.setControllerMappingHyphenStyle(true);
  106. strategy.setTablePrefix(pc.getModuleName() + "_");
  107. mpg.setStrategy(strategy);
  108. mpg.setTemplateEngine(new FreemarkerTemplateEngine());
  109. mpg.execute();
  110. }
  111. }

转载:https://www.kuangstudy.com/bbs/1397740321307127810

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值