MyBatisPlus

前言

记录自己在使用MyBatisPuls时遇到的一些问题,一个找不到Bean的问题真的卡了我好久,一个上午都在解决这个问题,因为自己对于底层的实现并不了解,所以导致在报错的时候也不懂是怎么一回事😞,通过一上午的摸索,也对注解开发的过程有了一定的了解,记录下来怕自己下次又忘了,也是对自己知识的巩固

简介

MyBatisPlus(简称MP)是基于MyBatis框架上的增强开发工具,具有无侵入支持lambda,内置通用Mapper,支持主键自动生成内置分页插件等特性

开发方式

本文是基于SpringBoot使用MyBatisPlus,SpringBoot版本为3.4.0,Mp版本为3.5.7,可以兼容使用

1
2
3
4
5
6
7
8
9
10
11
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>

如何快速开发

快速开发实体类

1
2
3
4
><dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

SpringBoot默认管理了lombok的版本依赖,不需要指定版本号,记得下载lombok插件

1
2
3
4
5
6
7
8
9
10
11
12
package com.dome.domain;

import lombok.Data;

@Data
public class User {
private Integer id;
private String username;
private String password;
private Integer role ;
private String myname;
}

注意事项

​ 导入lombok后在使用注解,这就实体类要写的所有代码,@Data不包含构造方法的注解,要用什么自己添上,Lombok对静态属性不会提供get、set方法, transient修饰实体类属性(修饰的属性不会被序列化),@TableField(exist=false),这个注解用来表示数据表中不存在该字段,默认是true

主键自增策略

MP的默认主键策略是基于雪花算法的自增主键,主键采用雪花算法生成值的前提是实体类的主键属性名称必须为id,数据表字段带有_的可以自动映射到驼峰式命名的属性上(t_user——》tUser)

  1. 数据库名不同,在类上增加@TableName(“mp_user”)
  2. 主键ID的驼峰一般无法识别,在主键属性上增加@TableId
  3. 属性与字段名不相同,在属性上增加@TableField(“name”)

也可以在yml配置文件中开启全局配置

逻辑删除和乐观锁

逻辑删除:在数据设置中有一个是否可用字段,如果要删除这条数据,就将该字段设置为不可用,数据仍然保留在数据库中

乐观锁:主要用于秒杀抢单,乐观锁查询记录时不会上锁,但是会在更新记录的时候去判断下有没有人去更新了这条记录,数据库中有一个字段记录了更新的值,每次更新该字段就自增

  1. 实现乐观锁需要在version字段上添加@Version注解

  2. 添加拦截器,一定要先查询再更新,不然乐观锁没法生效

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     package com.dome.config;

    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    public class MpConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    final MybatisPlusInterceptor mp = new MybatisPlusInterceptor();
    //添加具体的拦截器,这是分页拦截器,开启分页查询功能
    mp.addInnerInterceptor(new PaginationInnerInterceptor());
    // 添加乐观锁插件,可以添加多个拦截器
    mp.addInnerInterceptor(newOptimisticLockerInnerInterceptor());

    return mp;

    }
    }

条件查询和分页查询

  1. 使用内置的查询方法进行查询时,实体类要实现序列化接口Serialzable

  2. 普通查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     //方式一:按条件查询
    QueryWrapper qw = new QueryWrapper();
    qw.lt("age",18);
    List<User> userList = userDao.selectList(qw);
    System.out.println(userList);
    //方式二:lambda格式按条件查询
    QueryWrapper<User> qw = new QueryWrapper<User>();
    qw.lambda().lt(User::getAge, 10);
    List<User> userList = userDao.selectList(qw);
    System.out.println(userList);

    //方式三:lambda格式按条件查询
    LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
    lqw.lt(User::getAge, 10);
    List<User> userList = userDao.selectList(lqw);
    System.out.println(userList);

  3. 条件传递过来是空值怎么办

      
    1
    2
    3
    4
    5
    6
    7
    8
    9
    LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
    //先判定第一个参数是否为true,如果为true连接当前条件
    //相当于不为空的话就链接uq.get出来的值
    lqw.lt(null != uq.getAge2(),User::getAge, uq.getAge2());
    lqw.gt(null != uq.getAge(),User::getAge, uq.getAge());
    //链式链接
    lqw.lt(null != uq.getAge2(),User::getAge, uq.getAge2())
    .gt(null != uq.getAge(),User::getAge, uq.getAge());
    List<User> userList = userDao.selectList(lqw);

    3.查询投影和分组聚合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //查询投影,选择自己想看的字段
    LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
    lqw.select(User::getId,User::getName,User::getAge);
    QueryWrapper<User> lqw = new QueryWrapper<User>();
    lqw.select("id","name","age","tel");
    List<User> userList = userDao.selectList(lqw);
    System.out.println(userList);
    //分组查询聚合函数,不能用lambda
    QueryWrapper<User> lqw = new QueryWrapper<User>();
    lqw.select("count(*) as count, tel");
    lqw.groupBy("tel");
    List<Map<String, Object>> userList = userDao.selectMaps(lqw);
    System.out.println(userList);

    LambdaQueryWrapperQueryWrapper都是用来构建查询条件的,一个支持lambda表达式

