04-MyBatisPlus之DML编程控制
四、DML编程控制
4.1、id生成策略(insert)
问题导入
- 主键生成的策略有哪几种方式?
- 不同的表应用不同的id生成策略
- 日志:自增(1,2,3,4,……)
- 购物订单:特殊规则(FQ23948AK3843)
- 外卖单:关联地区日期等信息(10 04 20200314 34 91)
- 关系表:可省略id
- ...
同时针对每个公司,随着服务化演进,单个服务越来越多,数据库分的越来越细,有的时候一个业务需要分成好几个库,这时候自增主键或者序列之类的主键id生成方式已经不再满足需求,分布式系统中需要的是一个全局唯一的id生成规则。
具体的算法和代码可以参考
雪花算法(SnowFlake),有需要的可以评论区评论或者直接私聊鄙人
4.1.1、@TalbeId注解
-
名称
@TableId
-
类型
- 属性注解
-
位置
- 模型类中用于表示主键的属性,定义在上方
-
作用
- 设置当前类中主键属性的生成策略
-
相关属性
type:设置主键属性的生成策略,值参照IdType枚举值![]()
-
测试步骤
-
1、添加无参和有参的构造方法
-
package com.coolman.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor @TableName(value = "tbl_user") public class User { //使用雪花算法 @TableId(type = IdType.ASSIGN_ID) private Long id; private String name; private String gender; // private String pwd; // @TableField(value = "pwd") // 指定该字段在数据库中的列名 // @TableField(select = false) // 不参与查询 private String password; private Integer age; private String tel; //test new member // @TableField(exist = false) // 该字段在数据库中不存在 // private Integer online; }
-
-
2、插入数据测试
-
4.1.2、全局策略配置
-
除了使用上述的
@TableId注解之外,还可以在applicaion.yml中进行全局的配置 -
测试步骤
-
1、
id-type让所有表主键生成策略相同 -
2、
table-prefix在每个实体类的前面添加相同的前缀 -
mybatis-plus: global-config: db-config: id-type: assign_id table-prefix: tbl_
-
4.1.2.1、id生成策略全局配置
4.1.2.2、表名前缀全局配置
-
![]()
-
实体类去掉@TableName和@TableId注解
-
package com.coolman.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor //@TableName(value = "tbl_user") public class User { //使用雪花算法 // @TableId(type = IdType.ASSIGN_ID) private Long id; private String name; private String gender; // private String pwd; // @TableField(value = "pwd") // 指定该字段在数据库中的列名 // @TableField(select = false) // 不参与查询 private String password; private Integer age; private String tel; //test new member // @TableField(exist = false) // 该字段在数据库中不存在 // private Integer online; }
-
-
再次进行新增测试
4.2、多记录操作(批量Delete/Select)
问题导入
- MyBatisPlus是否支持批量操作?
4.2.1、按照主键删除多条记录
-
方法
deleteBatchIds()
-
代码
-
package com.coolman.test; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.coolman.mapper.UserMapper; import com.coolman.model.User; import lombok.extern.slf4j.Slf4j; 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; import java.util.Map; @Slf4j @SpringBootTest public class MyBatisPlusTest04 { @Autowired(required = false) private UserMapper userMapper; // 批量删除 @Test public void selectByInterval() { ArrayList<Long> list = new ArrayList<>(); list.add(1541476421560475649L); list.add(1542481093725270017L); list.add(1542481194023673857L); list.add(1542481281839804417L); list.add(1542481341910663169L); userMapper.deleteBatchIds(list); } } -
生成的SQL代码如下所示
DELETE FROM tbl_user WHERE id IN ( ? , ? , ? , ? , ? )
-
4.2.2、根据主键查询多条记录
-
方法
selectBathchIds()
-
代码
-
// 根据主键批量查询 @Test public void selectBatchByIds() { ArrayList<Long> list = new ArrayList<>(); list.add(1L); list.add(2L); list.add(3L); list.add(4L); list.add(5L); list.add(6L); List<User> users = userMapper.selectBatchIds(list); for (User user : users) { log.info(user + ""); } } -
生成的SQL代码如下所示
-
SELECT id,name,gender,password,age,tel FROM tbl_user WHERE id IN ( ? , ? , ? , ? , ? , ? )
-
4.3、逻辑删除(Delete/Update)
在实际生产环境中,如果想删除一条数据,是不会真的从数据库删除该条数据的
- 删除操作业务问题:业务数据从数据库中丢失
- 逻辑删除:为数据设置是否可用状态的字段,删除时状态设置状态字段为不可用状态,数据保留在数据库中
4.3.1、逻辑删除案例
-
演示步骤
-
1、修改之前的User表结构,添加逻辑删除标记字段
-
-- 添加一列deleted字段,设置默认值为0(0为未删除,1为已删除) alter table tbl_user add column deleted int(1) default 0; desc tbl_user; -
![]()
-
-
2、实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
-
@TableLogic注解包含以下两个属性value:未删除时的值delval:删除了的值
-
// logic delete @TableLogic private Integer deleted;
-
-
3、在配置文件中配置逻辑删除字面值
-
4、执行逻辑删除操作(逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段
-
// 逻辑删除 @Test public void logicDeleteTest(){ userMapper.deleteById(5); }
-
-
生成的SQL语句如下所示
UPDATE tbl_user SET deleted=1 WHERE id=? AND deleted=0- 上面的
deleted=0条件,在添加逻辑删除字段之后,每次执行查询或者修改的时候都会带上这个条件
-
-
数据库的数据如下图所示
4.4、乐观锁(Update)
乐观锁主张的思想
- 业务并发现象带来的问题:秒杀
4.4.1、悲观锁(Pessimistic Lock)的概念
- 当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观锁。
- 之所以叫做悲观锁,是因为这是一种对数据的修改持有悲观态度的并发控制方式。总是假设最坏的情况,每次读取数据的时候都默认其他线程会更改数据,因此线程想要访问数据时,都需要阻塞挂起。
4.4.2、乐观锁(Optimistic Locking)的概念
- 乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。
- 乐观锁采取了更加宽松的加锁机制。也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。
- 乐观锁的实现
- 1、CAS实现
- Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。
- 2、版本号控制
- 一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会 +1。当线程 A 要更新数据时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。
- 1、CAS实现
4.4.3、乐观锁案例
-
操作步骤
-
1、数据库表中添加锁标记字段
-
alter table tbl_user add column version int default 0; desc tbl_user; -
![]()
-
-
2、实体类中添加对应字段,并设定当前字段为版本控制手段
-
@Version注解
-
// lock @Version private Integer version;
-
-
3、配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装
-
4、使用乐观锁机制在修改前必须先获取到对应数据的version才可以正常进行
-
// 乐观锁测试 @Test public void optimisticLockTest(){ // 1. 查询当前要修改的记录 User user = userMapper.selectById(5); // 2. 修改数据 user.setAge(37); userMapper.updateById(user); } -
执行结果如下所示
![]()
![]()
- 很显然,更新操作执行一次version就会+1
-
-
5、模拟多条记录同时更新
-
// 乐观锁测试2 @Test public void optimisticLockTest2(){ // 1. 查询当前要修改的记录 User user1 = userMapper.selectById(1); User user2 = userMapper.selectById(1); // 2. 修改数据 user1.setAge(44); user2.setAge(99); userMapper.updateById(user1); userMapper.updateById(user2); }
-
-
-
结果演示














浙公网安备 33010602011771号