Mybatis-Plus(下篇)
条件构造器
条件构造器 | MyBatis-Plushttps://baomidou.com/guides/wrapper/
条件构造器是 MyBatis-Plus 提供的一个核心功能,它用于构建 SQL 的 WHERE 条件部分。通过链式调用的方式,可以非常灵活地组合各种查询条件,避免了手写 SQL 的繁琐和易错性。
MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
常用的条件构造器
-
QueryWrapper:用于构建查询条件。
-
UpdateWrapper:用于构建更新条件。
-
DeleteWrapper:用于构建删除条件。可以将2,3进行合并统一使用2来进行操作,减少记忆,和操作API的种类。
前言:
WhereWrapper是MyBatis-Plus中用于构建SQL查询条件的一个重要工具。它通过提供各种条件构造器(如QueryWrapper和UpdateWrapper)来帮助开发者更方便地构建复杂的查询条件,而不需要手动编写大量的SQL语句
WhereWrapper是MyBatis-Plus中条件构造器的一个抽象类,其下有多种实现,包括:
QueryWrapper:用于查询/删除操作,可以构建复杂的查询/删除条件。
UpdateWrapper:用于更新操作,可以构建复杂的更新条件。
LambdaQueryWrapper:使用Lambda表达式进行查询/删除操作。
LambdaUpdateWrapper:使用Lambda表达式进行更新操作。
Wrapper : 条件构造抽象类,最顶端父类
-
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
-
QueryWrapper : 查询/删除条件封装
-
UpdateWrapper : 修改条件封装
-
AbstractLambdaWrapper : 使用Lambda 语法
-
LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
-
LambdaUpdateWrapper : Lambda 更新封装Wrapper
-
-
在 MyBatis-Plus 中,Wrapper 类是构建查询和更新条件的核心工具。以下是主要的 Wrapper 类及其功能:
AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。它定义了条件构造的基本逻辑,包括字段(column)、值(value)、操作符(condition)等。所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都继承自 AbstractWrapper。
QueryWrapper:专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以链式调用的方式添加多个查询条件,并且可以组合使用
and
和or
逻辑。UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。
LambdaQueryWrapper:这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。
基于QueryWrapper 组装条件
①获取条件构造器QueryWrapper
②在条件构造器中拼接对应的条件表达语句
③在对应的增删改查方法形参列表中,加入已经构建(拼接)号的条件构造器实体
@SpringBootTest
public class QueryWrapperTest {
@Autowired
private UserMapper userMapper;
===============================组装查询条件========================================
@Test
public void testQueryWrapper() {
//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> QueryWrapper = new QueryWrapper<>();
QueryWrapper.like("name","a");
QueryWrapper.between("age",20,30);
QueryWrapper.isNotNull("email");
List<User> users = userMapper.selectList(QueryWrapper);
users.forEach(System.out::println);
//或者可以直接使用链式语法进行条件构造器设置。代码量少,方便又美观(下面就统一使用链式语法进行条件构造器的编译)
QueryWrapper.like("name","a")
.between("age",20,30)
.isNotNull("email");
}
=========================组装排序条件==============================================
@Test
public void testQueryWrapper2() {
//按年龄降序查询用户,如果年龄相同则按id升序排列
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC
QueryWrapper<User> QueryWrapper = new QueryWrapper<>();
QueryWrapper.orderByDesc("age")
.orderByAsc("id");
List<User> users = userMapper.selectList(QueryWrapper);
users.forEach(System.out::println);
}
============================组装删除条件===========================================
@Test
public void testQueryWrapper3() {
//删除email为空的用户
//DELETE FROM t_user WHERE (email IS NULL)
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.isNull("email");
userMapper.delete(userQueryWrapper);
System.out.println("受影响的行数:" + result);
}
=========================and与or关键字的使用========================================
@Test
public void testQueryWrapper4() {
//将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改
//UPDATE t_user SET age=?, email=? WHERE username LIKE ? AND age > ? OR email IS NULL)
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.like("name","a")
.gt("age",20)
.or()
.isNull("email");
User user = new User();
user.setAge(20);
user.setName("胡尔摩斯");
userMapper.update(user, userQueryWrapper);
System.out.println("受影响的行数:" + result);
}
==========================指定映射查询=============================================
@Test
public void testQueryWrapper5() {
//查询用户信息的username和age字段
//SELECT username,age FROM t_user
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.select("name","age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为null
List<Map<String, Object>> maps = userMapper.selectMaps(userQueryWrapper);
maps.forEach(System.out::println);
}
=========================condition判断组织条件查询==================================
@Test
public void testQueryWrapper6() {
// 拼接condition判断
//每个条件拼接方法都condition参数,这是一个比较运算,为true追加当前条件!
//eq(condition,列名,值)
Integer age = 20;
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq(age!=null && age>18,"age",age);
userMapper.selectList(userQueryWrapper);
}
}
注意:使用queryWrapper + 实体类形式可以实现修改,但是无法将列值修改为null值!
基于 UpdateWrapper组装条件
@Test
public void testQuick2(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//将id = 3 的email设置为null, age = 18
updateWrapper.eq("id",3)
.set("email",null) // set 指定列和结果
.set("age",18);
//如果使用updateWrapper 实体对象写null即可!
int result = userMapper.update(null, updateWrapper);
System.out.println("result = " + result);
}
基于LambdaQueryWrapper组装条件
LambdaQueryWrapper对比QueryWrapper优势
①提高了代码可读性:通过 Lambda 表达式引用属性,代码更加直观易懂,尤其是在属性名较长或复杂时。
②提高了代码健壮性:通过 Lambda 表达式来引用实体类的属性,例如
lambdaQueryWrapper.eq(User::getName, "张三")
。这种方式在编译时就能检查属性是否存在或拼写错误。③提高了代码可维护性:Lambda 表达式引用的是实体类的属性,只要实体类属性与数据库字段的映射关系正确,代码就能正常工作。
@Test
public void testQuick4(){
String name = "root";
int age = 18;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//每个条件拼接方法都condition参数,这是一个比较运算,为true追加当前条件!
//eq(condition,列名,值)
queryWrapper.eq(!StringUtils.isEmpty(name),"name",name)
.eq(age>1,"age",age);
//TODO: 使用lambdaQueryWrapper
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//注意: 需要使用方法引用
//技巧: 类名::方法名
lambdaQueryWrapper.eq(!StringUtils.isEmpty(name), User::getName,name);
List<User> users= userMapper.selectList(lambdaQueryWrapper);
System.out.println(users);
}
基于LambdaUpdateWrapper组装条件
@Test
public void testQuick2(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//将id = 3 的email设置为null, age = 18
updateWrapper.eq("id",3)
.set("email",null) // set 指定列和结果
.set("age",18);
//使用lambdaUpdateWrapper
LambdaUpdateWrapper<User> updateWrapper1 = new LambdaUpdateWrapper<>();
updateWrapper1.eq(User::getId,3)
.set(User::getEmail,null)
.set(User::getAge,18);
//如果使用updateWrapper 实体对象写null即可!
int result = userMapper.update(null, updateWrapper);
System.out.println("result = " + result);
}
核心注解
思考:
MyBatis-plus中Mapper继承BaseMapper<实体类>后为什么会自动生成对应实体类(对应数据库中的一张表)的CRUD方法。
默认情况下, 根据指定的<实体类>的名称对应数据库表名,属性名对应数据库的列名!
但是不是所有数据库的信息和实体类都完全映射!
例如: 表名 t_user → 实体类 User 这时候就不对应了!
自定义映射关系就可以使用mybatis-plus提供的注解即可!
@TableName注解
描述:表名注解,标识实体类对应的数据库表
使用位置:实体类
使用场景:
实体类的类名与数据库表名不一致(忽略大小写的情况)。
数据库表名遵循特定的命名规范(例如,使用下划线分隔单词),而实体类名遵循 Java 的命名规范(使用驼峰命名法)。
在多数据源场景下,不同的数据源可能包含相同名称的表,但需要通过不同的实体类来映射这些表。
#对于Spring Boot工程还可以进行yml文件配置来回正对应的映射关系(设置一些全局前缀等...)
mybatis-plus: # mybatis-plus的配置
global-config:
db-config:
table-prefix: sys_ # 表名前缀字符串
@TableId 注解
描述:主键注解
使用位置:实体类主键字段
注解属性:
-
value
:指定实体类中的主键属性名。如果不指定,MyBatis-Plus 默认会寻找名为id
的属性作为主键。 -
type
:指定主键的生成策略。MyBatis-Plus 提供了多种内置的主键生成策略,如IdType.AUTO
(数据库 ID 自增)、IdType.ASSIGN_ID
(分配 ID,默认实现是雪花算法)、IdType.ASSIGN_UUID
(分配 UUID)等。如果不指定,MyBatis-Plus 会根据数据库的类型和配置自动选择一种合适的策略。
使用场景:
实体类的字段与数据库表的主键字段不同名
自定义主键生成策略,如使用 UUID、雪花算法等。
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 主键字段名 |
type | Enum | 否 | IdType.NONE | 指定主键类型 |
其中IdType属性可选值如下:
@TableName("sys_user")
public class User {
@TableId(value="主键列名",type=主键策略)
private Long id;
private String name;
private Integer age;
private String email;
}
①主键策略为雪花算法时,数据库主键需要是 bigint/varchar(64)类型
实体类需要是long类型,随机生成一个数字赋值给主键(不重复)
②如果要设置全局主键策略,可以在application.xml文件中进行配置
雪花算法
雪花算法(Snowflake Algorithm)是一种分布式系统中生成全局唯一ID的算法,由Twitter开源。雪花算法生成的ID是一个64位的整数,通常以一个特定的格式来组织,以确保在分布式环境下生成的ID不仅全局唯一,而且是有序的。
雪花算法的结构
一个典型的雪花算法生成的64位ID由以下几部分组成:
-
符号位:1位,始终为0,因为生成的ID都是正整数。
-
时间戳位:41位,用来记录时间戳,单位通常是毫秒。这41位时间戳可以使用69年(从1970年开始计算,因为时间戳是从1970年1月1日00:00:00 UTC开始的)。
-
数据中心ID:5位,可以部署在1024个数据中心。
-
机器ID:5位,每个数据中心可以部署32台机器。
-
序列号:12位,毫秒内的计数,同一毫秒内最多可以生成4096个ID。
雪花算法的工作方式如下:
-
当前时间戳从某一固定的起始时间开始计算,可以用于计算ID的时间部分。
-
节点ID是分布式系统中每个节点的唯一标识,可以通过配置或自动分配的方式获得。
-
序列号用于记录在同一毫秒内生成的不同ID的序号,从0开始自增,最多支持4096个ID生成。
需要注意的是,雪花算法依赖于系统的时钟,需要确保系统时钟的准确性和单调性,否则可能会导致生成的ID不唯一或不符合预期的顺序。
雪花算法是一种简单但有效的生成唯一ID的算法,广泛应用于分布式系统中,如微服务架构、分布式数据库、分布式锁等场景,以满足全局唯一标识的需求。
你需要记住的: 雪花算法生成的数字,需要使用Long 或者 String类型主键!!
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
# 配置MyBatis-Plus的主键策略
id-type: auto
@TableField
描述:字段注解(非主键)
使用位置:实体类的属性
注解属性:
-
value
:指定数据库表的列名。如果不指定,MyBatis-Plus 默认会使用实体类属性的名称(去除前缀,并将驼峰命名法转换为下划线分隔的形式)作为列名。 -
exist
:指定该属性是否映射到数据库表的列。如果设置为false
,则表示该属性不会映射到任何数据库表的列,这在进行一些特殊的查询或更新操作时可能很有用。
使用场景:
实体类的属性名与数据库表的列名不一致。
实体类的属性需要映射到数据库表的非主键列。
在使用 MyBatis-Plus 的代码生成器时,可以通过配置自动生成带有
@TableField
注解的实体类,从而简化开发工作。
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | “ ” | 数据库字段名 |
exist | Boolean | 否 | true | 是否为数据库表字段 |
@TableLogic
描述:逻辑删除注解
使用位置:实体类的属性
@TableLogic
注解有一个可选的属性 value
,用于指定逻辑删除标记字段的值。如果不指定 value
属性,MyBatis-Plus 默认会使用 1
表示删除状态,0
表示未删除状态。
使用场景:
需要保留数据的历史记录,而不是永久删除。
避免物理删除可能带来的数据恢复困难或数据安全问题。
实现软删除功能,即数据在逻辑上被删除,但在物理上仍然保留在数据库中。
MyBatis-Plus会自动开启驼峰命名风格映射!!!
高级扩展
逻辑删除
由于数据库中所有表均采用逻辑删除策略,所以查询数据时均需要增加过滤条件`is_deleted=0`。
上述操作虽不难实现,但是每个查询接口都要考虑到,也显得有些繁琐。为简化上述操作,可以使用Mybatis-Plus提供的逻辑删除功能,它可以自动为查询操作增加`is_deleted=0`过滤条件,并将删除操作转为更新语句。具体配置如下:
两种删除方式:
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
逻辑删除实现:
①添加逻辑删除字段(可以声明为一个布尔类型,整数类型或枚举类型)
②实体类添加逻辑删除属性
③指定逻辑删除字段和属性值
ALTER TABLE USER ADD deleted INT DEFAULT 0 ; # int 类型 1 逻辑删除 0 未逻辑删除
@Data
public class User {
// @TableId
private Integer id;
private String name;
private Integer age;
private String email;
@TableLogic
//逻辑删除字段 int mybatis-plus下,默认 逻辑删除值为1 未逻辑删除 1
private Integer deleted;
}
//单一进行指定
@Data
public class User {
// @TableId
private Integer id;
private String name;
private Integer age;
private String email;
@TableLogic
//逻辑删除字段 int mybatis-plus下,默认 逻辑删除值为1 未逻辑删除 1
private Integer deleted;
}
#全局进行指定
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
注意:
逻辑删除功能只对Mybatis-Plus自动注入的sql起效,也就是说,对于手动在Mapper.xml
文件配置的sql不会生效,需要单独考虑。
忽略特定字段
通常情况下接口响应的Json对象中并不需要`create_time`、`update_time`、`is_deleted`等字段,这时只需在实体类中的相应字段添加`@JsonIgnore`注解,该字段就会在序列化时被忽略。
具体配置如下:
package com.atguigu.lease.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class BaseEntity implements Serializable {
@Schema(description = "主键")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "创建时间")
@TableField(value = "create_time")
@JsonIgnore
private Date createTime;
@Schema(description = "更新时间")
@TableField(value = "update_time")
@JsonIgnore//忽视序列化
//后端查询完数据库后,将查询到的数据序列化后,才将数据发送给前端
//在使用该注解后,会在序列化数据时,将本字段忽略掉
private Date updateTime;
@Schema(description = "逻辑删除")
@TableField("is_deleted")
@JsonIgnore
@TableLogic//逻辑删除字段注解
//可以在配置文件中来指定对应的逻辑删除字段名
//配置逻辑删除值,用来将指定什么值代表逻辑删除,什么值代表未删除
//当然了,在未配置的情况下。默认1是逻辑删除,0是未删除
//注意:这种逻辑删除的配置只对通用mapper和通用service生效。对于自己写的sql代码mapper层是不生效的
private Byte isDeleted;
}
公共字段填充
保存或更新数据时,前端通常不会传入`createTime`、`updateTime`这2个字段,因此我们需要手动赋值。但是数据库中几乎每张表都有上述字段,所以手动去赋值就显得有些繁琐。为简化上述操作,我们可采取以下措施。
create_time和update_time:可使用mybatis-plus的自动填充功能,所谓自动填充,就是通过统一配置,在插入或更新数据时,自动为某些字段赋值。 为相关字段配置触发填充的时机,例如`create_time`需要在插入数据时填充,而`update_time`需要在更新数据时填充。具体配置如下,观察`@TableField`注解中的`fill`属性。
package com.atguigu.lease.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class BaseEntity implements Serializable {
@Schema(description = "主键")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "创建时间")
@TableField(value = "create_time",fill = FieldFill.INSERT)
@JsonIgnore
private Date createTime;
@Schema(description = "更新时间")
@TableField(value = "update_time",fill = FieldFill.UPDATE)
@JsonIgnore//忽视序列化
//后端查询完数据库后,将查询到的数据序列化后,才将数据发送给前端
//在使用该注解后,会在序列化数据时,将本字段忽略掉
private Date updateTime;
@Schema(description = "逻辑删除")
@TableField("is_deleted")
@JsonIgnore
@TableLogic//逻辑删除字段注解
//可以在配置文件中来指定对应的逻辑删除字段名
//配置逻辑删除值,用来将指定什么值代表逻辑删除,什么值代表未删除
//当然了,在未配置的情况下。默认1是逻辑删除,0是未删除
//注意:这种逻辑删除的配置只对通用mapper和通用service生效。对于自己写的sql代码mapper层是不生效的
private Byte isDeleted;
}
自动填充组件配置:
package com.atguigu.lease.common.mybatisplus;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MybatisMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
//当填充操作发生时,会为实体对象的createTime属性填充当前时间
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
//当更新操作发生时,会为实体对象的updateTime属性填充当前时间
}
}
乐观锁与悲观锁
乐观锁与悲观锁是并发控制中的两种不同策略,用于解决多线程环境下的数据一致性问题。
悲观锁和乐观锁是两种解决并发数据问题的思路,不是具体技术!!!
乐观锁
乐观锁假设多用户并发的事务在处理时不会彼此干扰,因此在数据提交时才会对数据的冲突与否进行检测。如果发现冲突了,则回滚当前事务,让用户重新尝试。乐观锁的实现通常依赖于数据版本(Version)记录机制。
特点:
-
乐观态度:认为冲突很少发生,因此在进行更新操作时不会加锁。
-
性能较高:因为减少了锁的竞争,所以在高并发环境下性能较好
具体技术和实现方案:
①数据版本:在数据库表中增加一个版本字段,每次更新数据时,版本字段都会递增。在更新操作时,会检查当前版本是否与预期版本一致,如果一致则更新成功,否则更新失败。
②时间戳:类似于数据版本,但使用时间戳来记录数据的最后修改时间。
③CAS(Compare-and-Swap):使用原子操作比较当前值与旧值是否一致,若一致则进行更新操作,否则重新尝试。
④无锁数据结构:采用无锁数据结构,如无锁队列、无锁哈希表等,通过使用原子操作实现并发安全。
悲观锁
悲观锁则假设最坏的情况,认为多用户并发的事务一定会发生冲突,因此在数据处理过程中,将数据处于锁定状态。悲观锁的实现依赖于数据库的锁机制。
特点:
-
悲观态度:认为冲突经常发生,因此在整个数据处理过程中都会保持锁定状态。
-
性能较低:因为锁定了数据,所以其他线程无法访问被锁定的数据,可能导致并发性能下降。
具体技术和实现方案:
①锁机制:使用传统的锁机制,如互斥锁(Mutex Lock)或读写锁(Read-Write Lock)来保证对共享资源的独占访问。
②数据库锁:在数据库层面使用行级锁或表级锁来控制并发访问。
③信号量(Semaphore):使用信号量来限制对资源的并发访问。
版本号乐观锁实现:
①每条数据添加一个版本号字段version
②取出记录时,获取当前 version
③更新时,检查获取版本号是不是数据库当前最新版本号
④如果是[证明没有人修改数据], 执行更新, set 数据更新 , version = version+ 1
⑤如果 version 不对[证明有人已经修改了],我们现在的其他记录就是失效数据!就更新失败
===============================添加版本号更新插件====================================
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
===============================数据库添加version字段================================
ALTER TABLE USER ADD VERSION INT DEFAULT 1 ; # int 类型 乐观锁字段
=====================在数据库所对应的实体类上添加@version的注解=========================
@Version
private Integer version;
===========================正常对数据库进行操作即可===================================
//演示乐观锁生效场景
@Test
public void testQuick7(){
//步骤1: 先查询,在更新 获取version数据
//同时查询两条,但是version唯一,最后更新的失败
User user = userMapper.selectById(5);
User user1 = userMapper.selectById(5);
user.setAge(20);
user1.setAge(30);
userMapper.updateById(user);
//乐观锁生效,失败!
userMapper.updateById(user1);
}
@Version注解
描述:用于在实体类中标记乐观锁的版本号字段
使用位置:实体类的属性
使用场景
-
在多用户并发访问和修改同一数据记录的场景下,使用乐观锁可以确保数据的一致性和完整性。
-
乐观锁适用于读多写少的场景,因为它假设冲突不常发生,从而减少了锁的开销,提高了系统的并发性能。
防止全表的更新和删除实现
针对 update 和 delete 语句 作用: 阻止恶意的全表更新删除
==========================添加防止全表更新和删除拦截器================================
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}
===========================测试全表更新或者删除======================================
@Test
public void testQuick8(){
User user = new User();
user.setName("custom_name");
user.setEmail("xxx@mail.com");
//Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
//全局更新,报错
userService.saveOrUpdate(user,null);
}
Mybatisx代码生成器(MyBatisX插件)
MybatisX 是一款专为 IntelliJ IDEA 设计的快速开发插件,旨在提升 MyBatis 与 MyBatis-Plus 框架的开发效率。
MyBatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率
MyBatisX插件,还有许多核心功能并没有提及如果感兴趣请移步至官网开发手册,有全套核心功能教程供学习,。这里主要说明与一下其逆向工程和代码生成功能简易开发。(在前面的MyBatis笔记下篇中有逆向工程的具体操作流程,可以自行查看)
Mybatis X 插件 | MyBatis-Plushttps://baomidou.com/guides/mybatis-x/