概述
MybatisPlus 一款基于Mybatis的增强工具,用于简化开发,提高效率。它在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。
快速入门
准备工作
首先准备一个数据库表:
然后创建一个SpringBoot工程,然后在pom.xml中引入我们需要的依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--这是父工程,SpringBoot的固定写法-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--这是本项目的项目坐标-->
<groupId>com.Why</groupId>
<artifactId>mplearning</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mplearning</name>
<!--项目描述-->
<description>Demo project for Spring Boot</description>
<!--项目的java版本-->
<properties>
<java.version>1.8</java.version>
</properties>
<!--我们需要的依赖-->
<dependencies>
<!--SpringBoot的场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--测试的启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--web环境的场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.5</version>
</dependency>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
</dependencies>
<!--插件:能够以Maven的方式为应用提供Spring Boot的支持,
即为Spring Boot应用提供了执行Maven操作的可能。
(除了比较关键的几个启动器比较有用,这些其实不太重要,这些只不过是使用Spring Initializr快速生成Boot应用自带的而已)-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
准备实体类
package com.why.mplearning.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//以下注解均由lombok插件提供
@Data //提供set、get方法
@AllArgsConstructor //提供全参构造器
@NoArgsConstructor //提供无参构造器
public class User {
//字段要对应数据库上的
private Integer id;
private String username;
private String password;
}
使用MybatisPlus
添加mp的依赖
<!--引入MybatisPlus依赖(不需要引入mybatis因为mp里面已经包含了mybatis)-->
<dependency>
<!--苞米豆团队开发的-->
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!--引入mysql的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
然后在配置文件中写上数据库的配置信息
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssm
username: root
password: 121314
driver-class-name: com.mysql.cj.jdbc.Driver
注意:首先非同一层级要缩进两个空格,另外yml文件中键值对之间是有空格的。
创建Mapper接口
创建Mapper接口继承BaseMapper接口
BaseMapper接口中已经提供了很多的常用方法,所以我们只需要直接从容器中获取Mapper就可以进行操作了,不需要自己去编写sql语句。
配置Mapper扫描
一种方式是,我们可以在我们UserMapper接口上增加@Mapper注解来扫描,但是当要扫描的接口过多时,每一个都加一个@Mapper是比较麻烦的,所以我们可以直接在启动类上加上一个@MapperScan,然后在括号里写上扫描哪个包就行了。
package com.why.mplearning;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.why.mplearning.mapper")
public class MplearningApplication {
public static void main(String[] args) {
SpringApplication.run(MplearningApplication.class, args);
}
}
获取Mapper测试
进行测试:
可以看见数据库中的数据被查询到了。
常用设置
设置表映射规则
默认情况下,MP操作的表名就是实体类的类名,但是如果表名不和类名一致的话就需要我们自己设置映射规则。
单独设置
可以在实体类的类名上加上@TableName注解来进行标识。
例如:如果表名是tb_user,而实体类名是User则可以使用以下写法:
@TableName("tb_user")
public class User{
//....
}
全局设置表名前缀
一般一个项目表名的前缀都是统一风格的,这个时候如果一个一个设置就太麻烦了。我们可以通过配置来设置全局的表名前缀。
例如:如果一个项目中所有的表名相比于类名都是多了个前缀:tb_。这个时候就可以使用如下方式配置:
mybatis-plus:
global-config:
db-config:
# 表名前缀
table-prefix: tb_
设置主键生成策略
单独设置
默认情况下使用MP插入数据时,如果在我们没有设置主键生成策略的情况下默认的策略是基于雪花算法的自增id。
如我们现在插入数据并没有指明id值:
但是插入之后在数据库中MP会采用雪花算法自动给我们增添上一个id值:
但如果我们需要使用别的策略可以在定义实体类时,在代表主键的字段上加上@TableId注解,使用其type属性指定主键生成策略。
例如:我们要设置主键自动增长则可以设置如下:
@Data
@NoArgsConstruct
@AllArgsConstruct
public class User{
@TableId(type = IdType,AUTO)
private Long id;
//.....
}
全部主键策略定义在了枚举类IdType中,IdType有如下的取值
1、 AUTO
数据库ID自增,依赖于数据库。该类型请确保数据库设置了ID自增,否则无效。
2、 NONE
未设置主键类型。若在代码中没有手动设置主键,则会根据主键的全局策略自动生成(默认的主键全局策略是基于雪花算法的自增ID)
2、INPUT
需要手动设置主键,若不设置,插入操作生成SQL语句时,主键这一列的值会是null。
3、 ASSIGN_ID
当没有手动设置主键,即实体类中的主键属性为空时,才会自动填充,使用雪花算法。
4、 ASSIGN_UUID
当实体类的主键属性为空时,才会自动填充,使用UUID。
全局设置
mybatis-plus:
global-config:
db-config:
# id生成策略 auto为数据库自增
id-type: auto
设置字段映射关系
默认情况下MP会根据实体类的属性名去映射表的列名。
如果数据库的列表和实体类的属性名不一致了完美可以使用@TableField注解的value属性去设置映射关系。
例如:
如果表中一个列名叫address而实体类中的属性名为addressStr则可以使用如下方式进行配置。
@TableField("address")
private String addressStr;
设置字段和列名的驼峰映射
默认情况下MP会开启字段名列名的驼峰映射,即从经典数据库列名A_COLUMN(下划线命名)到经典Java属性名aColumn(驼峰命名)的类似映射。
如果需要关闭我们可以使用如下配置进行关闭。
mybatis-plus:
configuration:
# 是否开启自动驼峰命名规则(camel case)映射,
#即从经典数据库列名A_COLUMN(下换线命名)到经典Java属性名aColumn(驼峰命名)的类似映射
map-underscore-to-camel-case: false
日志
如果需要打印MP操作对应的SQL语句等,可以配置日志输出。
配置方式如下:
mybatis-plus:
configuration:
# 日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
基本使用
插入数据
在MP中我们使用insert来实现数据的插入:
删除操作
MP提供的删除方法有很多,按条件删除需要学习Wrapper的知识,所以后面再说,这里先说其他的。
批量删除(deleteBatchIds(传入list一个集合))
使用deleteBatchIds可以根据传入的集合中的id值批量删除数据,注意该方法使用时需要向其中传入一个id值的集合:
该方法返回一个整形数值,表示受影响的数据库中的行数。
单个删除(deleteById)
很明显啦,这就是传入一个id值进行删除即可。
根据列名删除(deleteByMap)
MP的这个方法可以帮我们进行列名来查找对应数据删除,比如列名为name列中我们要删除的数据为name=“zhangsan”,那么我们可将其作为KV键值对传入该方法进行删除。
从日志中可以看出,如果map集合中有多个kv键值对时,则表示筛选条件要同时满足这多个条件才可进行删除,否则无法删除。
更新操作
根据用户id更新:updateById(传入一个对象)
跟删除差不多,根据你传入的对象的id属性进行更新:
条件构造器Wrapper
概述
我们在实际操作数据库的时候会涉及到很多的条件(比如按什么什么条件进行删除或更新或查询),所以MP为我们提供了一个功能强大的条件构造器Wrapper,使用它可以很方便的让我们进行条件的构造。
其继承体系如下:
在其子类的AbstractWrapper中提供了很多用于构造Where条件的方法。
AbstractWrapper的子类QueryWrapper则额外提供了用于针对Select语法的select方法。可以用来设置查询哪些列。
AbstractWrapper的子类UpdateWrapper则额外提供了用于针对Set语法的set方法。可以用来设置对哪些列进行更新。
常用的AbstractWrapper方法
示例:
比如我们的查询sql是这样的:
那么将其转换为我们的Wrapper写法就是:
sql如下:
Wrapper写法如下:
注意模糊查询的那一对百分号其实可以不用加,因为MP已经替我们加上了。
sql如下:
wrapper写法如下:
常用的QueryWrapper方法
QueryWrapper的select方法中可以设置要查询的列(可以多个)。
示例:
sql如下:
wrapper写法如下:
上述wrapper也还有另一种写法:
即:
sql如下:
除了address列我不查,其他都要查,用上上面的第二种写法就可以轻易做到:
在address的查询语句前面加个"!"号即表示除了address不查其他都查。
常用UpdateWrapper方法
我们前面在使用update方法时需要传入一个实体类对象传入,用来指定要更新的列以及对应的值,但是如果需要更新的列比较少时,创建这么一个对象显得有点麻烦和复杂。
那么我们就可以使用UpdateWrapper的set方法来设置要更新的列及其值。同时这种方式也可以使用Wrapper去指定更复杂的更新条件。
示例:
sql如下:
wrapper写法如下:
Lambda条件构造器
我们前面在使用条件构造器时列名都是使用字符串的形式去指定,这种方式无法在编译期确定列名的合法性。所以MP就提供了一个Lambda条件构造器可以让我们直接以实体类的方法引用的形式来指定列名。这样就可以弥补上述缺陷。
示例:
sql如下:
如果用之前的方式去进行查询:
不难发现,如果我们的age字符串写错了,写成aage或者其他什么,那么编译器是不会给我们提醒报错的。会很麻烦,所以可以使用MP提供的Lambda条件构造器,示例如下:
就是使用个双冒号来表示映射关系,即User::getAge这一句的意思就是:对User表中的age字段进行操作。
自定义SQL
虽然MP为我们提供了很多的常用方法,并且也提供了条件构造器。但是在开发当中我们难免还是会碰见非常复杂的SQL语句,那这个时候就会需要我们自己去定义对应的方法,自己去写对应的SQL,这样SQL也更有利于后期维护。
因为MP是对mybatis做了增强,所以还是支持之前mybatis的方式去自定义方法。
同时也支持在使用mybatis的自定义方法时使用MP的条件构造器帮助我们进行条件构造。
接下来我们去实现一下这个事情。
其实就是之前Mybatis的方式
如上图三步走,先去Mapper接口中定义方法:
然后去配置xml文件的存放目录:
然后在resources目录下创建一个mapper文件夹,在文件夹中写上mybatis的xml配置文件:
然后像正常的MybatisPlus调用sql方法去调用我们写的这个方法即可:
Mybatis方式结合条件构造器
我们在使用上述方式自定义方法时,如果也希望我们的自定义方法能像MP自带的方法一样使用条件构造器来进行条件构造的话只需要使用如下方式即可。
1、方法定义中添加Wrapper类型的参数
添加Wrapper类型的参数,并且要注意给其指定参数名。
2、在SQL语句中获取Wrapper拼接的SQL片段进行拼接。
来实现一下:
第一步:
在UserMapper接口下写上方法:
第二步在xml文件中拼接sql片段:
说明:ew是MP底层提供的一个常量,因为我们之前定义的参数是MP底层提供的Constants.WRAPPER,所以这里调用的参数也就是MP提供的ew(和原生的MP的其他wrapper方法差不多),然后ew后面的customSqlSegment则是MP底层提供的用来进行sql语句拼接的方法。
同时要注意:这里不能使用#{}而应该用${}。
然后去测试就行了:
我们传入的wrapper它会自动帮我们进行拼接字符串的拼接。
分页查询
MP提供了分页查询的基本功能,如果不是要配置特别复杂的分页查询,那么这个MP的基本分页其实就已经足够我们使用了。
使用步骤如下:
第一步:配置分页查询拦截器
一般我们会在主程序包下创建一个config配置包来放这个项目的所有的配置类,这里我们也将MP的分页查询器配置放在这个包下:
这段代码在MP的官网上就有,可以直接去官网复制。注意因为我们使用的MP是3.4.0之后的版本,所以我们都如上那么写,如果是之前的版本,则方式不一样,在官网上有其过去版本的代码。
第二步:我们去查询就可以了
多表分页查询
如果需要在多表查询时进行分页查询的话,就可以在mapper接口中自定义方法,然后让方法接收Page对象。
示例:
需求:我们需要去查询Order订单表,并且要求查询的时候除了要获取到Orders表中的字段,还要获取到每个订单的下单用户的用户名。
准备工作:
SQL准备、实体类修改(就是查询结果的一个实体类,因为你需要一个实体类来装载查询出来的新的表呀);
实体类:
中间省略…因为我们查询的表最后所展现的列数就是order表的所有列+一个username,所以还要加上下面这一句username放进去。
上面的SQL语句是将user表和order表进行一个连查,新查出来的表的信息的列为订单表的所有列以及加上用户表的name列。匹配条件是两张表的id都相同。
可以看见已经在可视化窗口中查出来了。
那么现在我们去实现一下:
我们先要自己去定义一个关于多表操作的方法,因为MP所提供的方法都是单表操作,而我们要实现的是多表操作,所以我们只能自己去写对应的方法代码了;
然后去xml映射文件中写sql:
先测试一下:
没有问题那么我们现在开始对这个查询结果进行一个分页的操作:
首先改一下我们原来的查询方法:
然后测试:
Service接口概述
学到这了不可能不知道三层架构叭?MP其实也替我们实现了service服务层的一些方法,但这里我们先回忆一下原先我们的三层架构中的service层是怎么写的:
第一步先在主程序下创建一个service包
然后在service包下写上一个XXXService接口
最后在service包下再写一个impl实现包来实现所有的service接口。在这个实现包下去注入xxxMapper,然后在这里面实现业务的逻辑方法:
MP也为我们提供了Service层的实现,我们只需要写一个接口去继承Iservice,并创建一个接口实现类继承ServiceImpl,即可使用。
相比于Mapper接口,Service层主要是支持了更多批量操作的方法。
接下来我们进行演示:
第一步就是让我们的xxxService接口去继承Iservice(方法可以不用写,因为Iservice中为我们封装了很多方法了,但是也仅限于是对于单表的操作):
第二步就是去改变我们的实现类,让其继承一个ServiceImpl的类,其中要传两个泛型,第一个是这个service方法对应的Mapper层接口,第二个是操作的对象类型,如下:
这其中具体有哪些方法我们可以自行去官网进行查看即可,这里就不写了。
不过直接用之前mapper里面的方法应该问题也是不大的。
Service层自定义方法
步骤和Mapper层中自定义方法差不多。
首先在service接口层中添加我们自己定义的方法:
然后去实现类中实现我们的自定义方法:
其中如果是要注入本类型的变量,比如上图中的UserMapper,我们就不用再写@Autowired去注入了,直接可以使用MP的一个方法通过反射去生成对应对象,如果是要引用其他类型的变量则可以通过@Autowired注入。
代码生成器
MP提供了一个代码生成器,可以让我们一键生成实体类、Mapper接口、Service、Controller等全套代码。
使用方式如下:
1、添加依赖
2、生成就行
修改相应配置以后执行以下代码即可:
package com.lcy.demo.generator;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
public class MysqlGenerator {
public static void main(String[] args) {
AutoGenerator mpg = new AutoGenerator();
//1、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java"); //生成路径(一般都是生成在此项目的src/main/java下面)
gc.setAuthor("liangcy"); //设置作者
gc.setOpen(false);
gc.setFileOverride(true); //第二次生成会把第一次生成的覆盖掉
gc.setServiceName("%sService"); //生成的service接口名字首字母是否为I,这样设置就没有
gc.setBaseResultMap(true); //生成resultMap
mpg.setGlobalConfig(gc);
//2、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis-plus-demo?useUnicode=true&serverTimezone=GMT&useSSL=false&characterEncoding=utf8");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 3、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("sys");
pc.setParent("com.lcy.demo");
mpg.setPackageInfo(pc);
// 4、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setSuperControllerClass("com.lcy.demo.sys.controller.BaseController");
strategy.setSuperEntityClass("com.lcy.demo.sys.entity.BaseEntity");
// strategy.setTablePrefix("t_"); // 表名前缀
strategy.setEntityLombokModel(true); //使用lombok
strategy.setInclude("user"); // 逆向工程使用的表 如果要生成多个,这里可以传入String[]
mpg.setStrategy(strategy);
//5、执行
mpg.execute();
}
}
注意:这上面的代码看自己需求需要进行改动才能使用嗷~
而且我觉得在项目中的话不方便用main方法执行的话,那么可以将其作为测试方法用@Test运行,然后就可以在主类中生成对应的代码啦。
自动填充
在实际项目中的表会和我们的orders表一样,有更新时间、创建时间、创建人、更新人等字段。那么我们可以使用@TableField的fill属性来设置字段的自动填充。让我们能更方便的更新相关字段。
示例:
第一步:在对应需要更新的字段上增加注解
第二步:自定义填充处理器MetaObjectHandler
逻辑删除
啥是逻辑删除?
在实际开发当中,一般我们所谓的删除数据并不会真正的将这个数据从我们的数据库中移除,相反我们会保留下来,但确实要让用户看见自己想删除的被删除了,所以我们会预留一个表示删除与否的状态字段。比如del_flag,用0表示没被删除,用1表示已经被删除。
乐观锁
使用如下(代码在MP官网都有哈,可以自己去copy,我这里都是图):
第一步:配置对应插件
第二步:在实体类的字段上加上@version注解
第三步:检查更新