Mybatis-Plus 全面解析讲解---全干货篇
1. 前言
1.1 MP是什么
MyBatis-Plus(简称MP)是 MyBatis 的增强工具库,它是在 MyBatis 的基础上进行了扩展和增强,旨在简化开发,提高效率。MyBatis-Plus 提供了一些常用且便捷的功能,使开发人员能够更轻松地进行数据库操作。 主要特点和功能包括:
- CRUD操作的便捷方法: MyBatis-Plus 提供了通用的 Mapper 接口,通过继承该接口,即可使用其中提供的诸多便捷的 CRUD 操作方法,无需手动编写 SQL。
- 条件构造器: MyBatis-Plus 提供了强大的条件构造器,可以通过代码构建复杂的查询条件,避免手动拼接 SQL。
- 分页插件: 提供了分页查询的支持,简化了分页操作的代码编写。
- 代码生成器: MyBatis-Plus 提供了代码生成器,可以根据数据库表自动生成对应的实体类、Mapper 接口以及 XML 文件,加速开发过程。
- 性能分析插件: 可以方便地进行 SQL 执行性能分析,帮助开发人员优化数据库访问性能。
- 乐观锁插件: 支持乐观锁的功能,确保数据的一致性和并发安全性。
1.2 为什么要用MP
引用MP的原话就是如下:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
2. 快速开始
2.1 配置依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
</dependencies>
2.2 配置yml
server:
port: 10086
servlet:
context-path : /api #访问前缀
spring:
datasource:
url: jdbc:mysql://localhost:3306/db111?useUnicode=true&characterEncoding=gbk&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 111
# 上面是配置数据库,注意换成自己的
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #MP日志配置
global-config:
db-config:
id-type: assign_id #配置默认ID类型 在实体类也可以具体配置,之后在注解开发会细讲
logic-delete-field: deleted # 默认软删除字段,在实体类也可以具体配置,
logic-delete-value: 1 # 默认代表删除的值,在实体类也可以具体配置,
logic-not-delete-value: 0 # 默认代表没被删除的值,在实体类也可以具体配置,
enable-sql-runner: true # 开启sqlRunner(用于发起原生sql的方式,之后在原生sql会细讲)
2.3 配置实体类
package com.example.mptest.pojo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.*;
/**
* @author hfLiuX
* @version 1.0
* @description: 酒店信息实体类
* @date 2023/12/5 15:11
*/
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@TableName("tb_hotel") //配置数据库名
public class Hotel {
@TableId(type = IdType.ASSIGN_ID) //配置主键ID类型,之后在注解开发会细讲
private Long id;
private String name;
private String address;
private double price;
private int score;
private String brand;
private String city;
private String starName;
private String business;
private String latitude;
private String longitude;
private String pic;
@TableLogic(value = "0",delval = "1")//这个也可以在yml文件进行全局配置
private int deleted;//软删除字段,0代表未删除,1代表已删除
@TableField(exist = false)//代表数据库中没有这个字段
private boolean status;
}
2.4 配置Mapper
package com.example.mptest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mptest.pojo.entity.Hotel;
import java.util.Collection;
import java.util.List;
/**
* @author M
* @version 1.0
* @description:
* @date 2023/12/5 15:20
*/
public interface HotelMapper extends BaseMapper<Hotel> {
//是的,没有看错 其他什么都不用写了,也不需要再配置XML的sql代码了(特别复杂的sql还是要单独写的)
}
2.5 简单的CRUD
/**
* @author M
* @version 1.0
* @description:
* @date 2023/12/5 15:48
*/
@SpringBootTest
public class TestAdd {
@Autowired
private HotelMapper hotelMapper;
@Test
public void testAddSigal() {
System.out.println(("----- 插入单条数据数据 ------"));
Hotel hotel1=new Hotel(null,"天津唐拉雅秀国际酒店","和平路199号",999,47,"唐拉雅秀","天津","二钻","小白楼","40.10144","116.380641","https://m.tuniucdn.com/fb2/t1/G2/M00/C7/CB/Cii-T1km_5eICnpJAAHOWN1GylMAAKYJwF0Hp8AAc5w000_w200_h200_c1_t0.jpg",0,false);
int insert = hotelMapper.insert(hotel1);
System.out.println(insert);
}
@Autowired
HotelMapper hotelMapper;
@Test
public void testDeleteCommon() {
System.out.println(("----- 根据ID删除单条数据------"));
int i = hotelMapper.deleteById(2048047291L);
System.out.println(i);
}
@Test
public void testSelect() {
System.out.println(("----- 查询所有数据 ------"));
List<Hotel> hotels = hotelMapper.selectList(null);//里面是查询条件
for (int i=0;i<hotels.size();i++){
System.out.println(hotels.get(i).toString());
}
}
@Test
public void testUpdate() {
System.out.println(("----- 根据ID修改单条数据 ------"));
Hotel hotel1=new Hotel();
hotel1.setId(2056105938L);
hotel1.setAddress("丽泽金融商务区凤凰路新址");
int i = hotelMapper.updateById(hotel1);
System.out.println(i);
}
}
这些都可以正常运行后,那么就证明MP启动成功,基础的配置也都搭建完成,可以继续了
3. 注解开发
其实主要都是在实体类中的注解
3.1 TableName
- 描述:表名注解,标识实体类对应的表
- 使用位置:实体类
@TableName("tb_hotel") //配置数据库名
public class Hotel {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
private String address;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 表名 |
schema | String | 否 | "" | schema |
keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时) |
resultMap | String | 否 | "" | xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定) |
autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入) |
excludeProperty | String[] | 否 | {} | 需要排除的属性名 @since 3.3.1 |
3.2 TableId
- 描述:主键注解
- 使用位置:实体类主键字段
@TableName("tb_hotel") //配置数据库名
public class Hotel {
@TableId(type = IdType.ASSIGN_ID) //配置主键ID类型
private Long id;
private String name;
private String address;
private double price;
private int score;
private String brand;
private String city;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 主键字段名 |
type | Enum | 否 | IdType.NONE | 指定主键类型 |
3.3 IdType
@TableName("tb_hotel") //配置数据库名
public class Hotel {
@TableId(type = IdType.ASSIGN_ID) //配置主键ID类型
private Long id;
private String name;
private String address;
private double price;
private int score;
private String brand;
private String city;
}
值 | 描述 |
---|---|
AUTO | 数据库 ID 自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert 前自行 set 主键值 |
ASSIGN_ID | 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默认实现类为DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默认 default 方法) |
ID_WORKER | 分布式全局唯一 ID 长整型类型(please use ASSIGN_ID ) |
UUID | 32 位 UUID 字符串(please use ASSIGN_UUID ) |
ID_WORKER_STR | 分布式全局唯一 ID 字符串类型(please use ASSIGN_ID ) |
3.4 TableField
- 描述:字段注解(非主键)
@TableName("tb_hotel") //配置数据库名
public class Hotel {
@TableId(type = IdType.ASSIGN_ID) //配置主键ID类型,之后在注解开发会细讲
private Long id;
@TableField("hotel_name")//代表数据库的字段名是hotel_name
private String name;
@TableField(exist = false)//代表数据库中没有这个字段
private boolean status;
private String address;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 数据库字段名 |
exist | boolean | 否 | true | 是否为数据库表字段 |
condition | String | 否 | "" | 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s} ,参考(opens new window) |
update | String | 否 | "" | 字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性) |
insertStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_NULL insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) |
updateStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:IGNORED update table_a set column=#{columnProperty} |
whereStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_EMPTY where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> |
fill | Enum | 否 | FieldFill.DEFAULT | 字段自动填充策略 |
select | boolean | 否 | true | 是否进行 select 查询 |
keepGlobalFormat | boolean | 否 | false | 是否保持使用全局的 format 进行处理 |
jdbcType | JdbcType | 否 | JdbcType.UNDEFINED | JDBC 类型 (该默认值不代表会按照该值生效) |
typeHandler | Class<? extends TypeHandler> | 否 | UnknownTypeHandler.class | 类型处理器 (该默认值不代表会按照该值生效) |
numericScale | String | 否 | "" | 指定小数点后保留的位数 |
3.5 Version
@TableName("tb_hotel") //配置数据库名
public class Hotel {
@Version
private int version;//乐观锁的字段设置(注意需要去加拦截器)
}
- 描述:乐观锁注解、标记
@Version
在字段上
3.6 TableLogic
- 描述:表字段逻辑处理注解(逻辑删除)
@TableName("tb_hotel") //配置数据库名
public class Hotel {
@TableId(type = IdType.ASSIGN_ID) //配置主键ID类型,之后在注解开发会细讲
private Long id;
@TableField("hotel_name")//代表数据库的字段名是hotel_name
private String name;
@TableLogic(value = "0",delval = "1")//这个也可以在yml文件进行全局配置
private int deleted;//软删除字段,0代表未删除,1代表已删除
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 逻辑未删除值 |
delval | String | 否 | "" | 逻辑删除值 |
3.7 OrderBy
- 描述:
内置 SQL
默认指定排序,优先级低于wrapper
条件查询
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
asc | boolean | 否 | true | 是否倒序查询 |
sort | short | 否 | Short.MAX_VALUE | 数字越小越靠前 |
4. CRUD详解
4.1 查询操作
package com.example.mptest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mptest.mapper.HotelMapper;
import com.example.mptest.pojo.entity.Hotel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author M
* @version 1.0
* @description:
* @date 2023/12/5 15:29
*/
@SpringBootTest
public class TestSelect {
@Autowired
private HotelMapper hotelMapper;
@Test
public void testSelect() {
System.out.println(("----- 查询所有数据 ------"));
List<Hotel> hotels = hotelMapper.selectList(null);//里面是查询条件
for (int i=0;i<hotels.size();i++){
System.out.println(hotels.get(i).toString());
}
}
@Test
public void testSelectById() {
System.out.println(("----- 根据ID查询单条酒店数据 ------"));
Hotel hotel = hotelMapper.selectById(609023);//里面是查询条件
System.out.println(hotel);
}
@Test
public void testSelectBathById() {
System.out.println(("----- 根据ID查询多条酒店数据 ------"));
List<Integer> IDList = Arrays.asList(648219, 672207, 5871652, 200214538);
List<Hotel> hotels = hotelMapper.selectBatchIds(IDList);//里面是查询条件
for (int i=0;i<hotels.size();i++){
System.out.println(hotels.get(i).toString());
}
}
@Test
public void testSelectPagination() {
System.out.println(("----- 分页查询所有数据 ------"));
Page<Hotel> hotelPage = new Page<>(1 , 10 , false);//页面对象
Page<Hotel> hotelPageNew = hotelMapper.selectPage(hotelPage, null);
//记住 记住,记住 一定要去配分页拦截器去才可以生效,否则会把所有数据全部返回回来
System.out.println("当前页码值"+hotelPageNew.getCurrent());
System.out.println("每页显示数"+hotelPageNew.getSize());
System.out.println("总共页数"+hotelPageNew.getPages());
System.out.println("总共条数"+hotelPageNew.getTotal());
System.out.println("页面数据"+hotelPageNew.getRecords());
}
@Test
public void testSelectByQuery() {
System.out.println(("----- 第一种一般条件查询 ------"));
QueryWrapper wrapper=new QueryWrapper();
wrapper.gt("price",1000);//价格大于800的约束
List list = hotelMapper.selectList(wrapper);
for (int i=0;i<list.size();i++){
System.out.println(list.get(i).toString());
}
}
@Test
public void testSelectByQuery2() {
System.out.println(("----- 第二种(lambma)的一般条件查询 ------"));
QueryWrapper<Hotel> wrapper=new QueryWrapper<Hotel>();
wrapper.lambda().le(Hotel::getPrice,400);//价格小于400的约束
List<Hotel> List = hotelMapper.selectList(wrapper);
for (int i=0;i<List.size();i++){
System.out.println(List.get(i).toString());
}
}
@Test
public void testSelectByQueryMuti() {
System.out.println(("----- 较为复杂的条件查询 ------"));
QueryWrapper<Hotel> wrapper=new QueryWrapper<Hotel>();
wrapper.lambda().le(Hotel::getPrice,800).like(Hotel::getName,"深圳");//表示和的情况不需要加and,但表示或的情况需要加or()
List<Hotel> List = hotelMapper.selectList(wrapper);
for (int i=0;i<List.size();i++){
System.out.println(List.get(i).toString());
}
}
@Test
public void testSelectByQueryMuti2() {
System.out.println(("----- 较为复杂的条件查询(更加规范与间接) ------"));
LambdaQueryWrapper<Hotel> lqw=new LambdaQueryWrapper<Hotel>();
lqw.le(Hotel::getPrice,800).like(Hotel::getName,"深圳");//表示和的情况不需要加and,但表示或的情况需要加or()
List<Hotel> List = hotelMapper.selectList(lqw);
for (int i=0;i<List.size();i++){
System.out.println(List.get(i).toString());
}
}
@Test
public void testSelectByQueryMutiSwitch() {
System.out.println(("----- 带有判断的条件查询 ------"));
LambdaQueryWrapper<Hotel> lqw=new LambdaQueryWrapper<Hotel>();
Hotel hotel1=new Hotel();
hotel1.setCity("天津");
lqw.lt(hotel1.getId()!=null,Hotel::getPrice,2000);//条件选择判断,先进行判断是否进行限制筛选
lqw.gt(Hotel::getPrice,1500);
List<Hotel> hotels = hotelMapper.selectList(lqw);
for (int i=0;i<hotels.size();i++){
System.out.println(hotels.get(i).toString());
}
}
@Test
public void testSelectMap() {
System.out.println(("----- 带有投影的条件查询 ------"));
LambdaQueryWrapper<Hotel> lqw=new LambdaQueryWrapper<Hotel>();
lqw.select(Hotel::getName,Hotel::getPrice,Hotel::getBrand);//只查询name price brand这三个字段
lqw.gt(Hotel::getPrice,1400);
List<Hotel> hotels = hotelMapper.selectList(lqw);
for (int i=0;i<hotels.size();i++){
System.out.println(hotels.get(i).toString());
}
}
@Test
public void testSelectMap2() {
System.out.println(("----- 带有group和count的投影条件查询 ------"));
QueryWrapper<Hotel>qw=new QueryWrapper<Hotel>();
qw.select("Count(*) as count , city");//按城市进行数量上的分类
qw.groupBy("city");
List<Map<String, Object>> maps = hotelMapper.selectMaps(qw);
for (int i=0;i<maps.size();i++){
System.out.println(maps.get(i));
}
}
}
4.2 增添操作
package com.example.mptest;
import com.example.mptest.mapper.HotelMapper;
import com.example.mptest.pojo.entity.Hotel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
/**
* @author M
* @version 1.0
* @description:
* @date 2023/12/5 15:48
*/
@SpringBootTest
public class TestAdd {
@Autowired
private HotelMapper hotelMapper;
@Test
public void testAddSigal() {
System.out.println(("----- 插入单条数据数据 ------"));
Hotel hotel1=new Hotel(null,"天津唐拉雅秀国际酒店","和平路199号",999,47,"唐拉雅秀","天津","二钻","小白楼","40.10144","116.380641","https://m.tuniucdn.com/fb2/t1/G2/M00/C7/CB/Cii-T1km_5eICnpJAAHOWN1GylMAAKYJwF0Hp8AAc5w000_w200_h200_c1_t0.jpg",0,false);
int insert = hotelMapper.insert(hotel1);
System.out.println(insert);
}
@Test
public void testAddMuti() {
System.out.println(("----- 插入多条数据数据 ------"));
Hotel hotel1=new Hotel(1999999l,"天津喜马拉雅快捷酒店","和平路133号",198,47,"喜马拉雅","天津","二钻","小白楼","40.10144","116.380641","https://m.tuniucdn.com/fb2/t1/G2/M00/C7/CB/Cii-T1km_5eICnpJAAHOWN1GylMAAKYJwF0Hp8AAc5w000_w200_h200_c1_t0.jpg",0,false);
Hotel hotel2=new Hotel(1999992l,"天津四季酒店","和平路111号",1999,47,"唐拉雅秀","天津","二钻","小白楼","40.10144","116.380641","https://m.tuniucdn.com/fb2/t1/G2/M00/C7/CB/Cii-T1km_5eICnpJAAHOWN1GylMAAKYJwF0Hp8AAc5w000_w200_h200_c1_t0.jpg",0,false);
Hotel hotel3=new Hotel(1999991l,"天津北国宾馆","和平路222号",799,47,"唐拉雅秀","天津","二钻","小白楼","40.10144","116.380641","https://m.tuniucdn.com/fb2/t1/G2/M00/C7/CB/Cii-T1km_5eICnpJAAHOWN1GylMAAKYJwF0Hp8AAc5w000_w200_h200_c1_t0.jpg",0,false);
ArrayList hotelList=new ArrayList<Hotel>();
hotelList.add(hotel1);
hotelList.add(hotel2);
hotelList.add(hotel3);
boolean insert = hotelMapper.saveHotelsByNative(hotelList);
System.out.println(insert);
}
}
4.3 删除操作
package com.example.mptest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.mptest.mapper.HotelMapper;
import com.example.mptest.pojo.entity.Hotel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
/**
* @author M
* @version 1.0
* @description:
* @date 2023/12/5 15:48
*/
@SpringBootTest
public class TestDelete {
@Autowired
HotelMapper hotelMapper;
@Test
public void testDeleteCommon() {
System.out.println(("----- 根据ID删除单条数据------"));
int i = hotelMapper.deleteById(2048047291L);
System.out.println(i);
}
@Test
public void testDeleteMuti() {
System.out.println(("----- 根据ID删除多条数据------"));
ArrayList hotelList=new ArrayList();
hotelList.add(2048047291L);
hotelList.add(727679);
hotelList.add(1457521002);
int i = hotelMapper.deleteBatchIds(hotelList);
System.out.println(i);
}
@Test
public void testDeleteByQuery() {
System.out.println(("----- 根据查询删除单条数据------"));
LambdaQueryWrapper<Hotel> lqt=new LambdaQueryWrapper<Hotel>();
lqt.like(Hotel::getName,"深圳大中华");
int i = hotelMapper.delete(lqt);
System.out.println(i);
}
}
4.4 修改操作
package com.example.mptest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.mptest.mapper.HotelMapper;
import com.example.mptest.pojo.entity.Hotel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author M
* @version 1.0
* @description:
* @date 2023/12/5 15:48
*/
@SpringBootTest
public class TestUpdate {
@Autowired
private HotelMapper hotelMapper;
@Test
public void testUpdate() {
System.out.println(("----- 根据ID修改单条数据 ------"));
Hotel hotel1=new Hotel();
hotel1.setId(2056105938L);
hotel1.setAddress("丽泽金融商务区凤凰路新址");
int i = hotelMapper.updateById(hotel1);
System.out.println(i);
}
@Test
public void testUpdateMuti() {
System.out.println(("----- 根据条件进行修改符合的数据 ------"));
Hotel hotel1=new Hotel();
hotel1.setPrice(1999);
LambdaQueryWrapper<Hotel> lqw=new LambdaQueryWrapper<Hotel>();
lqw.eq(Hotel::getCity,"天津");
int i = hotelMapper.update(hotel1,lqw);//将成为为天津的酒店的价格统一为1999元
System.out.println(i);
}
}
5. 开发实用技术点
5.1 分页查询
实现MP的分页查询功能,需要配置MP的分页拦截器增强功能
5.1.1 分页拦截器配置
在config包中创建一个MPConfig的类,实现一下代码
package com.example.mptest.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author M
* @version 1.0
* @description:
* @date 2023/12/5 19:38
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());//配置分页增强拦截器
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//配置乐观锁增强拦截器
return mybatisPlusInterceptor;
}
}
5.1.2 分页功能实现
@Test
public void testSelectPagination() {
System.out.println(("----- 分页查询所有数据 ------"));
Page<Hotel> hotelPage = new Page<>(1 , 10 ,100, true);//页面对象 分别是页面index和页面size和total和searchCount
Page<Hotel> hotelPageNew = hotelMapper.selectPage(hotelPage, null);
//记住 记住,记住 一定要去配分页拦截器去才可以生效,否则会把所有数据全部返回回来
System.out.println("当前页码值"+hotelPageNew.getCurrent());
System.out.println("每页显示数"+hotelPageNew.getSize());
System.out.println("总共页数"+hotelPageNew.getPages());
System.out.println("总共条数"+hotelPageNew.getTotal());
System.out.println("页面数据"+hotelPageNew.getRecords());
}
5.2 乐观锁问题
实现MP的乐观锁功能,需要配置MP的乐观锁拦截器增强功能和配置乐观锁字段
5.1.1 乐观锁拦截器配置
在config包中创建一个MPConfig的类,实现一下代码
package com.example.mptest.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author M
* @version 1.0
* @description:
* @date 2023/12/5 19:38
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());//配置分页增强拦截器
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//配置乐观锁增强拦截器
return mybatisPlusInterceptor;
}
}
5.1.2 乐观锁字段配置
//在实体类中加入如下字段
@Version
private int version;//乐观锁的字段设置(注意需要去加拦截器)
//就像下面一样
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@TableName("tb_hotel")
public class Hotel {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
private String address;
private double price;
private int score;
private String brand;
private String city;
private String starName;
private String business;
private String latitude;
private String longitude;
private String pic;
@Version
private int version;//乐观锁的字段设置(注意需要去加拦截器)
@TableLogic(value = "0",delval = "1")//这个也可以在yml文件进行全局配置
private int deleted;//软删除字段,0代表未删除,1代表已删除
@TableField(exist = false)//代表数据库中没有这个字段
private boolean status;
}
5.1.3 乐观锁功能实现
@Test
public void testLock() {
Hotel u1=hotelMapper.selectById(101176l);
u1.setAge(77);
int i = hotelMapper.updateById(u1);
System.out.println(i);
//乐观锁的原理是sql执行时会where检查此时的version和拿到的version对比
//如果此时并发操作,version已经被修改了(增加),同时将要修改的version不一样了,则并发的第二条数据不会被执行
//这样子:UPDATE user SET name=?, age=?, email=?, version=2 WHERE id=? AND version=1 AND deleted=0
}
6. 总结
本实践没有过多的文本描述,多在代码中的注释。但通过此个实践了解学习之后,应该可以较好的掌握MP这套持久层开发解决方案,对于中小型项目,可以大幅提升开发效率
相关的配套实践Demo会上传Github开源,SQL文件也在上面