代码生成器

 官方代码

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FastAutoGenerator.create("url", "username", "password")
.globalConfig(builder -> builder
.author("Baomidou")
.outputDir(Paths.get(System.getProperty("user.dir")) + "/src/main/java")
.commentDate("yyyy-MM-dd")
)
.packageConfig(builder -> builder
.parent("com.baomidou.mybatisplus")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.xml("mapper.xml")
)
.strategyConfig(builder -> builder
.entityBuilder()
.enableLombok()
)
.templateEngine(new FreemarkerTemplateEngine())
.execute();
实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.dome;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;

public class Generator {
public static void main(String[] args) {

// 使用 FastAutoGenerator 快速配置代码生成器
FastAutoGenerator.create("jdbc:mysql://localhost:3306/my_book?serverTimezone=GMT%2B8", "username", "1234")
.globalConfig(builder -> {
builder.author("YU") // 设置作者
.outputDir("Mp_dome02_generator\\src\\main\\java"); // 输出目录
})
.packageConfig(builder -> {
builder.parent("com.xxx") // 设置父包名
.entity("domain") // 设置实体类包名
.mapper("dao") // 设置 Mapper 接口包名
.service("service") // 设置 Service 接口包名
.serviceImpl("service.impl") // 设置 Service 实现类包名
.xml("mappers"); // 设置 Mapper XML 文件包名
})
.strategyConfig(builder -> {
builder.addInclude("user") // 设置需要生成的表名
.entityBuilder()
.enableLombok() // 启用 Lombok
.enableTableFieldAnnotation() // 启用字段注解
.controllerBuilder()
.enableRestStyle(); // 启用 REST 风格
})
.templateEngine(new VelocityTemplateEngine()) // 使用 Velocity 模板引擎
.execute(); // 执行生成

}
}

导入的坐标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.7</version>
</dependency>

<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.4.1</version>
</dependency>

**这里使用的是velocity模板,如果要导入其他模板,跟换相应的坐标,代码生成器的版本要和上面导入的坐标兼容**

遇到的问题

  1. 出现报错Error creating bean with name

出现这个错误的原因是找不到bean,说明在Spring容器中并没有找到你自动装配的对象

一种是Error creating bean with name 'dataSource' defined in class path resource,这种就是数据库连接有问题,不要以为在代码中写了数据库用户密码就不要在配置中写了

1
2
3
4
5
6
7
8
9
10
11
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:mapper/*Mapper.xml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/my_book?serverTimezone=UTC
username: username
password: 1234
  1. 还是找不到bean的问题,这次是找不到在servicemapper包下的bean

出现这种情况的原因是没有开启注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.dome;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.yyy.service")
@MapperScan("com.yyy.mapper")
public class MpTestGeneratApplication {

public static void main(String[] args) {
SpringApplication.run(MpTestGeneratApplication.class, args);
}

}

把他们加入到扫包的行列中就解决了找不到bean的问题,在mapper包下它并没有生成@Mapper注解,所以直接在主运行程序导入,但在service包下有@Service注解它也还是会出现扫不到的问题,所以直接在主运行类上加入要扫的包是最保险的做法